Merge branch 'wellenvogel:master' into master

This commit is contained in:
Norbert Walter 2024-10-17 16:49:32 +02:00 committed by GitHub
commit 96dfd9e773
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 2540 additions and 2010 deletions

View File

@ -10,6 +10,8 @@ jobs:
os: [ubuntu-latest] os: [ubuntu-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env:
PIP_BREAK_SYSTEM_PACKAGES: 1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:

View File

@ -18,6 +18,9 @@ jobs:
# The type of runner that the job will run on # The type of runner that the job will run on
runs-on: ubuntu-latest 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 represent a sequence of tasks that will be executed as part of the job
steps: steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it

Binary file not shown.

Binary file not shown.

View File

@ -18,6 +18,8 @@ OWN_FILE="extra_script.py"
GEN_DIR='lib/generated' GEN_DIR='lib/generated'
CFG_FILE='web/config.json' CFG_FILE='web/config.json'
XDR_FILE='web/xdrconfig.json' XDR_FILE='web/xdrconfig.json'
INDEXJS="index.js"
INDEXCSS="index.css"
CFG_INCLUDE='GwConfigDefinitions.h' CFG_INCLUDE='GwConfigDefinitions.h'
CFG_INCLUDE_IMPL='GwConfigDefImpl.h' CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
XDR_INCLUDE='GwXdrTypeMappings.h' XDR_INCLUDE='GwXdrTypeMappings.h'
@ -66,6 +68,7 @@ def isCurrent(infile,outfile):
def compressFile(inFile,outfile): def compressFile(inFile,outfile):
if isCurrent(inFile,outfile): if isCurrent(inFile,outfile):
return return
print("compressing %s"%inFile)
with open(inFile, 'rb') as f_in: with open(inFile, 'rb') as f_in:
with gzip.open(outfile, 'wb') as f_out: with gzip.open(outfile, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out) shutil.copyfileobj(f_in, f_out)
@ -372,6 +375,16 @@ def getLibs():
rt.append(e) rt.append(e)
return rt 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"] OWNLIBS=getLibs()+["FS","WiFi"]
GLOBAL_INCLUDES=[] GLOBAL_INCLUDES=[]
@ -440,6 +453,8 @@ def prebuild(env):
compressFile(mergedConfig,mergedConfig+".gz") compressFile(mergedConfig,mergedConfig+".gz")
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False) generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False)
generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE_IMPL),True) 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) embedded=getEmbeddedFiles(env)
filedefs=[] filedefs=[]
for ef in embedded: for ef in embedded:
@ -453,7 +468,6 @@ def prebuild(env):
filedefs.append((pureName,usname,ct)) filedefs.append((pureName,usname,ct))
inFile=os.path.join(basePath(),"web",pureName) inFile=os.path.join(basePath(),"web",pureName)
if os.path.exists(inFile): if os.path.exists(inFile):
print("compressing %s"%inFile)
compressFile(inFile,ef) compressFile(inFile,ef)
else: else:
print("#WARNING: infile %s for %s not found"%(inFile,ef)) print("#WARNING: infile %s for %s not found"%(inFile,ef))

View File

