Merge branch 'wellenvogel:master' into master
This commit is contained in:
commit
96dfd9e773
|
@ -10,6 +10,8 @@ jobs:
|
|||
os: [ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
|
|
|
@ -18,6 +18,9 @@ jobs:
|
|||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
PIP_BREAK_SYSTEM_PACKAGES: 1
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -18,6 +18,8 @@ OWN_FILE="extra_script.py"
|
|||
GEN_DIR='lib/generated'
|
||||
CFG_FILE='web/config.json'
|
||||
XDR_FILE='web/xdrconfig.json'
|
||||
INDEXJS="index.js"
|
||||
INDEXCSS="index.css"
|
||||
CFG_INCLUDE='GwConfigDefinitions.h'
|
||||
CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
|
||||
XDR_INCLUDE='GwXdrTypeMappings.h'
|
||||
|
@ -66,6 +68,7 @@ def isCurrent(infile,outfile):
|
|||
def compressFile(inFile,outfile):
|
||||
if isCurrent(inFile,outfile):
|
||||
return
|
||||
print("compressing %s"%inFile)
|
||||
with open(inFile, 'rb') as f_in:
|
||||
with gzip.open(outfile, 'wb') as f_out:
|
||||
shutil.copyfileobj(f_in, f_out)
|
||||
|
@ -372,6 +375,16 @@ def getLibs():
|
|||
rt.append(e)
|
||||
return rt
|
||||
|
||||
def joinFiles(target,pattern,dirlist):
|
||||
with gzip.open(target,"wb") as oh:
|
||||
for dir in dirlist:
|
||||
fn=os.path.join(dir,pattern)
|
||||
if os.path.exists(fn):
|
||||
print("adding %s to %s"%(fn,target))
|
||||
with open(fn,"rb") as rh:
|
||||
shutil.copyfileobj(rh,oh)
|
||||
|
||||
|
||||
OWNLIBS=getLibs()+["FS","WiFi"]
|
||||
GLOBAL_INCLUDES=[]
|
||||
|
||||
|
@ -440,6 +453,8 @@ def prebuild(env):
|
|||
compressFile(mergedConfig,mergedConfig+".gz")
|
||||
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False)
|
||||
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE_IMPL),True)
|
||||
joinFiles(os.path.join(outPath(),INDEXJS+".gz"),INDEXJS,["web"]+userTaskDirs)
|
||||
joinFiles(os.path.join(outPath(),INDEXCSS+".gz"),INDEXCSS,["web"]+userTaskDirs)
|
||||
embedded=getEmbeddedFiles(env)
|
||||
filedefs=[]
|
||||
for ef in embedded:
|
||||
|
@ -453,7 +468,6 @@ def prebuild(env):
|
|||
filedefs.append((pureName,usname,ct))
|
||||
inFile=os.path.join(basePath(),"web",pureName)
|
||||
if os.path.exists(inFile):
|
||||
print("compressing %s"%inFile)
|
||||
compressFile(inFile,ef)
|
||||
else:
|
||||
print("#WARNING: infile %s for %s not found"%(inFile,ef))
|
||||
|
|
|
@ -47,7 +47,6 @@ GwBoatItemBase::GwBoatItemBase(String name, String format, GwBoatItemBase::TOTyp
|
|||
this->format = format;
|
||||
this->type = 0;
|
||||
this->lastUpdateSource = -1;
|
||||
this->toType=TOType::user;
|
||||
}
|
||||
void GwBoatItemBase::setInvalidTime(unsigned long it, bool force){
|
||||
if (toType != TOType::user || force ){
|
||||
|
@ -375,7 +374,7 @@ GwBoatItem<T> *GwBoatData::getOrCreate(T initial, GwBoatItemNameProvider *provid
|
|||
provider->getBoatItemFormat(),
|
||||
provider->getInvalidTime(),
|
||||
&values);
|
||||
rt->update(initial);
|
||||
rt->update(initial,-1);
|
||||
LOG_DEBUG(GwLog::LOG, "creating boatItem %s, type %d",
|
||||
name.c_str(), rt->getCurrentType());
|
||||
return rt;
|
||||
|
|
|
@ -105,8 +105,8 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
|
|||
GwBoatItem(String name,String formatInfo,unsigned long invalidTime=INVALID_TIME,GwBoatItemMap *map=NULL);
|
||||
GwBoatItem(String name,String formatInfo,TOType toType,GwBoatItemMap *map=NULL);
|
||||
virtual ~GwBoatItem(){}
|
||||
bool update(T nv, int source=-1);
|
||||
bool updateMax(T nv,int sourceId=-1);
|
||||
bool update(T nv, int source);
|
||||
bool updateMax(T nv,int sourceId);
|
||||
T getData(){
|
||||
return data;
|
||||
}
|
||||
|
@ -185,7 +185,6 @@ public:
|
|||
#define GWSPECBOATDATA(clazz,name,toType,fmt) \
|
||||
clazz *name=new clazz(#name,GwBoatItemBase::fmt,toType,&values) ;
|
||||
class GwBoatData{
|
||||
static const unsigned long DEF_TIME=4000;
|
||||
private:
|
||||
GwLog *logger;
|
||||
GwBoatItemBase::GwBoatItemMap values;
|
||||
|
|
|
@ -16,16 +16,63 @@
|
|||
#define _GWCONVERTERCONFIG_H
|
||||
|
||||
#include "GWConfig.h"
|
||||
#include "N2kTypes.h"
|
||||
#include <map>
|
||||
|
||||
//list of configs for the PGN 130306 wind references
|
||||
static std::map<tN2kWindReference,String> windConfigs={
|
||||
{N2kWind_True_water,GwConfigDefinitions::windmtra},
|
||||
{N2kWind_Apparent,GwConfigDefinitions::windmawa},
|
||||
{N2kWind_True_boat,GwConfigDefinitions::windmgna},
|
||||
{N2kWind_Magnetic,GwConfigDefinitions::windmmgd},
|
||||
{N2kWind_True_North,GwConfigDefinitions::windmtng},
|
||||
};
|
||||
|
||||
class GwConverterConfig{
|
||||
public:
|
||||
class WindMapping{
|
||||
public:
|
||||
using Wind0183Type=enum{
|
||||
AWA_AWS,
|
||||
TWA_TWS,
|
||||
TWD_TWS,
|
||||
GWA_GWS,
|
||||
GWD_GWS
|
||||
};
|
||||
tN2kWindReference n2kType;
|
||||
Wind0183Type nmea0183Type;
|
||||
bool valid=false;
|
||||
WindMapping(){}
|
||||
WindMapping(const tN2kWindReference &n2k,const Wind0183Type &n183):
|
||||
n2kType(n2k),nmea0183Type(n183),valid(true){}
|
||||
WindMapping(const tN2kWindReference &n2k,const String &n183):
|
||||
n2kType(n2k){
|
||||
if (n183 == "twa_tws"){
|
||||
nmea0183Type=TWA_TWS;
|
||||
valid=true;
|
||||
return;
|
||||
}
|
||||
if (n183 == "awa_aws"){
|
||||
nmea0183Type=AWA_AWS;
|
||||
valid=true;
|
||||
return;
|
||||
}
|
||||
if (n183 == "twd_tws"){
|
||||
nmea0183Type=TWD_TWS;
|
||||
valid=true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
int minXdrInterval=100;
|
||||
int starboardRudderInstance=0;
|
||||
int portRudderInstance=-1; //ignore
|
||||
int min2KInterval=50;
|
||||
int rmcInterval=1000;
|
||||
int rmcCheckTime=4000;
|
||||
void init(GwConfigHandler *config){
|
||||
int winst312=256;
|
||||
std::vector<WindMapping> windMappings;
|
||||
void init(GwConfigHandler *config, GwLog*logger){
|
||||
minXdrInterval=config->getInt(GwConfigDefinitions::minXdrInterval,100);
|
||||
starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0);
|
||||
portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1);
|
||||
|
@ -36,6 +83,30 @@ class GwConverterConfig{
|
|||
rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000);
|
||||
if (rmcInterval < 0) rmcInterval=0;
|
||||
if (rmcInterval > 0 && rmcInterval <100) rmcInterval=100;
|
||||
winst312=config->getInt(GwConfigDefinitions::winst312,256);
|
||||
for (auto && it:windConfigs){
|
||||
String cfg=config->getString(it.second);
|
||||
WindMapping mapping(it.first,cfg);
|
||||
if (mapping.valid){
|
||||
LOG_DEBUG(GwLog::ERROR,"add wind mapping n2k=%d,nmea0183=%01d(%s)",
|
||||
(int)(mapping.n2kType),(int)(mapping.nmea0183Type),cfg.c_str());
|
||||
windMappings.push_back(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
const WindMapping findWindMapping(const tN2kWindReference &n2k) const{
|
||||
for (const auto & it:windMappings){
|
||||
if (it.n2kType == n2k) return it;
|
||||
}
|
||||
return WindMapping();
|
||||
}
|
||||
const WindMapping findWindMapping(const WindMapping::Wind0183Type &n183) const{
|
||||
for (const auto & it:windMappings){
|
||||
if (it.nmea0183Type == n183) return it;
|
||||
}
|
||||
return WindMapping();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
.examplecss{
|
||||
background-color: coral;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
(function(){
|
||||
const api=window.esp32nmea2k;
|
||||
if (! api) return;
|
||||
//we only do something if a special capability is set
|
||||
//on our case this is "testboard"
|
||||
//so we only start any action when we receive the init event
|
||||
//and we successfully checked that our requested capability is there
|
||||
let isActive=false;
|
||||
const tabName="example";
|
||||
const configName="exampleBDSel";
|
||||
const infoUrl='https://github.com/wellenvogel/esp32-nmea2000/tree/master/lib/exampletask';
|
||||
let boatItemName;
|
||||
let boatItemElement;
|
||||
api.registerListener((id,data)=>{
|
||||
if (id === api.EVENTS.init){
|
||||
//data is capabilities
|
||||
//check if our requested capability is there (see GwExampleTask.h)
|
||||
if (data.testboard) isActive=true;
|
||||
if (isActive){
|
||||
//add a simple additional tab page
|
||||
//you will have to build the content of the page dynamically
|
||||
//using normal dom manipulation methods
|
||||
//you can use the helper addEl to create elements
|
||||
let page=api.addTabPage(tabName,"Example");
|
||||
api.addEl('div','hdg',page,"this is a test tab");
|
||||
api.addEl('button','',page,'Info').addEventListener('click',function(ev){
|
||||
window.open(infoUrl,'info');
|
||||
})
|
||||
//add a tab for an external URL
|
||||
api.addTabPage('exhelp','Info',infoUrl);
|
||||
}
|
||||
}
|
||||
if (isActive){
|
||||
//console.log("exampletask listener",id,data);
|
||||
if (id === api.EVENTS.tab){
|
||||
if (data === tabName){
|
||||
//maybe we need some activity when our page is being activated
|
||||
console.log("example tab activated");
|
||||
}
|
||||
}
|
||||
if (id == api.EVENTS.config){
|
||||
//we have a configuration that
|
||||
//gives us the name of a boat data item we would like to
|
||||
//handle special
|
||||
//in our case we just use an own formatter and add some
|
||||
//css to the display field
|
||||
//as this item can change we need to keep track of the
|
||||
//last item we handled
|
||||
let nextboatItemName=data[configName];
|
||||
console.log("value of "+configName,nextboatItemName);
|
||||
if (nextboatItemName){
|
||||
//register a user formatter that will be called whenever
|
||||
//there is a new valid value
|
||||
//we simply add an "X:" in front
|
||||
api.addUserFormatter(nextboatItemName,"m(x)",function(v,valid){
|
||||
if (!valid) return;
|
||||
return "X:"+v;
|
||||
})
|
||||
//after this call the item will be recreated
|
||||
}
|
||||
if (boatItemName !== undefined && boatItemName != nextboatItemName){
|
||||
//if the boat item that we handle has changed, remove
|
||||
//the previous user formatter (this will recreate the item)
|
||||
api.removeUserFormatter(boatItemName);
|
||||
}
|
||||
boatItemName=nextboatItemName;
|
||||
boatItemElement=undefined;
|
||||
}
|
||||
if (id == api.EVENTS.dataItemCreated){
|
||||
//this event is called whenever a data item has
|
||||
//been created (or recreated)
|
||||
//if this is the item we handle, we just add a css class
|
||||
//we could also completely rebuild the dom below the element
|
||||
//and use our formatter to directly write/draw the data
|
||||
//avoid direct manipulation of the element (i.e. changing the classlist)
|
||||
//as this element remains there all the time
|
||||
if (boatItemName && boatItemName == data.name){
|
||||
boatItemElement=data.element;
|
||||
//use the helper forEl to find elements within the dashboard item
|
||||
//the value element has the class "dashValue"
|
||||
api.forEl(".dashValue",function(el){
|
||||
el.classList.add("examplecss");
|
||||
},boatItemElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})();
|
|
@ -103,7 +103,7 @@ private:
|
|||
if (v != NMEA0183UInt32NA){
|
||||
return target->update(v,sourceId);
|
||||
}
|
||||
return v;
|
||||
return false;
|
||||
}
|
||||
uint32_t getUint32(GwBoatItem<uint32_t> *src){
|
||||
return src->getDataWithDefault(N2kUInt32NA);
|
||||
|
@ -399,28 +399,29 @@ private:
|
|||
return;
|
||||
}
|
||||
tN2kMsg n2kMsg;
|
||||
tN2kWindReference n2kRef;
|
||||
bool shouldSend=false;
|
||||
WindAngle=formatDegToRad(WindAngle);
|
||||
GwConverterConfig::WindMapping mapping;
|
||||
switch(Reference){
|
||||
case NMEA0183Wind_Apparent:
|
||||
n2kRef=N2kWind_Apparent;
|
||||
shouldSend=updateDouble(boatData->AWA,WindAngle,msg.sourceId) &&
|
||||
updateDouble(boatData->AWS,WindSpeed,msg.sourceId);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxAws->updateMax(WindSpeed);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxAws->updateMax(WindSpeed,msg.sourceId);
|
||||
mapping=config.findWindMapping(GwConverterConfig::WindMapping::AWA_AWS);
|
||||
break;
|
||||
case NMEA0183Wind_True:
|
||||
n2kRef=N2kWind_True_water;
|
||||
shouldSend=updateDouble(boatData->TWA,WindAngle,msg.sourceId) &&
|
||||
updateDouble(boatData->TWS,WindSpeed,msg.sourceId);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed,msg.sourceId);
|
||||
mapping=config.findWindMapping(GwConverterConfig::WindMapping::TWA_TWS);
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG(GwLog::DEBUG,"unknown wind reference %d in %s",(int)Reference,msg.line);
|
||||
}
|
||||
if (shouldSend){
|
||||
SetN2kWindSpeed(n2kMsg,1,WindSpeed,WindAngle,n2kRef);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)n2kRef));
|
||||
//TODO: try to compute TWD and get mapping for this one
|
||||
if (shouldSend && mapping.valid){
|
||||
SetN2kWindSpeed(n2kMsg,1,WindSpeed,WindAngle,mapping.n2kType);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)mapping.n2kType));
|
||||
}
|
||||
}
|
||||
void convertVWR(const SNMEA0183Msg &msg)
|
||||
|
@ -457,11 +458,14 @@ private:
|
|||
bool shouldSend = false;
|
||||
shouldSend = updateDouble(boatData->AWA, WindAngle, msg.sourceId) &&
|
||||
updateDouble(boatData->AWS, WindSpeed, msg.sourceId);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxAws->updateMax(WindSpeed);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxAws->updateMax(WindSpeed,msg.sourceId);
|
||||
if (shouldSend)
|
||||
{
|
||||
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, WindAngle, N2kWind_Apparent);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)N2kWind_Apparent));
|
||||
const GwConverterConfig::WindMapping mapping=config.findWindMapping(GwConverterConfig::WindMapping::AWA_AWS);
|
||||
if (mapping.valid){
|
||||
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, WindAngle, mapping.n2kType);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)mapping.n2kType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,13 +503,21 @@ private:
|
|||
if (WindDirection != NMEA0183DoubleNA){
|
||||
shouldSend = updateDouble(boatData->TWD, WindDirection, msg.sourceId) &&
|
||||
updateDouble(boatData->TWS, WindSpeed, msg.sourceId);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed);
|
||||
if (WindSpeed != NMEA0183DoubleNA) boatData->MaxTws->updateMax(WindSpeed,msg.sourceId);
|
||||
if(shouldSend && boatData->HDT->isValid()) {
|
||||
double twa = WindDirection-boatData->HDT->getData();
|
||||
if(twa<0) { twa+=2*M_PI; }
|
||||
updateDouble(boatData->TWA, twa, msg.sourceId);
|
||||
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, twa, N2kWind_True_water);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)N2kWind_True_water));
|
||||
const GwConverterConfig::WindMapping mapping=config.findWindMapping(GwConverterConfig::WindMapping::TWA_TWS);
|
||||
if (mapping.valid){
|
||||
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, twa, mapping.n2kType);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)mapping.n2kType));
|
||||
}
|
||||
const GwConverterConfig::WindMapping mapping2=config.findWindMapping(GwConverterConfig::WindMapping::TWD_TWS);
|
||||
if (mapping2.valid){
|
||||
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, WindDirection, mapping2.n2kType);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)mapping2.n2kType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -590,10 +602,10 @@ private:
|
|||
}
|
||||
//offset == 0? SK does not allow this
|
||||
if (Offset != NMEA0183DoubleNA && Offset>=0 ){
|
||||
if (! boatData->DBS->update(DepthBelowTransducer+Offset)) return;
|
||||
if (! boatData->DBS->update(DepthBelowTransducer+Offset,msg.sourceId)) return;
|
||||
}
|
||||
if (Offset == NMEA0183DoubleNA) Offset=N2kDoubleNA;
|
||||
if (! boatData->DBT->update(DepthBelowTransducer)) return;
|
||||
if (! boatData->DBT->update(DepthBelowTransducer,msg.sourceId)) return;
|
||||
tN2kMsg n2kMsg;
|
||||
SetN2kWaterDepth(n2kMsg,1,DepthBelowTransducer,Offset);
|
||||
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((Offset != N2kDoubleNA)?1:0));
|
||||
|
|
|
@ -469,39 +469,66 @@ private:
|
|||
unsigned char SID;
|
||||
tN2kWindReference WindReference;
|
||||
double WindAngle=N2kDoubleNA, WindSpeed=N2kDoubleNA;
|
||||
|
||||
tNMEA0183WindReference NMEA0183Reference;
|
||||
if (ParseN2kWindSpeed(N2kMsg, SID, WindSpeed, WindAngle, WindReference)) {
|
||||
tNMEA0183Msg NMEA0183Msg;
|
||||
tNMEA0183WindReference NMEA0183Reference;
|
||||
GwConverterConfig::WindMapping mapping=config.findWindMapping(WindReference);
|
||||
bool shouldSend = false;
|
||||
|
||||
// MWV sentence contains apparent/true ANGLE and SPEED
|
||||
// https://gpsd.gitlab.io/gpsd/NMEA.html#_mwv_wind_speed_and_angle
|
||||
// https://docs.vaisala.com/r/M211109EN-L/en-US/GUID-7402DEF8-5E82-446F-B63E-998F49F3D743/GUID-C77934C7-2A72-466E-BC52-CE6B8CC7ACB6
|
||||
|
||||
if (WindReference == N2kWind_Apparent) {
|
||||
if (mapping.valid)
|
||||
{
|
||||
if (mapping.nmea0183Type == GwConverterConfig::WindMapping::AWA_AWS)
|
||||
{
|
||||
NMEA0183Reference = NMEA0183Wind_Apparent;
|
||||
updateDouble(boatData->AWA, WindAngle);
|
||||
updateDouble(boatData->AWS, WindSpeed);
|
||||
setMax(boatData->MaxAws, boatData->AWS);
|
||||
shouldSend = true;
|
||||
}
|
||||
if (WindReference == N2kWind_True_water) {
|
||||
if (mapping.nmea0183Type == GwConverterConfig::WindMapping::TWA_TWS)
|
||||
{
|
||||
NMEA0183Reference = NMEA0183Wind_True;
|
||||
updateDouble(boatData->TWA, WindAngle);
|
||||
updateDouble(boatData->TWS, WindSpeed);
|
||||
setMax(boatData->MaxTws, boatData->TWS);
|
||||
shouldSend = true;
|
||||
if (boatData->HDT->isValid()) {
|
||||
double twd = WindAngle+boatData->HDT->getData();
|
||||
if (twd>2*M_PI) { twd-=2*M_PI; }
|
||||
if (boatData->HDT->isValid())
|
||||
{
|
||||
double twd = WindAngle + boatData->HDT->getData();
|
||||
if (twd > 2 * M_PI)
|
||||
{
|
||||
twd -= 2 * M_PI;
|
||||
}
|
||||
updateDouble(boatData->TWD, twd);
|
||||
}
|
||||
}
|
||||
if (mapping.nmea0183Type == GwConverterConfig::WindMapping::TWD_TWS)
|
||||
{
|
||||
NMEA0183Reference = NMEA0183Wind_True;
|
||||
updateDouble(boatData->TWD, WindAngle);
|
||||
updateDouble(boatData->TWS, WindSpeed);
|
||||
setMax(boatData->MaxTws, boatData->TWS);
|
||||
if (boatData->HDT->isValid())
|
||||
{
|
||||
shouldSend = true;
|
||||
double twa = WindAngle - boatData->HDT->getData();
|
||||
if (twa > 2 * M_PI)
|
||||
{
|
||||
twa -= 2 * M_PI;
|
||||
}
|
||||
updateDouble(boatData->TWA, twa);
|
||||
WindAngle=twa;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSend && NMEA0183SetMWV(NMEA0183Msg, formatCourse(WindAngle), NMEA0183Reference, WindSpeed, talkerId)) {
|
||||
if (shouldSend && NMEA0183SetMWV(NMEA0183Msg, formatCourse(WindAngle), NMEA0183Reference, WindSpeed, talkerId))
|
||||
{
|
||||
SendMessage(NMEA0183Msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* if (WindReference == N2kWind_Apparent && boatData->SOG->isValid())
|
||||
{ // Lets calculate and send TWS/TWA if SOG is available
|
||||
|
@ -1305,6 +1332,20 @@ private:
|
|||
return;
|
||||
}
|
||||
int i=0;
|
||||
if (TempSource == N2kts_SeaTemperature) {
|
||||
updateDouble(boatData->WTemp, Temperature);
|
||||
tNMEA0183Msg NMEA0183Msg;
|
||||
|
||||
if (!NMEA0183Msg.Init("MTW", talkerId))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddDoubleField(KelvinToC(Temperature)))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddStrField("C"))
|
||||
return;
|
||||
|
||||
SendMessage(NMEA0183Msg);
|
||||
}
|
||||
|
||||
GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,TempSource,0,0);
|
||||
if (updateDouble(&mapping,Temperature)){
|
||||
LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str());
|
||||
|
@ -1337,6 +1378,21 @@ private:
|
|||
LOG_DEBUG(GwLog::DEBUG,"unable to parse PGN %d",msg.PGN);
|
||||
return;
|
||||
}
|
||||
if (TemperatureSource == N2kts_SeaTemperature &&
|
||||
(config.winst312 == TemperatureInstance || config.winst312 == 256)) {
|
||||
updateDouble(boatData->WTemp, Temperature);
|
||||
tNMEA0183Msg NMEA0183Msg;
|
||||
|
||||
if (!NMEA0183Msg.Init("MTW", talkerId))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddDoubleField(KelvinToC(Temperature)))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddStrField("C"))
|
||||
return;
|
||||
|
||||
SendMessage(NMEA0183Msg);
|
||||
}
|
||||
|
||||
GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,0,TemperatureInstance);
|
||||
if (updateDouble(&mapping,Temperature)){
|
||||
LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str());
|
||||
|
|
|
@ -850,7 +850,7 @@ void setup() {
|
|||
xdrMappings.begin();
|
||||
logger.flush();
|
||||
GwConverterConfig converterConfig;
|
||||
converterConfig.init(&config);
|
||||
converterConfig.init(&config,&logger);
|
||||
nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,
|
||||
[](const tNMEA0183Msg &msg, int sourceId){
|
||||
SendNMEA0183Message(msg,sourceId,false);
|
||||
|
|
|
@ -56,7 +56,16 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
|
|||
def do_POST(self):
|
||||
if not self.do_proxy():
|
||||
super().do_POST()
|
||||
def guess_type(self,path):
|
||||
if path.endswith('.gz'):
|
||||
return super().guess_type(path[0:-3])
|
||||
return super().guess_type(path)
|
||||
def end_headers(self):
|
||||
if hasattr(self,"isgz") and self.isgz:
|
||||
self.send_header("Content-Encoding","gzip")
|
||||
super().end_headers()
|
||||
def translate_path(self, path):
|
||||
self.isgz=False
|
||||
"""Translate a /-separated PATH to the local filename syntax.
|
||||
|
||||
Components that mean special things to the local file system
|
||||
|
@ -90,6 +99,9 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
|
|||
rpath += '/'
|
||||
if os.path.exists(rpath):
|
||||
return rpath
|
||||
if os.path.exists(rpath+".gz"):
|
||||
self.isgz=True
|
||||
return rpath+".gz"
|
||||
if isSecond:
|
||||
return rpath
|
||||
isSecond=True
|
||||
|
|
119
web/config.json
119
web/config.json
|
@ -219,14 +219,54 @@
|
|||
"category":"converter"
|
||||
},
|
||||
{
|
||||
"name":"checkRMCt",
|
||||
"name": "checkRMCt",
|
||||
"label": "check RMC time",
|
||||
"type": "number",
|
||||
"description": "start sending RMC if we did not see an external RMC after this much ms",
|
||||
"default":"4000",
|
||||
"default": "4000",
|
||||
"min": 1000,
|
||||
"check":"checkMinMax",
|
||||
"category":"converter"
|
||||
"check": "checkMinMax",
|
||||
"category": "converter"
|
||||
},
|
||||
{
|
||||
"name": "timeouts",
|
||||
"type": "array",
|
||||
"replace": [
|
||||
{
|
||||
"n": "Default",
|
||||
"d": "4000",
|
||||
"l": "default",
|
||||
"t": "NMEA"
|
||||
},
|
||||
{
|
||||
"n": "Sensor",
|
||||
"d": "60000",
|
||||
"l": "sensor",
|
||||
"t": "sensor"
|
||||
},
|
||||
{
|
||||
"n": "Long",
|
||||
"d": "32000",
|
||||
"l": "long",
|
||||
"t": "special NMEA"
|
||||
},
|
||||
{
|
||||
"n": "Ais",
|
||||
"d": "120000",
|
||||
"l": "ais",
|
||||
"t": "ais"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"name": "timo$n",
|
||||
"label": "timeout $l",
|
||||
"default": "$d",
|
||||
"type": "number",
|
||||
"description": "data timeouts(ms) for $t data",
|
||||
"category": "converter"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stbRudderI",
|
||||
|
@ -251,45 +291,70 @@
|
|||
"category": "converter"
|
||||
},
|
||||
{
|
||||
"name": "timeouts",
|
||||
"name": "windmappings",
|
||||
"type": "array",
|
||||
"replace":[
|
||||
{
|
||||
"n":"Default",
|
||||
"d":"4000",
|
||||
"l": "default",
|
||||
"t": "NMEA"
|
||||
"n": "tng",
|
||||
"l": "true north ground",
|
||||
"t": "True_North=0",
|
||||
"d": "twa_tws"
|
||||
},
|
||||
{
|
||||
"n":"Sensor",
|
||||
"d":"60000",
|
||||
"l": "sensor",
|
||||
"t": "sensor"
|
||||
"n": "mgd",
|
||||
"l": "magnetic ground dir",
|
||||
"t": "Magnetic=1",
|
||||
"d":""
|
||||
},
|
||||
{
|
||||
"n":"Long",
|
||||
"d":"32000",
|
||||
"l": "long",
|
||||
"t": "special NMEA"
|
||||
"n": "awa",
|
||||
"l": "apparent angle",
|
||||
"t": "Apparent=2",
|
||||
"d":"awa_aws"
|
||||
},
|
||||
{
|
||||
"n":"Ais",
|
||||
"d":"120000",
|
||||
"l": "ais",
|
||||
"t": "ais"
|
||||
"n": "gna",
|
||||
"l": "ground angle",
|
||||
"t": "True_boat=3",
|
||||
"d": ""
|
||||
},
|
||||
{
|
||||
"n": "tra",
|
||||
"l": "true angle",
|
||||
"t": "True_water=4",
|
||||
"d":""
|
||||
}
|
||||
|
||||
],
|
||||
"children":[
|
||||
{
|
||||
"name":"timo$n",
|
||||
"label":"timeout $l",
|
||||
"default": "$d",
|
||||
"type": "number",
|
||||
"description": "data timeouts(ms) for $t data",
|
||||
"category": "converter"
|
||||
"name":"windm$n",
|
||||
"type":"list",
|
||||
"description": "mapping of the PGN 130306 wind reference $t",
|
||||
"label":"wind $l",
|
||||
"list":[
|
||||
{"l": "-unset-","v":""},
|
||||
{"l": "TWA/TWS","v":"twa_tws"},
|
||||
{"l": "AWA/AWS", "v":"awa_aws"},
|
||||
{"l": "TWD/TWS","v":"twd_tws"}
|
||||
],
|
||||
"category":"converter",
|
||||
"default":"$d"
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "winst312",
|
||||
"label": "130312 WTemp iid",
|
||||
"type": "number",
|
||||
"check": "checkMinMax",
|
||||
"min": -1,
|
||||
"max": 256,
|
||||
"description": "the temp instance of PGN 130312 used for water temperature (0...255), use -1 for none, 256 for any",
|
||||
"default": "256",
|
||||
"category":"converter"
|
||||
},
|
||||
{
|
||||
"name": "usbActisense",
|
||||
"label": "USB mode",
|
||||
|
|
|
@ -22,7 +22,7 @@ body {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tabPage{
|
||||
#tabPages{
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
@ -120,6 +120,9 @@ body {
|
|||
.hidden{
|
||||
display: none !important;
|
||||
}
|
||||
.dash.invalid{
|
||||
display: none;
|
||||
}
|
||||
#xdrPage .row>.label{
|
||||
display: none;
|
||||
}
|
||||
|
@ -220,6 +223,7 @@ body {
|
|||
}
|
||||
#tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
border-bottom: 1px solid grey;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
@ -341,3 +345,6 @@ body {
|
|||
.error{
|
||||
color: red;
|
||||
}
|
||||
input.error{
|
||||
background-color: rgba(255, 0, 0, 0.329);
|
||||
}
|
|
@ -23,7 +23,8 @@
|
|||
<div class="tab" data-page="updatePage">Update</div>
|
||||
<div class="tab" data-url="https://github.com/wellenvogel/esp32-nmea2000" data-window="help" id="helpButton">Help</div>
|
||||
</div>
|
||||
<div id="statusPage" class="tabPage">
|
||||
<div id="tabPages">
|
||||
<div id="statusPage" class="tabPage">
|
||||
<div id="statusPageContent">
|
||||
<div class="row">
|
||||
<span class="label">VERSION</span>
|
||||
|
@ -66,8 +67,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<button id="reset">Reset</button>
|
||||
</div>
|
||||
<div class="configForm tabPage hidden" id="configPage" >
|
||||
</div>
|
||||
<div class="configForm tabPage hidden" id="configPage">
|
||||
<div class="buttons">
|
||||
<button id="resetForm">ReloadConfig</button>
|
||||
<button id="forgetPass">ForgetPass</button>
|
||||
|
@ -79,8 +80,8 @@
|
|||
<div class="configFormRows">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="configForm tabPage hidden" id="xdrPage" >
|
||||
</div>
|
||||
<div class="configForm tabPage hidden" id="xdrPage">
|
||||
<div class="buttons">
|
||||
<button id="resetForm">ReloadConfig</button>
|
||||
<button id="changeConfig">Save&Restart</button>
|
||||
|
@ -91,11 +92,11 @@
|
|||
<div class="configFormRows">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabPage hidden" id="dashboardPage">
|
||||
</div>
|
||||
<div class="tabPage hidden" id="dashboardPage">
|
||||
|
||||
</div>
|
||||
<div class="tabPage hidden" id="updatePage">
|
||||
</div>
|
||||
<div class="tabPage hidden" id="updatePage">
|
||||
<div class="row">
|
||||
<span class="label">firmware type</span>
|
||||
<span class="value status-fwtype">---</span>
|
||||
|
@ -122,6 +123,7 @@
|
|||
<div class="buttons">
|
||||
<button id="uploadBin">Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
2079
web/index.js
2079
web/index.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue