xdrmappings: map 130314

This commit is contained in:
wellenvogel 2021-11-20 17:53:02 +01:00
parent ad29c93dd2
commit 15ca1b9e17
8 changed files with 151 additions and 25 deletions

View File

@ -118,7 +118,7 @@ def generateXdrMappings():
first=False first=False
else: else:
oh.write(",\n") oh.write(",\n")
oh.write(" new GwXDRTypeMapping(%d,%d,0)"%(cid,tc)) oh.write(" new GwXDRTypeMapping(%d,%d,0) /*%s*/"%(cid,tc,cat))
fields=item.get('fields') fields=item.get('fields')
if fields is None: if fields is None:
continue continue
@ -138,7 +138,7 @@ def generateXdrMappings():
first=False first=False
else: else:
oh.write(",\n") oh.write(",\n")
oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s*/"%(cid,tc,id,l)) oh.write(" new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,tc,cat,id,l))
oh.write("\n") oh.write("\n")
oh.write("};\n") oh.write("};\n")
except Exception as e: except Exception as e:

View File

@ -22,7 +22,11 @@ void GwLog::logString(const char *fmt,...){
va_start(args,fmt); va_start(args,fmt);
xSemaphoreTake(locker, portMAX_DELAY); xSemaphoreTake(locker, portMAX_DELAY);
vsnprintf(buffer,99,fmt,args); vsnprintf(buffer,99,fmt,args);
if (! writer) return; buffer[99]=0;
if (! writer) {
xSemaphoreGive(locker);
return;
}
writer->write(prefix.c_str()); writer->write(prefix.c_str());
writer->write(buffer); writer->write(buffer);
writer->write("\n"); writer->write("\n");

View File

@ -64,8 +64,52 @@ class N2kToNMEA0183Functions : public N2kDataToNMEA0183
{ {
private: private:
GwXDRMappings *xdrMappings;
ConverterList<N2kToNMEA0183Functions,tN2kMsg> converters; ConverterList<N2kToNMEA0183Functions,tN2kMsg> converters;
static const unsigned long RMCPeriod = 500; static const unsigned long RMCPeriod = 500;
tNMEA0183Msg xdrMessage;
bool xdrOpened=false;
String buildXdrEntry(GwXDRFoundMapping &mapping,double value){
char buffer[40];
String name=mapping.definition->xdrName;
if (mapping.definition->instanceMode == GwXDRMappingDef::IS_AUTO ||
mapping.definition->instanceMode == GwXDRMappingDef::IS_SINGLE
){
name+="#";
name+=String(mapping.instanceId);
}
if (mapping.type->tonmea){
value=(* (mapping.type->tonmea))(value);
}
snprintf(buffer,39,"%s,%.3f,%s,%s",
mapping.type->xdrtype.c_str(),
value,
mapping.type->xdrunit.c_str(),
name);
buffer[39]=0;
return String(buffer);
}
bool addToXdr(String entry){
if (! xdrOpened){
xdrMessage.Init("XDR",talkerId);
xdrOpened=true;
}
int len=entry.length();
if (! xdrMessage.AddStrField(entry.c_str())){
SendMessage(xdrMessage);
xdrMessage.Init("XDR",talkerId);
xdrMessage.AddStrField(entry.c_str());
}
return true;
}
bool finalizeXdr(){
if (! xdrOpened) return false;
SendMessage(xdrMessage);
xdrOpened=false;
return true;
}
void setMax(GwBoatItem<double> *maxItem, GwBoatItem<double> *item) void setMax(GwBoatItem<double> *maxItem, GwBoatItem<double> *item)
{ {
unsigned long now = millis(); unsigned long now = millis();
@ -1152,6 +1196,23 @@ private:
SendMessage(nmeaMsg); SendMessage(nmeaMsg);
} }
void Handle130314(const tN2kMsg &msg){
unsigned char SID=-1;
unsigned char PressureInstance=0;
tN2kPressureSource PressureSource;
double ActualPressure;
if (! ParseN2kPGN130314(msg,SID, PressureInstance,
PressureSource, ActualPressure)){
LOG_DEBUG(GwLog::DEBUG,"unable to parse PGN %d",msg.PGN);
return;
}
GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRPRESSURE,(int)PressureSource,0,PressureInstance);
if (mapping.empty) return;
LOG_DEBUG(GwLog::DEBUG,"found pressure mapping %s",mapping.definition->toString().c_str());
addToXdr(buildXdrEntry(mapping,ActualPressure));
finalizeXdr();
}
void registerConverters() void registerConverters()
{ {
@ -1178,6 +1239,7 @@ private:
converters.registerConverter(127251UL, &N2kToNMEA0183Functions::HandleROT); converters.registerConverter(127251UL, &N2kToNMEA0183Functions::HandleROT);
converters.registerConverter(129283UL, &N2kToNMEA0183Functions::HandleXTE); converters.registerConverter(129283UL, &N2kToNMEA0183Functions::HandleXTE);
converters.registerConverter(129284UL, &N2kToNMEA0183Functions::HandleNavigation); converters.registerConverter(129284UL, &N2kToNMEA0183Functions::HandleNavigation);
converters.registerConverter(130314UL, &N2kToNMEA0183Functions::Handle130314);
#define HANDLE_AIS #define HANDLE_AIS
#ifdef HANDLE_AIS #ifdef HANDLE_AIS
converters.registerConverter(129038UL, &N2kToNMEA0183Functions::HandleAISClassAPosReport); // AIS Class A Position Report, Message Type 1 converters.registerConverter(129038UL, &N2kToNMEA0183Functions::HandleAISClassAPosReport); // AIS Class A Position Report, Message Type 1
@ -1191,7 +1253,7 @@ private:
public: public:
N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData, N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData,
tNMEA2000 *NMEA2000, tSendNMEA0183MessageCallback callback, int sourceId, tNMEA2000 *NMEA2000, tSendNMEA0183MessageCallback callback, int sourceId,
String talkerId) String talkerId, GwXDRMappings *xdrMappings)
: N2kDataToNMEA0183(logger, boatData, NMEA2000, callback,sourceId,talkerId) : N2kDataToNMEA0183(logger, boatData, NMEA2000, callback,sourceId,talkerId)
{ {
LastPosSend = 0; LastPosSend = 0;
@ -1200,6 +1262,7 @@ private:
this->logger = logger; this->logger = logger;
this->boatData = boatData; this->boatData = boatData;
this->xdrMappings=xdrMappings;
registerConverters(); registerConverters();
} }
virtual void loop() virtual void loop()
@ -1215,8 +1278,8 @@ private:
N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000,
tSendNMEA0183MessageCallback callback, int sourceId,String talkerId){ tSendNMEA0183MessageCallback callback, int sourceId,String talkerId, GwXDRMappings *xdrMappings){
LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183"); LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183");
return new N2kToNMEA0183Functions(logger,boatData,NMEA2000,callback, sourceId,talkerId); return new N2kToNMEA0183Functions(logger,boatData,NMEA2000,callback, sourceId,talkerId,xdrMappings);
} }
//***************************************************************************** //*****************************************************************************

View File

@ -28,6 +28,7 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <GwLog.h> #include <GwLog.h>
#include <GwBoatData.h> #include <GwBoatData.h>
#include <GwXDRMappings.h>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class N2kDataToNMEA0183 : public tNMEA2000::tMsgHandler class N2kDataToNMEA0183 : public tNMEA2000::tMsgHandler
@ -42,10 +43,12 @@ protected:
char talkerId[3]; char talkerId[3];
tSendNMEA0183MessageCallback SendNMEA0183MessageCallback; tSendNMEA0183MessageCallback SendNMEA0183MessageCallback;
void SendMessage(const tNMEA0183Msg &NMEA0183Msg); void SendMessage(const tNMEA0183Msg &NMEA0183Msg);
N2kDataToNMEA0183(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tSendNMEA0183MessageCallback callback, int sourceId,String talkerId); N2kDataToNMEA0183(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000,
tSendNMEA0183MessageCallback callback, int sourceId,String talkerId);
public: public:
static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tSendNMEA0183MessageCallback callback, int sourceId,String talkerId); static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tSendNMEA0183MessageCallback callback,
int sourceId,String talkerId, GwXDRMappings *xdrMappings);
virtual void HandleMsg(const tN2kMsg &N2kMsg) = 0; virtual void HandleMsg(const tN2kMsg &N2kMsg) = 0;
virtual void loop(); virtual void loop();
virtual ~N2kDataToNMEA0183(){} virtual ~N2kDataToNMEA0183(){}

View File

@ -90,7 +90,7 @@ static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field)
} }
return GwXDRType::UNKNOWN; return GwXDRType::UNKNOWN;
} }
//category,direction,selector,field,instanceMode,instance,name
String GwXDRMappingDef::toString() String GwXDRMappingDef::toString()
{ {
String rt = ""; String rt = "";
@ -105,6 +105,8 @@ String GwXDRMappingDef::toString()
rt += String(instanceMode); rt += String(instanceMode);
rt += ","; rt += ",";
rt += String(instanceId); rt += String(instanceId);
rt += ",";
rt += xdrName;
return rt; return rt;
} }
bool GwXDRMappingDef::handleToken(String tok, int index, GwXDRMappingDef *def) bool GwXDRMappingDef::handleToken(String tok, int index, GwXDRMappingDef *def)
@ -151,10 +153,13 @@ bool GwXDRMappingDef::handleToken(String tok, int index, GwXDRMappingDef *def)
i = tok.toInt(); i = tok.toInt();
def->instanceId = i; def->instanceId = i;
return true; return true;
case 6:
def->xdrName=tok;
return true;
default: default:
break; break;
} }
return false; return true; //ignore unknown trailing stuff
} }
GwXDRMappingDef *GwXDRMappingDef::fromString(String s) GwXDRMappingDef *GwXDRMappingDef::fromString(String s)
{ {
@ -216,7 +221,8 @@ void GwXDRMappings::begin()
if (def) if (def)
{ {
int typeIndex = 0; int typeIndex = 0;
LOG_DEBUG(GwLog::DEBUG, "read xdr mapping %s", def->toString().c_str()); LOG_DEBUG(GwLog::DEBUG, "read xdr mapping %s from %s",
def->toString().c_str(),namebuf);
//n2k: find first matching type mapping //n2k: find first matching type mapping
GwXDRType::TypeCode code = findTypeMapping(def->category, def->field); GwXDRType::TypeCode code = findTypeMapping(def->category, def->field);
if (code == GwXDRType::UNKNOWN) if (code == GwXDRType::UNKNOWN)
@ -271,6 +277,9 @@ void GwXDRMappings::begin()
mapping=new GwXDRMapping(def,type); mapping=new GwXDRMapping(def,type);
} }
} }
else{
LOG_DEBUG(GwLog::DEBUG,"unable to parse mapping %s",cfg->asCString());
}
} }
} }
} }
@ -279,7 +288,7 @@ void GwXDRMappings::begin()
* select the best matching mapping * select the best matching mapping
* depending on the instance id * depending on the instance id
*/ */
GwXDRFoundMapping GwXDRMappings::selectMapping(GwXDRMapping::MappingList *list,int instance){ GwXDRFoundMapping GwXDRMappings::selectMapping(GwXDRMapping::MappingList *list,int instance,unsigned long key){
GwXDRMapping *candidate=NULL; GwXDRMapping *candidate=NULL;
for (auto mit=list->begin();mit != list->end();mit++){ for (auto mit=list->begin();mit != list->end();mit++){
GwXDRMappingDef *def=(*mit)->definition; GwXDRMappingDef *def=(*mit)->definition;
@ -300,6 +309,10 @@ GwXDRFoundMapping GwXDRMappings::selectMapping(GwXDRMapping::MappingList *list,i
case GwXDRMappingDef::IS_AUTO: case GwXDRMappingDef::IS_AUTO:
candidate=*mit; candidate=*mit;
break; break;
case GwXDRMappingDef::IS_IGNORE:
if (candidate == NULL) candidate=*mit;
break;
default: default:
break; break;
} }
@ -309,6 +322,7 @@ GwXDRFoundMapping GwXDRMappings::selectMapping(GwXDRMapping::MappingList *list,i
LOG_DEBUG(GwLog::DEBUG,"found mapping %s",candidate->definition->toString().c_str()); LOG_DEBUG(GwLog::DEBUG,"found mapping %s",candidate->definition->toString().c_str());
return GwXDRFoundMapping(candidate,instance); return GwXDRFoundMapping(candidate,instance);
} }
LOG_DEBUG(GwLog::DEBUG,"no instance mapping found for key=%lu, i=%d",key,instance);
return GwXDRFoundMapping(); return GwXDRFoundMapping();
} }
GwXDRFoundMapping GwXDRMappings::getMapping(String xName,String xType,String xUnit){ GwXDRFoundMapping GwXDRMappings::getMapping(String xName,String xType,String xUnit){
@ -330,12 +344,30 @@ GwXDRFoundMapping GwXDRMappings::getMapping(String xName,String xType,String xUn
return selectMapping(&(it->second),instance); return selectMapping(&(it->second),instance);
} }
GwXDRFoundMapping GwXDRMappings::getMapping(GwXDRCategory category,int selector,int field,int instance){ GwXDRFoundMapping GwXDRMappings::getMapping(GwXDRCategory category,int selector,int field,int instance){
long n2kKey=GwXDRMappingDef::n2kKey(category,selector,field); unsigned long n2kKey=GwXDRMappingDef::n2kKey(category,selector,field);
auto it=n2kMap.find(n2kKey); auto it=n2kMap.find(n2kKey);
if (it == n2kMap.end()){ if (it == n2kMap.end()){
LOG_DEBUG(GwLog::DEBUG,"find n2kmapping for c=%d,s=%d,f=%d,i=%d - nothing found", LOG_DEBUG(GwLog::DEBUG,"find n2kmapping for c=%d,s=%d,f=%d,i=%d - nothing found",
(int)category,selector,field,instance); (int)category,selector,field,instance);
addUnknown(category,selector,field,instance);
return GwXDRFoundMapping(); return GwXDRFoundMapping();
} }
return selectMapping(&(it->second),instance); GwXDRFoundMapping rt=selectMapping(&(it->second),instance);
if (rt.empty){
addUnknown(category,selector,field,instance);
}
return rt;
}
#define MAX_UNKNOWN 200
bool GwXDRMappings::addUnknown(GwXDRCategory category,int selector,int field,int instance){
if (unknown.size() >= 200) return false;
unsigned long uk=((int)category) &0x7f;
if (selector < 0) selector=0;
uk=(uk<<7) + (selector &0x7f);
if (field < 0) field=0;
uk=(uk<<7) + (field & 0x7f);
if (instance < 0 || instance > 255) instance=256; //unknown
uk=(uk << 9) + (instance & 0x1ff);
unknown.push_back(uk);
return true;
} }

View File

@ -9,7 +9,7 @@
typedef enum { typedef enum {
XDRTEMP=0, XDRTEMP=0,
XDRHUMIDITY=1, XDRHUMIDITY=1,
XDRPRSSURE=2, XDRPRESSURE=2,
XDRTIME=3, //unused XDRTIME=3, //unused
XDRFLUID=4, XDRFLUID=4,
XDRDCTYPE=5, //unused XDRDCTYPE=5, //unused
@ -79,7 +79,7 @@ class GwXDRMappingDef{
String xdrName; String xdrName;
GwXDRCategory category; GwXDRCategory category;
int selector=-1; int selector=-1;
int field=-1; int field=0;
InstanceMode instanceMode=IS_AUTO; InstanceMode instanceMode=IS_AUTO;
int instanceId=-1; int instanceId=-1;
Direction direction=M_BOTH; Direction direction=M_BOTH;
@ -97,21 +97,22 @@ class GwXDRMappingDef{
GwXDRMappingDef(){ GwXDRMappingDef(){
category=XDRTEMP; category=XDRTEMP;
} }
//category,direction,selector,field,instanceMode,instance,name
String toString(); String toString();
static GwXDRMappingDef *fromString(String s); static GwXDRMappingDef *fromString(String s);
//we allow 100 entities of code,selector and field nid //we allow 100 entities of code,selector and field nid
static long n2kKey(GwXDRCategory category, int selector, int field) static unsigned long n2kKey(GwXDRCategory category, int selector, int field)
{ {
long rt = (int)category; long rt = ((int)category)&0xff;
if (selector < 0) if (selector < 0)
selector = 0; selector = 0;
rt = rt * 100 + selector; rt = (rt<<8) + (selector & 0xff);
if (field < 0) if (field < 0)
field = 0; field = 0;
rt = rt * 100 * field; rt = (rt <<8) + (field & 0xff);
return rt; return rt;
} }
long n2kKey(){ unsigned long n2kKey(){
return n2kKey(category,selector,field); return n2kKey(category,selector,field);
} }
static String n183key(String xdrName, String xdrType, String xdrUnit) static String n183key(String xdrName, String xdrType, String xdrUnit)
@ -136,7 +137,7 @@ class GwXDRMapping{
} }
typedef std::vector<GwXDRMapping*> MappingList; typedef std::vector<GwXDRMapping*> MappingList;
typedef std::map<String,MappingList> N138Map; typedef std::map<String,MappingList> N138Map;
typedef std::map<long,MappingList> N2KMap; typedef std::map<unsigned long,MappingList> N2KMap;
}; };
class GwXDRFoundMapping{ class GwXDRFoundMapping{
public: public:
@ -164,7 +165,9 @@ class GwXDRMappings{
GwConfigHandler *config; GwConfigHandler *config;
GwXDRMapping::N138Map n183Map; GwXDRMapping::N138Map n183Map;
GwXDRMapping::N2KMap n2kMap; GwXDRMapping::N2KMap n2kMap;
std::vector<unsigned long> unknown;
GwXDRFoundMapping selectMapping(GwXDRMapping::MappingList *list,int instance); GwXDRFoundMapping selectMapping(GwXDRMapping::MappingList *list,int instance);
bool addUnknown(GwXDRCategory category,int selector,int field=0,int instance=-1);
public: public:
GwXDRMappings(GwLog *logger,GwConfigHandler *config); GwXDRMappings(GwLog *logger,GwConfigHandler *config);
void begin(); void begin();

View File

@ -85,6 +85,7 @@ bool fixedApPass=true;
GwWifi gwWifi(&config,&logger,fixedApPass); GwWifi gwWifi(&config,&logger,fixedApPass);
GwSocketServer socketServer(&config,&logger,MIN_TCP_CHANNEL_ID); GwSocketServer socketServer(&config,&logger,MIN_TCP_CHANNEL_ID);
GwBoatData boatData(&logger); GwBoatData boatData(&logger);
GwXDRMappings xdrMappings(&logger,&config);
int NodeAddress; // To store last Node Address int NodeAddress; // To store last Node Address
@ -249,7 +250,7 @@ bool delayedRestart(){
void startAddOnTask(TaskFunction_t task,int sourceId){ void startAddOnTask(TaskFunction_t task,int sourceId){
ApiImpl* api=new ApiImpl(sourceId); ApiImpl* api=new ApiImpl(sourceId);
xTaskCreate(task,"user",1000,api,3,NULL); xTaskCreate(task,"user",2000,api,3,NULL);
} }
#define JSON_OK "{\"status\":\"OK\"}" #define JSON_OK "{\"status\":\"OK\"}"
@ -525,9 +526,11 @@ void setup() {
{ return new BoatDataRequest(); }); { return new BoatDataRequest(); });
webserver.begin(); webserver.begin();
xdrMappings.begin();
logger.flush();
nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,&NMEA2000, nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,&NMEA2000,
SendNMEA0183Message, N2K_CHANNEL_ID,config.getString(config.talkerId,String("GP"))); SendNMEA0183Message, N2K_CHANNEL_ID,config.getString(config.talkerId,String("GP")),&xdrMappings);
toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg)->bool{ toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg)->bool{
logger.logDebug(GwLog::DEBUG+2,"send N2K %ld",msg.PGN); logger.logDebug(GwLog::DEBUG+2,"send N2K %ld",msg.PGN);
@ -591,10 +594,12 @@ void setup() {
nmea0183Converter->HandleMsg(n2kMsg); nmea0183Converter->HandleMsg(n2kMsg);
}); });
NMEA2000.Open(); NMEA2000.Open();
logger.logDebug(GwLog::LOG,"starting addon tasks");
logger.flush();
startAddOnTask(handleButtons,100); startAddOnTask(handleButtons,100);
setLedMode(LED_GREEN); setLedMode(LED_GREEN);
startAddOnTask(handleLeds,101); startAddOnTask(handleLeds,101);
logger.logDebug(GwLog::LOG,"setup done");
} }
//***************************************************************************** //*****************************************************************************

View File

@ -237,6 +237,22 @@
"check": "checkSSID", "check": "checkSSID",
"description": "the SSID for an external WIFI network", "description": "the SSID for an external WIFI network",
"category":"wifi client" "category":"wifi client"
},
{
"name": "XDR1",
"label": "XDR1",
"type": "xdr",
"default": "",
"check": "checkXDR",
"category":"xdr"
},
{
"name": "XDR2",
"label": "XDR2",
"type": "xdr",
"default": "",
"check": "checkXDR",
"category":"xdr"
} }