@ -47,7 +47,6 @@ GwBoatItemBase::GwBoatItemBase(String name, String format, GwBoatItemBase::TOTyp
this->format = format; this->format = format;
this->type = 0; this->type = 0;
this->lastUpdateSource = -1; this->lastUpdateSource = -1;
this->toType=TOType::user;
} }
void GwBoatItemBase::setInvalidTime(unsigned long it, bool force){ void GwBoatItemBase::setInvalidTime(unsigned long it, bool force){
if (toType != TOType::user || force ){ if (toType != TOType::user || force ){
@ -375,7 +374,7 @@ GwBoatItem<T> *GwBoatData::getOrCreate(T initial, GwBoatItemNameProvider *provid
provider->getBoatItemFormat(), provider->getBoatItemFormat(),
provider->getInvalidTime(), provider->getInvalidTime(),
&values); &values);
rt->update(initial); rt->update(initial,-1);
LOG_DEBUG(GwLog::LOG, "creating boatItem %s, type %d", LOG_DEBUG(GwLog::LOG, "creating boatItem %s, type %d",
name.c_str(), rt->getCurrentType()); name.c_str(), rt->getCurrentType());
return rt; return rt;

View File

@ -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,unsigned long invalidTime=INVALID_TIME,GwBoatItemMap *map=NULL);
GwBoatItem(String name,String formatInfo,TOType toType,GwBoatItemMap *map=NULL); GwBoatItem(String name,String formatInfo,TOType toType,GwBoatItemMap *map=NULL);
virtual ~GwBoatItem(){} virtual ~GwBoatItem(){}
bool update(T nv, int source=-1); bool update(T nv, int source);
bool updateMax(T nv,int sourceId=-1); bool updateMax(T nv,int sourceId);
T getData(){ T getData(){
return data; return data;
} }
@ -185,7 +185,6 @@ public:
#define GWSPECBOATDATA(clazz,name,toType,fmt) \ #define GWSPECBOATDATA(clazz,name,toType,fmt) \
clazz *name=new clazz(#name,GwBoatItemBase::fmt,toType,&values) ; clazz *name=new clazz(#name,GwBoatItemBase::fmt,toType,&values) ;
class GwBoatData{ class GwBoatData{
static const unsigned long DEF_TIME=4000;
private: private:
GwLog *logger; GwLog *logger;
GwBoatItemBase::GwBoatItemMap values; GwBoatItemBase::GwBoatItemMap values;

View File

@ -16,16 +16,63 @@
#define _GWCONVERTERCONFIG_H #define _GWCONVERTERCONFIG_H
#include "GWConfig.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{ class GwConverterConfig{
public: 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 minXdrInterval=100;
int starboardRudderInstance=0; int starboardRudderInstance=0;
int portRudderInstance=-1; //ignore int portRudderInstance=-1; //ignore
int min2KInterval=50; int min2KInterval=50;
int rmcInterval=1000; int rmcInterval=1000;
int rmcCheckTime=4000; 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); minXdrInterval=config->getInt(GwConfigDefinitions::minXdrInterval,100);
starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0); starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0);
portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1); portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1);
@ -36,6 +83,30 @@ class GwConverterConfig{
rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000); rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000);
if (rmcInterval < 0) rmcInterval=0; if (rmcInterval < 0) rmcInterval=0;
if (rmcInterval > 0 && rmcInterval <100) rmcInterval=100; 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 #endif

View File

@ -0,0 +1,3 @@
.examplecss{
background-color: coral;
}

88
lib/exampletask/index.js Normal file
View File

@ -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);
}
}
}
})
})();

View File

