intermediate: xdr mappings

This commit is contained in:
wellenvogel 2021-11-20 11:20:49 +01:00
parent fbc955cd53
commit d927861cdf
4 changed files with 361 additions and 129 deletions

View File

@ -9,8 +9,11 @@ from datetime import datetime
Import("env") Import("env")
GEN_DIR='generated' GEN_DIR='generated'
CFG_FILE='web/config.json' 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' CFG_INCLUDE='GwConfigDefinitions.h'
XDR_INCLUDE='GwXdrTypeMappings.h'
def basePath(): def basePath():
#see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined #see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined
@ -90,11 +93,71 @@ def generateCfg():
os.unlink(outfile) os.unlink(outfile)
raise 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(): if not checkDir():
sys.exit(1) sys.exit(1)
for f in FILES: for f in FILES:
print("compressing %s"%f) print("compressing %s"%f)
compressFile(f) compressFile(f)
generateCfg() generateCfg()
generateXdrMappings()
version="dev"+datetime.now().strftime("%Y%m%d") version="dev"+datetime.now().strftime("%Y%m%d")
env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) env.Append(CPPDEFINES=[('GWDEVVERSION',version)])

View File

@ -1,100 +1,183 @@
#include "GwXDRMappings.h" #include "GwXDRMappings.h"
#include "N2kMessages.h" #include "N2kMessages.h"
double PtoBar(double v){ double PtoBar(double v)
if (N2kIsNA(v)) return v; {
return v/100000L; if (N2kIsNA(v))
return v;
return v / 100000L;
} }
double BarToP(double v){ double BarToP(double v)
if (N2kIsNA(v)) return v; {
return v*100000L; if (N2kIsNA(v))
return v;
return v * 100000L;
} }
GwXDRType * types[]={ double ltrTom3(double v)
new GwXDRType(GwXDRType::PRESS,"P","P"), {
new GwXDRType(GwXDRType::PRESS,"P","B", 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, PtoBar,
BarToP), BarToP),
new GwXDRType(GwXDRType::VOLT,"U","V"), new GwXDRType(GwXDRType::VOLT, "U", "V"),
new GwXDRType(GwXDRType::AMP,"I","A"), new GwXDRType(GwXDRType::AMP, "I", "A"),
NULL 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(){ #include "GwXdrTypeMappings.h"
String rt="";
rt+=String((int)category); static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field)
rt+=","; {
rt+=String((int)direction); for (int i = 0; typeMappings[i] != NULL; i++)
rt+=","; {
rt+=String(selector); if (typeMappings[i]->fieldIndex == field &&
rt+=","; typeMappings[i]->category == category)
rt+=String(field); {
rt+=","; return typeMappings[i]->type;
rt+=String(instanceMode); }
rt+=","; }
rt+=String(instanceId); 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; return rt;
} }
bool GwXDRMappingDef::handleToken(String tok,int index,GwXDRMappingDef *def){ bool GwXDRMappingDef::handleToken(String tok, int index, GwXDRMappingDef *def)
switch(index){ {
switch (index)
{
int i; int i;
case 0: case 0:
//category //category
i=tok.toInt(); i = tok.toInt();
if (i< XDRTEMP || i > XDRENGINE) return false; if (i < XDRTEMP || i > XDRENGINE)
def->category=(GwXDRCategory)i; return false;
def->category = (GwXDRCategory)i;
return true; return true;
case 1: case 1:
//direction //direction
i=tok.toInt(); i = tok.toInt();
if (i < GwXDRMappingDef::M_DISABLED || if (i < GwXDRMappingDef::M_DISABLED ||
i>= GwXDRMappingDef::M_LAST) return false; i >= GwXDRMappingDef::M_LAST)
def->direction=(GwXDRMappingDef::Direction)i; return false;
def->direction = (GwXDRMappingDef::Direction)i;
return true; return true;
case 2: case 2:
//selector //selector
//TODO: check selector? //TODO: check selector?
i=tok.toInt(); i = tok.toInt();
def->selector=i; def->selector = i;
return true; return true;
case 3: case 3:
//field //field
i=tok.toInt(); i = tok.toInt();
def->field=i; def->field = i;
return true; return true;
case 4: case 4:
//instance mode //instance mode
i=tok.toInt(); i = tok.toInt();
if (i < GwXDRMappingDef::IS_SINGLE || if (i < GwXDRMappingDef::IS_SINGLE ||
i>= GwXDRMappingDef::IS_LAST) return false; i >= GwXDRMappingDef::IS_LAST)
def->instanceMode=(GwXDRMappingDef::InstanceMode)i; return false;
def->instanceMode = (GwXDRMappingDef::InstanceMode)i;
return true; return true;
case 5: case 5:
//instance id //instance id
i=tok.toInt(); i = tok.toInt();
def->instanceId=i; def->instanceId = i;
return true; return true;
default: default:
break; break;
} }
return false; return false;
} }
GwXDRMappingDef *GwXDRMappingDef::fromString(String s){ GwXDRMappingDef *GwXDRMappingDef::fromString(String s)
int found=0; {
int last=0; int found = 0;
int index=0; int last = 0;
GwXDRMappingDef *rt=new GwXDRMappingDef(); int index = 0;
while ((found = s.indexOf(',',last)) >= 0){ GwXDRMappingDef *rt = new GwXDRMappingDef();
String tok=s.substring(last,found); while ((found = s.indexOf(',', last)) >= 0)
if (!handleToken(tok,index,rt)){ {
String tok = s.substring(last, found);
if (!handleToken(tok, index, rt))
{
delete rt; delete rt;
return NULL; return NULL;
} }
last=found+1; last = found + 1;
index++; index++;
} }
if (last < s.length()){ if (last < s.length())
String tok=s.substring(last); {
if (!handleToken(tok,index,rt)){ String tok = s.substring(last);
if (!handleToken(tok, index, rt))
{
delete rt; delete rt;
return NULL; return NULL;
} }
@ -109,16 +192,73 @@ GwXDRMappings::GwXDRMappings(GwLog *logger, GwConfigHandler *config)
} }
#define MAX_MAPPINGS 100 #define MAX_MAPPINGS 100
void GwXDRMappings::begin(){ void GwXDRMappings::begin()
{
char namebuf[10]; char namebuf[10];
for (int i=0;i<MAX_MAPPINGS;i++){ /*
snprintf(namebuf,9,"XDR%d",i); build our mappings
namebuf[9]=0; for each defined mapping we fetch the type code and type definition
GwConfigInterface *cfg=config->getConfigItem(String(namebuf)); and create a mapping entries in our 2 maps:
if (cfg){ n2kmap: map category,selector and field index to a mapping
GwXDRMappingDef *mapping=GwXDRMappingDef::fromString(cfg->asCString()); n183map: map transducer name, transducer type and transducer unit to a mapping
if (mapping){ a #nnn will be stripped from the transducer name
LOG_DEBUG(GwLog::DEBUG,"read xdr mapping %s",mapping->toString().c_str()); 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;
}
} }
} }
} }

View File

@ -4,6 +4,7 @@
#include "GWConfig.h" #include "GWConfig.h"
#include <WString.h> #include <WString.h>
#include <vector> #include <vector>
#include <map>
//enum must match the defines in xdrconfig.json //enum must match the defines in xdrconfig.json
typedef enum { typedef enum {
XDRTEMP=0, XDRTEMP=0,
@ -24,7 +25,13 @@ class GwXDRType{
PRESS=0, PRESS=0,
PERCENT=1, PERCENT=1,
VOLT=2, VOLT=2,
AMP=3 AMP=3,
TEMP=4,
HUMID=5,
VOLPERCENT=6,
VOLUME=7,
FLOW=8,
UNKNOWN=99
}TypeCode; }TypeCode;
typedef double (* convert)(double); typedef double (* convert)(double);
TypeCode code; TypeCode code;
@ -45,11 +52,11 @@ class GwXDRTypeMapping{
GwXDRCategory category; GwXDRCategory category;
int fieldIndex; int fieldIndex;
GwXDRType::TypeCode type; GwXDRType::TypeCode type;
GwXDRTypeMapping(GwXDRCategory category, GwXDRTypeMapping(int category,
int fieldIndex, int fieldIndex,
GwXDRType::TypeCode type){ int type){
this->category=category; this->category=(GwXDRCategory) category;
this->type=type; this->type=(GwXDRType::TypeCode)type;
this->fieldIndex=fieldIndex; this->fieldIndex=fieldIndex;
} }
}; };
@ -92,43 +99,62 @@ class GwXDRMappingDef{
} }
String toString(); 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<GwXDRMappingDef*> MappingList;
private: private:
static bool handleToken(String tok,int index,GwXDRMappingDef *def); static bool handleToken(String tok,int index,GwXDRMappingDef *def);
}; };
class GwXDRMapping{ class GwXDRMapping{
public: public:
GwXDRMappingDef *definition; GwXDRMappingDef::MappingList definitions;
GwXDRType *type; GwXDRType *type;
GwXDRMapping(GwXDRMappingDef *definition,GwXDRType *type){ GwXDRMapping(GwXDRMappingDef *definition,GwXDRType *type){
this->definition=definition; this->definitions.push_back(definition);
this->type=type; this->type=type;
} }
//we allow 100 entities of code,selector and field nid void addMappingDef(GwXDRMappingDef *definition){
static long n2kKey(GwXDRType::TypeCode code,int selector,int field){ this->definitions.push_back(definition);
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;
} }
typedef std::map<String,GwXDRMapping*> N138Map;
typedef std::map<long,GwXDRMapping*> N2KMap;
}; };
class GwXDRMappings{ class GwXDRMappings{
private: private:
GwLog *logger; GwLog *logger;
GwConfigHandler *config; GwConfigHandler *config;
GwXDRMapping::N138Map n183Map;
GwXDRMapping::N2KMap n2kMap;
public: public:
GwXDRMappings(GwLog *logger,GwConfigHandler *config); GwXDRMappings(GwLog *logger,GwConfigHandler *config);
void begin(); 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);
}; };

View File

@ -1,6 +1,7 @@
{ {
"Temperature": { "Temperature": {
"id":0, "id":0,
"type":4,
"selector": [ "selector": [
{ {
"l": "SeaTemperature(0)", "l": "SeaTemperature(0)",
@ -66,6 +67,7 @@
}, },
"Humidity": { "Humidity": {
"id":1, "id":1,
"type":5,
"selector": [ "selector": [
{ {
"l": "InsideHumidity(0)", "l": "InsideHumidity(0)",
@ -83,6 +85,7 @@
}, },
"Pressure": { "Pressure": {
"id":2, "id":2,
"type":0,
"selector": [ "selector": [
{ {
"l": "Atmospheric(0)", "l": "Atmospheric(0)",
@ -194,8 +197,8 @@
} }
], ],
"fields":[ "fields":[
{"l":"Level","v":0,"t":-1}, {"l":"Level","v":0,"t":6},
{"l":"Capacity","v":1,"t":-1} {"l":"Capacity","v":1,"t":7}
] ]
}, },
"DCType": { "DCType": {
@ -290,24 +293,24 @@
"d":"127508", "d":"127508",
"id":9, "id":9,
"fields":[ "fields":[
"BatteryVoltage", {"l":"BatteryVoltage","t":2},
"BatteryCurrent", {"l":"BatteryCurrent","t":3},
"BatteryTemperature" {"l":"BatteryTemperature","t":4}
] ]
}, },
"Engine":{ "Engine":{
"d":"127489", "d":"127489",
"id":10, "id":10,
"fields":[ "fields":[
"EngineOilPress", {"l":"EngineOilPress","t":0},
"EngineOilTemp", {"l":"EngineOilTemp","t":4},
"EngineCoolantTemp", {"l":"EngineCoolantTemp","t":4},
"FuelRate", {"l":"FuelRate","t":8},
"EngineHours", {"l":"EngineHours","t":99},
"EngineCoolantPress", {"l":"EngineCoolantPress","t":0},
"EngineFuelPress", {"l":"EngineFuelPress","t":0},
"EngineLoad", {"l":"EngineLoad","t":99},
"EngineTorque" {"l":"EngineTorque","t":99}
] ]
} }
} }