esp32-nmea2000-obp60/lib/xdrmappings/GwXDRMappings.cpp

341 lines
10 KiB
C++

#include "GwXDRMappings.h"
#include "N2kMessages.h"
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 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;
}
#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)
{
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;
}
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))
{
delete rt;
return NULL;
}
last = found + 1;
index++;
}
if (last < s.length())
{
String tok = s.substring(last);
if (!handleToken(tok, index, rt))
{
delete rt;
return NULL;
}
}
return rt;
}
GwXDRMappings::GwXDRMappings(GwLog *logger, GwConfigHandler *config)
{
this->logger = logger;
this->config = config;
}
#define MAX_MAPPINGS 100
void GwXDRMappings::begin()
{
char namebuf[10];
/*
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);
GwXDRMapping *mapping = new GwXDRMapping(def, type);
if (it == n2kMap.end())
{
LOG_DEBUG(GwLog::DEBUG, "insert mapping with key %ld", n2kkey);
GwXDRMapping::MappingList mappings;
mappings.push_back(mapping);
n2kMap[n2kkey] = mappings;
}
else
{
LOG_DEBUG(GwLog::DEBUG, "append mapping with key %ld", n2kkey);
it->second.push_back(mapping);
}
//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());
GwXDRMapping::MappingList mappings;
mappings.push_back(mapping);
n183Map[n183key] = mappings;
}
else
{
LOG_DEBUG(GwLog::DEBUG, "append mapping with n183key %s", n183key.c_str());
it->second.push_back(mapping);
}
type = findType(code, &typeIndex);
if (!type)
break;
mapping=new GwXDRMapping(def,type);
}
}
}
}
}
/**
* select the best matching mapping
* depending on the instance id
*/
GwXDRFoundMapping GwXDRMappings::selectMapping(GwXDRMapping::MappingList *list,int instance){
GwXDRMapping *candidate=NULL;
for (auto mit=list->begin();mit != list->end();mit++){
GwXDRMappingDef *def=(*mit)->definition;
//if there is no instance we take a mapping with instance type ignore
//otherwise we prefer a matching instance before we use auto/ignore
if (instance < 0){
if (def->instanceMode != GwXDRMappingDef::IS_IGNORE) continue;
LOG_DEBUG(GwLog::DEBUG,"found mapping %s",def->toString().c_str());
return GwXDRFoundMapping(*mit);
}
else{
switch(def->instanceMode){
case GwXDRMappingDef::IS_SINGLE:
if (def->instanceId == instance){
LOG_DEBUG(GwLog::DEBUG,"found mapping %s",def->toString().c_str());
return GwXDRFoundMapping(*mit,instance);
}
case GwXDRMappingDef::IS_AUTO:
candidate=*mit;
break;
default:
break;
}
}
}
if (candidate != NULL){
LOG_DEBUG(GwLog::DEBUG,"found mapping %s",candidate->definition->toString().c_str());
return GwXDRFoundMapping(candidate,instance);
}
return GwXDRFoundMapping();
}
GwXDRFoundMapping GwXDRMappings::getMapping(String xName,String xType,String xUnit){
//get an instance suffix from the name and separate it
int sepIdx=xName.indexOf('#');
int instance=-1;
if (sepIdx>=0){
String istr=xName.substring(sepIdx+1);
xName=xName.substring(0,sepIdx);
instance=istr.toInt();
}
if (xName == "") return GwXDRFoundMapping();
String n183Key=GwXDRMappingDef::n183key(xName,xType,xUnit);
auto it=n183Map.find(n183Key);
if (it == n183Map.end()) {
LOG_DEBUG(GwLog::DEBUG,"find n183mapping for %s,i=%d - nothing found",n183Key.c_str(),instance);
return GwXDRFoundMapping();
}
return selectMapping(&(it->second),instance);
}
GwXDRFoundMapping GwXDRMappings::getMapping(GwXDRCategory category,int selector,int field,int instance){
long n2kKey=GwXDRMappingDef::n2kKey(category,selector,field);
auto it=n2kMap.find(n2kKey);
if (it == n2kMap.end()){
LOG_DEBUG(GwLog::DEBUG,"find n2kmapping for c=%d,s=%d,f=%d,i=%d - nothing found",
(int)category,selector,field,instance);
return GwXDRFoundMapping();
}
return selectMapping(&(it->second),instance);
}