From d927861cdf5c585ff06d7598ef6de295bf0785e7 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Sat, 20 Nov 2021 11:20:49 +0100 Subject: [PATCH] intermediate: xdr mappings --- extra_script.py | 65 +++++- lib/xdrmappings/GwXDRMappings.cpp | 318 +++++++++++++++++++++--------- lib/xdrmappings/GwXDRMappings.h | 74 ++++--- web/xdrconfig.json | 33 ++-- 4 files changed, 361 insertions(+), 129 deletions(-) diff --git a/extra_script.py b/extra_script.py index b7a1b2a..5c1bca7 100644 --- a/extra_script.py +++ b/extra_script.py @@ -9,8 +9,11 @@ from datetime import datetime Import("env") GEN_DIR='generated' CFG_FILE='web/config.json' -FILES=['web/index.html',CFG_FILE,'web/index.js','web/index.css'] +XDR_FILE='web/xdrconfig.json' +FILES=['web/index.html',CFG_FILE,XDR_FILE,'web/index.js','web/index.css'] CFG_INCLUDE='GwConfigDefinitions.h' +XDR_INCLUDE='GwXdrTypeMappings.h' + def basePath(): #see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined @@ -90,11 +93,71 @@ def generateCfg(): os.unlink(outfile) raise +def generateXdrMappings(): + outfile=os.path.join(outPath(),XDR_INCLUDE) + infile=os.path.join(basePath(),XDR_FILE) + if isCurrent(infile,outfile): + return + print("creating %s"%XDR_INCLUDE) + oh=None + + with open(infile,"rb") as fp: + jdoc=json.load(fp) + try: + with open(outfile,"w") as oh: + oh.write("static GwXDRTypeMapping* typeMappings[]={\n") + first=True + for cat in jdoc: + item=jdoc[cat] + cid=item.get('id') + if cid is None: + continue + tc=item.get('type') + if tc is not None: + if first: + first=False + else: + oh.write(",\n") + oh.write(" new GwXDRTypeMapping(%d,%d,0)"%(cid,tc)) + fields=item.get('fields') + if fields is None: + continue + idx=0 + for fe in fields: + if not isinstance(fe,dict): + continue + tc=fe.get('t') + id=fe.get('v') + if id is None: + id=idx + idx+=1 + l=fe.get('l') or '' + if tc is None or id is None: + continue + if first: + first=False + else: + oh.write(",\n") + oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s*/"%(cid,tc,id,l)) + oh.write("\n") + oh.write("};\n") + except Exception as e: + if oh: + try: + oh.close() + except: + pass + os.unlink(outfile) + raise + + + if not checkDir(): sys.exit(1) for f in FILES: print("compressing %s"%f) compressFile(f) generateCfg() +generateXdrMappings() version="dev"+datetime.now().strftime("%Y%m%d") env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) diff --git a/lib/xdrmappings/GwXDRMappings.cpp b/lib/xdrmappings/GwXDRMappings.cpp index bdd6b9e..0af8f62 100644 --- a/lib/xdrmappings/GwXDRMappings.cpp +++ b/lib/xdrmappings/GwXDRMappings.cpp @@ -1,100 +1,183 @@ #include "GwXDRMappings.h" #include "N2kMessages.h" -double PtoBar(double v){ - if (N2kIsNA(v)) return v; - return v/100000L; +double PtoBar(double v) +{ + if (N2kIsNA(v)) + return v; + return v / 100000L; } -double BarToP(double v){ - if (N2kIsNA(v)) return v; - return v*100000L; +double BarToP(double v) +{ + if (N2kIsNA(v)) + return v; + return v * 100000L; } -GwXDRType * types[]={ - new GwXDRType(GwXDRType::PRESS,"P","P"), - new GwXDRType(GwXDRType::PRESS,"P","B", - PtoBar, - BarToP), - new GwXDRType(GwXDRType::VOLT,"U","V"), - new GwXDRType(GwXDRType::AMP,"I","A"), - NULL -}; +double ltrTom3(double v) +{ + if (N2kIsNA(v)) + return v; + return v / 1000.0; +} +double m3ToL(double v) +{ + if (N2kIsNA(v)) + return v; + return v * 1000.0; +} +double ph2ps(double v) +{ + if (N2kIsNA(v)) + return v; + return v * 3600.0; +} +double ps2ph(double v) +{ + if (N2kIsNA(v)) + return v; + return v / 3600.0; +} +//https://www.nmea.org/Assets/20190916%20Standards%20Update%20NMEA%20Conferencev2.pdf +GwXDRType *types[] = { + new GwXDRType(GwXDRType::PRESS, "P", "P"), + new GwXDRType(GwXDRType::PRESS, "P", "B", + PtoBar, + BarToP), + new GwXDRType(GwXDRType::VOLT, "U", "V"), + new GwXDRType(GwXDRType::AMP, "I", "A"), + new GwXDRType(GwXDRType::TEMP, "C", "K"), + new GwXDRType(GwXDRType::TEMP, "C", "C", CToKelvin, KelvinToC), + new GwXDRType(GwXDRType::HUMID, "H", "P"), //percent + new GwXDRType(GwXDRType::VOLPERCENT, "V", "P"), + new GwXDRType(GwXDRType::VOLUME, "V", "M", m3ToL, ltrTom3), + new GwXDRType(GwXDRType::FLOW, "R", "I", ps2ph, ph2ps), + //important to have 2x NULL! + NULL, + NULL}; +static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) +{ + int from = 0; + if (start != NULL) + from = *start; + if (types[from] == NULL) + return NULL; + int i = from; + for (; types[i] != NULL; i++) + { + if (types[i]->code == type) + { + if (start != NULL) + *start = i + 1; + return types[i]; + } + } + if (start != NULL) + *start = i; + return NULL; +} -String GwXDRMappingDef::toString(){ - String rt=""; - rt+=String((int)category); - rt+=","; - rt+=String((int)direction); - rt+=","; - rt+=String(selector); - rt+=","; - rt+=String(field); - rt+=","; - rt+=String(instanceMode); - rt+=","; - rt+=String(instanceId); +#include "GwXdrTypeMappings.h" + +static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field) +{ + for (int i = 0; typeMappings[i] != NULL; i++) + { + if (typeMappings[i]->fieldIndex == field && + typeMappings[i]->category == category) + { + return typeMappings[i]->type; + } + } + return GwXDRType::UNKNOWN; +} + +String GwXDRMappingDef::toString() +{ + String rt = ""; + rt += String((int)category); + rt += ","; + rt += String((int)direction); + rt += ","; + rt += String(selector); + rt += ","; + rt += String(field); + rt += ","; + rt += String(instanceMode); + rt += ","; + rt += String(instanceId); return rt; } -bool GwXDRMappingDef::handleToken(String tok,int index,GwXDRMappingDef *def){ - switch(index){ +bool GwXDRMappingDef::handleToken(String tok, int index, GwXDRMappingDef *def) +{ + switch (index) + { int i; - case 0: - //category - i=tok.toInt(); - if (i< XDRTEMP || i > XDRENGINE) return false; - def->category=(GwXDRCategory)i; - return true; - case 1: - //direction - i=tok.toInt(); - if (i < GwXDRMappingDef::M_DISABLED || - i>= GwXDRMappingDef::M_LAST) return false; - def->direction=(GwXDRMappingDef::Direction)i; - return true; - case 2: - //selector - //TODO: check selector? - i=tok.toInt(); - def->selector=i; - return true; - case 3: - //field - i=tok.toInt(); - def->field=i; - return true; - case 4: - //instance mode - i=tok.toInt(); - if (i < GwXDRMappingDef::IS_SINGLE || - i>= GwXDRMappingDef::IS_LAST) return false; - def->instanceMode=(GwXDRMappingDef::InstanceMode)i; - return true; - case 5: - //instance id - i=tok.toInt(); - def->instanceId=i; - return true; - default: - break; + case 0: + //category + i = tok.toInt(); + if (i < XDRTEMP || i > XDRENGINE) + return false; + def->category = (GwXDRCategory)i; + return true; + case 1: + //direction + i = tok.toInt(); + if (i < GwXDRMappingDef::M_DISABLED || + i >= GwXDRMappingDef::M_LAST) + return false; + def->direction = (GwXDRMappingDef::Direction)i; + return true; + case 2: + //selector + //TODO: check selector? + i = tok.toInt(); + def->selector = i; + return true; + case 3: + //field + i = tok.toInt(); + def->field = i; + return true; + case 4: + //instance mode + i = tok.toInt(); + if (i < GwXDRMappingDef::IS_SINGLE || + i >= GwXDRMappingDef::IS_LAST) + return false; + def->instanceMode = (GwXDRMappingDef::InstanceMode)i; + return true; + case 5: + //instance id + i = tok.toInt(); + def->instanceId = i; + return true; + default: + break; } return false; } -GwXDRMappingDef *GwXDRMappingDef::fromString(String s){ - int found=0; - int last=0; - int index=0; - GwXDRMappingDef *rt=new GwXDRMappingDef(); - while ((found = s.indexOf(',',last)) >= 0){ - String tok=s.substring(last,found); - if (!handleToken(tok,index,rt)){ +GwXDRMappingDef *GwXDRMappingDef::fromString(String s) +{ + int found = 0; + int last = 0; + int index = 0; + GwXDRMappingDef *rt = new GwXDRMappingDef(); + while ((found = s.indexOf(',', last)) >= 0) + { + String tok = s.substring(last, found); + if (!handleToken(tok, index, rt)) + { delete rt; return NULL; } - last=found+1; + last = found + 1; index++; } - if (last < s.length()){ - String tok=s.substring(last); - if (!handleToken(tok,index,rt)){ + if (last < s.length()) + { + String tok = s.substring(last); + if (!handleToken(tok, index, rt)) + { delete rt; return NULL; } @@ -109,16 +192,73 @@ GwXDRMappings::GwXDRMappings(GwLog *logger, GwConfigHandler *config) } #define MAX_MAPPINGS 100 -void GwXDRMappings::begin(){ +void GwXDRMappings::begin() +{ char namebuf[10]; - for (int i=0;igetConfigItem(String(namebuf)); - if (cfg){ - GwXDRMappingDef *mapping=GwXDRMappingDef::fromString(cfg->asCString()); - if (mapping){ - LOG_DEBUG(GwLog::DEBUG,"read xdr mapping %s",mapping->toString().c_str()); + /* + build our mappings + for each defined mapping we fetch the type code and type definition + and create a mapping entries in our 2 maps: + n2kmap: map category,selector and field index to a mapping + n183map: map transducer name, transducer type and transducer unit to a mapping + a #nnn will be stripped from the transducer name + entries in the map are lists of mappings as potentially + we have different mappings for different instances + */ + for (int i = 0; i < MAX_MAPPINGS; i++) + { + snprintf(namebuf, 9, "XDR%d", i); + namebuf[9] = 0; + GwConfigInterface *cfg = config->getConfigItem(String(namebuf)); + if (cfg) + { + GwXDRMappingDef *def = GwXDRMappingDef::fromString(cfg->asCString()); + if (def) + { + int typeIndex = 0; + LOG_DEBUG(GwLog::DEBUG, "read xdr mapping %s", def->toString().c_str()); + //n2k: find first matching type mapping + GwXDRType::TypeCode code = findTypeMapping(def->category, def->field); + if (code == GwXDRType::UNKNOWN) + { + LOG_DEBUG(GwLog::DEBUG, "no type mapping for %s", def->toString().c_str()); + continue; + } + GwXDRType *type = findType(code, &typeIndex); + if (!type) + { + LOG_DEBUG(GwLog::DEBUG, "no type definition for %s", def->toString().c_str()); + continue; + } + long n2kkey=def->n2kKey(); + auto it=n2kMap.find(n2kkey); + if (it == n2kMap.end()){ + LOG_DEBUG(GwLog::DEBUG,"insert mapping with key %ld",n2kkey); + GwXDRMapping *mapping = new GwXDRMapping(def, type); + n2kMap[n2kkey]=mapping; + } + else{ + LOG_DEBUG(GwLog::DEBUG,"append mapping with key %ld",n2kkey); + it->second->addMappingDef(def); + } + //for nmea0183 there could be multiple entries + //as potentially there are different units that we can handle + //so after we inserted the definition we do additional type lookups + while (type != NULL){ + String n183key=GwXDRMappingDef::n183key(def->xdrName, + type->xdrtype,type->xdrunit); + auto it=n183Map.find(n183key); + if (it == n183Map.end()){ + LOG_DEBUG(GwLog::DEBUG,"insert mapping with n183key %s",n183key.c_str()); + n183Map[n183key]=new GwXDRMapping(def,type); + } + else{ + LOG_DEBUG(GwLog::DEBUG,"append mapping with n183key %s",n183key.c_str()); + it->second->addMappingDef(def); + } + type=findType(code,&typeIndex); + if (! type) break; + } } } } diff --git a/lib/xdrmappings/GwXDRMappings.h b/lib/xdrmappings/GwXDRMappings.h index f9629ff..85bf00c 100644 --- a/lib/xdrmappings/GwXDRMappings.h +++ b/lib/xdrmappings/GwXDRMappings.h @@ -4,6 +4,7 @@ #include "GWConfig.h" #include #include +#include //enum must match the defines in xdrconfig.json typedef enum { XDRTEMP=0, @@ -24,7 +25,13 @@ class GwXDRType{ PRESS=0, PERCENT=1, VOLT=2, - AMP=3 + AMP=3, + TEMP=4, + HUMID=5, + VOLPERCENT=6, + VOLUME=7, + FLOW=8, + UNKNOWN=99 }TypeCode; typedef double (* convert)(double); TypeCode code; @@ -45,11 +52,11 @@ class GwXDRTypeMapping{ GwXDRCategory category; int fieldIndex; GwXDRType::TypeCode type; - GwXDRTypeMapping(GwXDRCategory category, + GwXDRTypeMapping(int category, int fieldIndex, - GwXDRType::TypeCode type){ - this->category=category; - this->type=type; + int type){ + this->category=(GwXDRCategory) category; + this->type=(GwXDRType::TypeCode)type; this->fieldIndex=fieldIndex; } }; @@ -91,44 +98,63 @@ class GwXDRMappingDef{ category=XDRTEMP; } String toString(); - static GwXDRMappingDef *fromString(String s); + static GwXDRMappingDef *fromString(String s); + //we allow 100 entities of code,selector and field nid + static long n2kKey(GwXDRCategory category, int selector, int field) + { + long rt = (int)category; + if (selector < 0) + selector = 0; + rt = rt * 100 + selector; + if (field < 0) + field = 0; + rt = rt * 100 * field; + return rt; + } + long n2kKey(){ + return n2kKey(category,selector,field); + } + static String n183key(String xdrName, String xdrType, String xdrUnit) + { + String rt = xdrName; + rt += ","; + rt += xdrType; + rt += ","; + rt += xdrUnit; + return rt; + } + typedef std::vector MappingList; private: static bool handleToken(String tok,int index,GwXDRMappingDef *def); }; class GwXDRMapping{ public: - GwXDRMappingDef *definition; + GwXDRMappingDef::MappingList definitions; GwXDRType *type; GwXDRMapping(GwXDRMappingDef *definition,GwXDRType *type){ - this->definition=definition; + this->definitions.push_back(definition); this->type=type; } - //we allow 100 entities of code,selector and field nid - static long n2kKey(GwXDRType::TypeCode code,int selector,int field){ - long rt=(int)code; - if (selector < 0) selector=0; - rt=rt*100+selector; - if (field < 0) field=0; - rt=rt*100*field; - return rt; - } - static String n183key(String xdrName,String xdrType,String xdrUnit){ - String rt=xdrName; - rt+=","; - rt+=xdrType; - rt+=","; - rt+=xdrUnit; - return rt; + void addMappingDef(GwXDRMappingDef *definition){ + this->definitions.push_back(definition); } + typedef std::map N138Map; + typedef std::map N2KMap; }; class GwXDRMappings{ private: GwLog *logger; GwConfigHandler *config; + GwXDRMapping::N138Map n183Map; + GwXDRMapping::N2KMap n2kMap; public: GwXDRMappings(GwLog *logger,GwConfigHandler *config); void begin(); + //get the mappings + //the returned mapping will exactly contain one mapping def + GwXDRMapping getMapping(String xName,String xType,String xUnit); + GwXDRMapping getMapping(GwXDRCategory category,int selector,int field=0,int instance=0); }; diff --git a/web/xdrconfig.json b/web/xdrconfig.json index 643652e..e3a54c3 100644 --- a/web/xdrconfig.json +++ b/web/xdrconfig.json @@ -1,6 +1,7 @@ { "Temperature": { "id":0, + "type":4, "selector": [ { "l": "SeaTemperature(0)", @@ -66,6 +67,7 @@ }, "Humidity": { "id":1, + "type":5, "selector": [ { "l": "InsideHumidity(0)", @@ -83,6 +85,7 @@ }, "Pressure": { "id":2, + "type":0, "selector": [ { "l": "Atmospheric(0)", @@ -194,8 +197,8 @@ } ], "fields":[ - {"l":"Level","v":0,"t":-1}, - {"l":"Capacity","v":1,"t":-1} + {"l":"Level","v":0,"t":6}, + {"l":"Capacity","v":1,"t":7} ] }, "DCType": { @@ -290,24 +293,24 @@ "d":"127508", "id":9, "fields":[ - "BatteryVoltage", - "BatteryCurrent", - "BatteryTemperature" + {"l":"BatteryVoltage","t":2}, + {"l":"BatteryCurrent","t":3}, + {"l":"BatteryTemperature","t":4} ] }, "Engine":{ "d":"127489", "id":10, "fields":[ - "EngineOilPress", - "EngineOilTemp", - "EngineCoolantTemp", - "FuelRate", - "EngineHours", - "EngineCoolantPress", - "EngineFuelPress", - "EngineLoad", - "EngineTorque" + {"l":"EngineOilPress","t":0}, + {"l":"EngineOilTemp","t":4}, + {"l":"EngineCoolantTemp","t":4}, + {"l":"FuelRate","t":8}, + {"l":"EngineHours","t":99}, + {"l":"EngineCoolantPress","t":0}, + {"l":"EngineFuelPress","t":0}, + {"l":"EngineLoad","t":99}, + {"l":"EngineTorque","t":99} ] } -} \ No newline at end of file +}