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")
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)])

View File

@ -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;i<MAX_MAPPINGS;i++){
snprintf(namebuf,9,"XDR%d",i);
namebuf[9]=0;
GwConfigInterface *cfg=config->getConfigItem(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;
}
}
}
}

View File

@ -4,6 +4,7 @@
#include "GWConfig.h"
#include <WString.h>
#include <vector>
#include <map>
//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<GwXDRMappingDef*> 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<String,GwXDRMapping*> N138Map;
typedef std::map<long,GwXDRMapping*> 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);
};

View File

@ -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}
]
}
}
}