@ -103,7 +103,7 @@ private:
if (v != NMEA0183UInt32NA){ if (v != NMEA0183UInt32NA){
return target->update(v,sourceId); return target->update(v,sourceId);
} }
return v; return false;
} }
uint32_t getUint32(GwBoatItem<uint32_t> *src){ uint32_t getUint32(GwBoatItem<uint32_t> *src){
return src->getDataWithDefault(N2kUInt32NA); return src->getDataWithDefault(N2kUInt32NA);
@ -399,28 +399,29 @@ private:
return; return;
} }
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
tN2kWindReference n2kRef;
bool shouldSend=false; bool shouldSend=false;
WindAngle=formatDegToRad(WindAngle); WindAngle=formatDegToRad(WindAngle);
GwConverterConfig::WindMapping mapping;
switch(Reference){ switch(Reference){
case NMEA0183Wind_Apparent: case NMEA0183Wind_Apparent:
n2kRef=N2kWind_Apparent;
shouldSend=updateDouble(boatData->AWA,WindAngle,msg.sourceId) && shouldSend=updateDouble(boatData->AWA,WindAngle,msg.sourceId) &&
updateDouble(boatData->AWS,WindSpeed,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; break;
case NMEA0183Wind_True: case NMEA0183Wind_True:
n2kRef=N2kWind_True_water;
shouldSend=updateDouble(boatData->TWA,WindAngle,msg.sourceId) && shouldSend=updateDouble(boatData->TWA,WindAngle,msg.sourceId) &&
updateDouble(boatData->TWS,WindSpeed,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; break;
default: default:
LOG_DEBUG(GwLog::DEBUG,"unknown wind reference %d in %s",(int)Reference,msg.line); LOG_DEBUG(GwLog::DEBUG,"unknown wind reference %d in %s",(int)Reference,msg.line);
} }
if (shouldSend){ //TODO: try to compute TWD and get mapping for this one
SetN2kWindSpeed(n2kMsg,1,WindSpeed,WindAngle,n2kRef); if (shouldSend && mapping.valid){
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)n2kRef)); SetN2kWindSpeed(n2kMsg,1,WindSpeed,WindAngle,mapping.n2kType);
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)mapping.n2kType));
} }
} }
void convertVWR(const SNMEA0183Msg &msg) void convertVWR(const SNMEA0183Msg &msg)
@ -457,11 +458,14 @@ private:
bool shouldSend = false; bool shouldSend = false;
shouldSend = updateDouble(boatData->AWA, WindAngle, msg.sourceId) && shouldSend = updateDouble(boatData->AWA, WindAngle, msg.sourceId) &&
updateDouble(boatData->AWS, WindSpeed, 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) if (shouldSend)
{ {
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, WindAngle, N2kWind_Apparent); const GwConverterConfig::WindMapping mapping=config.findWindMapping(GwConverterConfig::WindMapping::AWA_AWS);
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)N2kWind_Apparent)); 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){ if (WindDirection != NMEA0183DoubleNA){
shouldSend = updateDouble(boatData->TWD, WindDirection, msg.sourceId) && shouldSend = updateDouble(boatData->TWD, WindDirection, msg.sourceId) &&
updateDouble(boatData->TWS, WindSpeed, 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()) { if(shouldSend && boatData->HDT->isValid()) {
double twa = WindDirection-boatData->HDT->getData(); double twa = WindDirection-boatData->HDT->getData();
if(twa<0) { twa+=2*M_PI; } if(twa<0) { twa+=2*M_PI; }
updateDouble(boatData->TWA, twa, msg.sourceId); updateDouble(boatData->TWA, twa, msg.sourceId);
SetN2kWindSpeed(n2kMsg, 1, WindSpeed, twa, N2kWind_True_water); const GwConverterConfig::WindMapping mapping=config.findWindMapping(GwConverterConfig::WindMapping::TWA_TWS);
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((int)N2kWind_True_water)); 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 //offset == 0? SK does not allow this
if (Offset != NMEA0183DoubleNA && Offset>=0 ){ 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 (Offset == NMEA0183DoubleNA) Offset=N2kDoubleNA;
if (! boatData->DBT->update(DepthBelowTransducer)) return; if (! boatData->DBT->update(DepthBelowTransducer,msg.sourceId)) return;
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
SetN2kWaterDepth(n2kMsg,1,DepthBelowTransducer,Offset); SetN2kWaterDepth(n2kMsg,1,DepthBelowTransducer,Offset);
send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((Offset != N2kDoubleNA)?1:0)); send(n2kMsg,msg.sourceId,String(n2kMsg.PGN)+String((Offset != N2kDoubleNA)?1:0));

View File

@ -469,38 +469,65 @@ private:
unsigned char SID; unsigned char SID;
tN2kWindReference WindReference; tN2kWindReference WindReference;
double WindAngle=N2kDoubleNA, WindSpeed=N2kDoubleNA; double WindAngle=N2kDoubleNA, WindSpeed=N2kDoubleNA;
tNMEA0183WindReference NMEA0183Reference;
if (ParseN2kWindSpeed(N2kMsg, SID, WindSpeed, WindAngle, WindReference)) { if (ParseN2kWindSpeed(N2kMsg, SID, WindSpeed, WindAngle, WindReference)) {
tNMEA0183Msg NMEA0183Msg; tNMEA0183Msg NMEA0183Msg;
tNMEA0183WindReference NMEA0183Reference; GwConverterConfig::WindMapping mapping=config.findWindMapping(WindReference);
bool shouldSend = false; bool shouldSend = false;
// MWV sentence contains apparent/true ANGLE and SPEED // MWV sentence contains apparent/true ANGLE and SPEED
// https://gpsd.gitlab.io/gpsd/NMEA.html#_mwv_wind_speed_and_angle // 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 // https://docs.vaisala.com/r/M211109EN-L/en-US/GUID-7402DEF8-5E82-446F-B63E-998F49F3D743/GUID-C77934C7-2A72-466E-BC52-CE6B8CC7ACB6
if (mapping.valid)
if (WindReference == N2kWind_Apparent) { {
NMEA0183Reference = NMEA0183Wind_Apparent; if (mapping.nmea0183Type == GwConverterConfig::WindMapping::AWA_AWS)
updateDouble(boatData->AWA, WindAngle); {
updateDouble(boatData->AWS, WindSpeed); NMEA0183Reference = NMEA0183Wind_Apparent;
setMax(boatData->MaxAws, boatData->AWS); updateDouble(boatData->AWA, WindAngle);
shouldSend = true; updateDouble(boatData->AWS, WindSpeed);
} setMax(boatData->MaxAws, boatData->AWS);
if (WindReference == N2kWind_True_water) { shouldSend = true;
NMEA0183Reference = NMEA0183Wind_True; }
updateDouble(boatData->TWA, WindAngle); if (mapping.nmea0183Type == GwConverterConfig::WindMapping::TWA_TWS)
updateDouble(boatData->TWS, WindSpeed); {
setMax(boatData->MaxTws, boatData->TWS); NMEA0183Reference = NMEA0183Wind_True;
shouldSend = true; updateDouble(boatData->TWA, WindAngle);
if (boatData->HDT->isValid()) { updateDouble(boatData->TWS, WindSpeed);
double twd = WindAngle+boatData->HDT->getData(); setMax(boatData->MaxTws, boatData->TWS);
if (twd>2*M_PI) { twd-=2*M_PI; } shouldSend = true;
updateDouble(boatData->TWD, twd); 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); {
SendMessage(NMEA0183Msg);
}
} }
/* if (WindReference == N2kWind_Apparent && boatData->SOG->isValid()) /* if (WindReference == N2kWind_Apparent && boatData->SOG->isValid())
@ -1305,6 +1332,20 @@ private:
return; return;
} }
int i=0; 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); GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,TempSource,0,0);
if (updateDouble(&mapping,Temperature)){ if (updateDouble(&mapping,Temperature)){
LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); 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); LOG_DEBUG(GwLog::DEBUG,"unable to parse PGN %d",msg.PGN);
return; 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); GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,0,TemperatureInstance);
if (updateDouble(&mapping,Temperature)){ if (updateDouble(&mapping,Temperature)){
LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str());

View File

@ -850,7 +850,7 @@ void setup() {
xdrMappings.begin(); xdrMappings.begin();
logger.flush(); logger.flush();
GwConverterConfig converterConfig; GwConverterConfig converterConfig;
converterConfig.init(&config); converterConfig.init(&config,&logger);
nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData, nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,
[](const tNMEA0183Msg &msg, int sourceId){ [](const tNMEA0183Msg &msg, int sourceId){
SendNMEA0183Message(msg,sourceId,false); SendNMEA0183Message(msg,sourceId,false);

View File

@ -56,7 +56,16 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
def do_POST(self): def do_POST(self):
if not self.do_proxy(): if not self.do_proxy():
super().do_POST() 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): def translate_path(self, path):
self.isgz=False
"""Translate a /-separated PATH to the local filename syntax. """Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system Components that mean special things to the local file system
@ -90,6 +99,9 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
rpath += '/' rpath += '/'
if os.path.exists(rpath): if os.path.exists(rpath):
return rpath return rpath
if os.path.exists(rpath+".gz"):
self.isgz=True
return rpath+".gz"
if isSecond: if isSecond:
return rpath return rpath
isSecond=True isSecond=True

View File

@ -219,16 +219,56 @@
"category":"converter" "category":"converter"
}, },
{ {
"name":"checkRMCt", "name": "checkRMCt",
"label": "check RMC time", "label": "check RMC time",
"type": "number", "type": "number",
"description": "start sending RMC if we did not see an external RMC after this much ms", "description": "start sending RMC if we did not see an external RMC after this much ms",
"default":"4000", "default": "4000",
"min": 1000, "min": 1000,
"check":"checkMinMax", "check": "checkMinMax",
"category":"converter" "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", "name": "stbRudderI",
"label":"stb rudder instance", "label":"stb rudder instance",
"type": "number", "type": "number",
@ -251,45 +291,70 @@
"category": "converter" "category": "converter"
}, },
{ {
"name": "timeouts", "name": "windmappings",
"type": "array", "type": "array",
"replace":[ "replace":[
{ {
"n":"Default", "n": "tng",
"d":"4000", "l": "true north ground",
"l": "default", "t": "True_North=0",
"t": "NMEA" "d": "twa_tws"
}, },
{ {
"n":"Sensor", "n": "mgd",
"d":"60000", "l": "magnetic ground dir",
"l": "sensor", "t": "Magnetic=1",
"t": "sensor" "d":""
}, },
{ {
"n":"Long", "n": "awa",
"d":"32000", "l": "apparent angle",
"l": "long", "t": "Apparent=2",
"t": "special NMEA" "d":"awa_aws"
}, },
{ {
"n":"Ais", "n": "gna",
"d":"120000", "l": "ground angle",
"l": "ais", "t": "True_boat=3",
"t": "ais" "d": ""
},
{
"n": "tra",
"l": "true angle",
"t": "True_water=4",
"d":""
} }
], ],
"children":[ "children":[
{ {
"name":"timo$n", "name":"windm$n",
"label":"timeout $l", "type":"list",
"default": "$d", "description": "mapping of the PGN 130306 wind reference $t",
"type": "number", "label":"wind $l",
"description": "data timeouts(ms) for $t data", "list":[
"category": "converter" {"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", "name": "usbActisense",
"label": "USB mode", "label": "USB mode",

View File

@ -22,7 +22,7 @@ body {
overflow: hidden; overflow: hidden;
} }
.tabPage{ #tabPages{
overflow: auto; overflow: auto;
} }
@ -120,6 +120,9 @@ body {
.hidden{ .hidden{
display: none !important; display: none !important;
} }
.dash.invalid{
display: none;
}
#xdrPage .row>.label{ #xdrPage .row>.label{
display: none; display: none;
} }
@ -220,6 +223,7 @@ body {
} }
#tabs { #tabs {
display: flex; display: flex;
flex-wrap: wrap;
border-bottom: 1px solid grey; border-bottom: 1px solid grey;
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
@ -341,3 +345,6 @@ body {
.error{ .error{
color: red; color: red;
} }
input.error{
background-color: rgba(255, 0, 0, 0.329);
}

View File

@ -23,104 +23,106 @@
<div class="tab" data-page="updatePage">Update</div> <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 class="tab" data-url="https://github.com/wellenvogel/esp32-nmea2000" data-window="help" id="helpButton">Help</div>
</div> </div>
<div id="statusPage" class="tabPage"> <div id="tabPages">
<div id="statusPageContent"> <div id="statusPage" class="tabPage">
<div class="row"> <div id="statusPageContent">
<span class="label">VERSION</span> <div class="row">
<span class="value" id="version">---</span> <span class="label">VERSION</span>
<button class="infoButton" id="converterInfo">?</button> <span class="value" id="version">---</span>
</div> <button class="infoButton" id="converterInfo">?</button>
</div>
<div class="row even"> <div class="row even">
<span class="label">Access Point IP</span> <span class="label">Access Point IP</span>
<span class="value" id="apIp">---</span> <span class="value" id="apIp">---</span>
</div>
<div class="row ">
<span class="label">wifi client connected</span>
<span class="value" id="wifiConnected">---</span>&nbsp;[<span class="value" id="wifiSSID">---</span>]
</div>
<div class="row even">
<span class="label">wifi client IP</span>
<span class="value" id="clientIP">---</span>
</div>
<div class="row">
<span class="label"># clients</span>
<span class="value" id="numClients">---</span>
</div>
<div class="row even">
<span class="label">TCP client connected</span>
<span class="value" id="clientCon">---</span>
</div>
<div class="row">
<span class="label">TCP client error</span>
<span class="value" id="clientErr">---</span>
</div>
<div class="row even">
<span class="label">Free heap</span>
<span class="value" id="heap">---</span>
</div>
<div class="row">
<span class="label">NMEA2000 State</span>
[<span class="value" id="n2knode">---</span>]&nbsp;
<span class="value" id="n2kstate">UNKNOWN</span>
</div>
</div> </div>
<div class="row "> <button id="reset">Reset</button>
<span class="label">wifi client connected</span> </div>
<span class="value" id="wifiConnected">---</span>&nbsp;[<span class="value" id="wifiSSID">---</span>] <div class="configForm tabPage hidden" id="configPage">
<div class="buttons">
<button id="resetForm">ReloadConfig</button>
<button id="forgetPass">ForgetPass</button>
<button id="changeConfig">Save&Restart</button>
<button id="exportConfig">Export</button>
<button id="importConfig">Import</button>
<button id="factoryReset">FactoryReset</button>
</div> </div>
<div class="row even"> <div class="configFormRows">
<span class="label">wifi client IP</span>
<span class="value" id="clientIP">---</span>
</div>
<div class="row">
<span class="label"># clients</span>
<span class="value" id="numClients">---</span>
</div>
<div class="row even">
<span class="label">TCP client connected</span>
<span class="value" id="clientCon">---</span>
</div>
<div class="row">
<span class="label">TCP client error</span>
<span class="value" id="clientErr">---</span>
</div>
<div class="row even">
<span class="label">Free heap</span>
<span class="value" id="heap">---</span>
</div>
<div class="row">
<span class="label">NMEA2000 State</span>
[<span class="value" id="n2knode">---</span>]&nbsp;
<span class="value" id="n2kstate">UNKNOWN</span>
</div> </div>
</div> </div>
<button id="reset">Reset</button> <div class="configForm tabPage hidden" id="xdrPage">
</div> <div class="buttons">
<div class="configForm tabPage hidden" id="configPage" > <button id="resetForm">ReloadConfig</button>
<div class="buttons"> <button id="changeConfig">Save&Restart</button>
<button id="resetForm">ReloadConfig</button> <button id="loadUnassigned">Show Unmapped</button>
<button id="forgetPass">ForgetPass</button> <button id="exportXdr">Export</button>
<button id="changeConfig">Save&Restart</button> <button id="importXdr">Import</button>
<button id="exportConfig">Export</button> </div>
<button id="importConfig">Import</button> <div class="configFormRows">
<button id="factoryReset">FactoryReset</button>
</div>
</div> </div>
<div class="configFormRows"> <div class="tabPage hidden" id="dashboardPage">
</div> </div>
</div> <div class="tabPage hidden" id="updatePage">
<div class="configForm tabPage hidden" id="xdrPage" > <div class="row">
<div class="buttons"> <span class="label">firmware type</span>
<button id="resetForm">ReloadConfig</button> <span class="value status-fwtype">---</span>
<button id="changeConfig">Save&Restart</button> </div>
<button id="loadUnassigned">Show Unmapped</button> <div class="row">
<button id="exportXdr">Export</button> <span class="label">chip type</span>
<button id="importXdr">Import</button> <span class="value status-chipid">---</span>
</div> </div>
<div class="configFormRows"> <div class="row">
<span class="label">currentVersion</span>
</div> <span class="value status-version">---</span>
</div> </div>
<div class="tabPage hidden" id="dashboardPage"> <div class="row">
<span class="label">New Firmware</span>
</div> <input type="file" name="file1" id="uploadFile">
<div class="tabPage hidden" id="updatePage"> </div>
<div class="row"> <div class="row">
<span class="label">firmware type</span> <span class="label"></span>
<span class="value status-fwtype">---</span> <span id="imageProperties" class="value"></span>
</div> </div>
<div class="row"> <div id="uploadProgress">
<span class="label">chip type</span> <div id="uploadDone"></div>
<span class="value status-chipid">---</span> </div>
</div> <div class="buttons">
<div class="row"> <button id="uploadBin">Upload</button>
<span class="label">currentVersion</span> </div>
<span class="value status-version">---</span>
</div>
<div class="row">
<span class="label">New Firmware</span>
<input type="file" name="file1" id="uploadFile">
</div>
<div class="row">
<span class="label"></span>
<span id="imageProperties" class="value"></span>
</div>
<div id="uploadProgress">
<div id="uploadDone"></div>
</div>
<div class="buttons">
<button id="uploadBin">Upload</button>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff