diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp index ead8f29..da352c3 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -3,8 +3,10 @@ #include "N2kMessages.h" #include "ConverterList.h" #include +#include #include #include "NMEA0183AIStoNMEA2000.h" +#include "GwXDRMappings.h" static const double mToFathoms=0.546806649; static const double mToFeet=3.2808398950131; @@ -44,6 +46,7 @@ class SNMEA0183Msg : public tNMEA0183Msg{ } }; + class NMEA0183DataToN2KFunctions : public NMEA0183DataToN2K { private: @@ -51,6 +54,7 @@ private: ConverterList converters; std::map lastSends; unsigned long minSendInterval=50; + GwXDRMappings *xdrMappings; class WaypointNumber{ public: unsigned long id; @@ -145,6 +149,161 @@ private: } return N2kxtem_Autonomous; } + class XdrMappingAndValue{ + public: + GwXDRFoundMapping mapping; + double value; + XdrMappingAndValue(GwXDRFoundMapping &mapping,double value){ + this->mapping=mapping; + this->value=value; + } + int field(){return mapping.definition->field;} + }; + typedef std::vector XdrMappingList; + /** + * find a mapping for a different field from the same + * category with similar instanceId and selector + */ + GwXDRFoundMapping getOtherFieldMapping(GwXDRFoundMapping &found, int field){ + if (found.empty) return GwXDRFoundMapping(); + return xdrMappings->getMapping(found.definition->category, + found.definition->selector, + field, + found.instanceId); + } + double getOtherFieldValue(GwXDRFoundMapping &found, int field){ + GwXDRFoundMapping other=getOtherFieldMapping(found,field); + if (other.empty) return N2kDoubleNA; + LOG_DEBUG(GwLog::DEBUG,"found other field mapping %s",other.definition->toString().c_str()); + return boatData->getDataWithDefault(N2kDoubleNA,&other); + } + /** + * fill all the fields we potentially need for the n2k message + * we take the current transducer value from the XdrMappingAndValue and + * try to find a mapping with similar category/instance/selector but different + * field id for the other fields + * if such a mapping exists we try to fetch the entry from boatData + * if we at least inserted one valid value into the field list + * we return true so that we can send out the message + */ + bool fillFieldList(XdrMappingAndValue ¤t,double *list,int numFields,int start=0){ + bool rt=false; + for (int i=start;iselector); + rt+="."; + rt+=String(found.instanceId); + return rt; + } + void convertXDR(const SNMEA0183Msg &msg){ + XdrMappingList foundMappings; + for (int offset=0;offset <= (msg.FieldCount()-4);offset+=4){ + //parse next transducer + String type=msg.Field(offset); + if (msg.FieldLen(offset+1) < 1) continue; //empty value + double value=atof(msg.Field(offset+1)); + String unit=msg.Field(offset+2); + String transducerName=msg.Field(offset+3); + GwXDRFoundMapping found=xdrMappings->getMapping(transducerName,type,unit); + if (found.empty) continue; + value=found.valueFromXdr(value); + if (!boatData->update(value,msg.sourceId,&found)) continue; + LOG_DEBUG(GwLog::DEBUG,"found mapped XDR %s, value %f",transducerName,value); + foundMappings.push_back(XdrMappingAndValue(found,value)); + } + static const int maxFields=20; + double fields[maxFields]; + //currently we will build similar n2k messages if there are + //multiple transducers in one XDR + //but we finally rely on the send interval filter + //to not send them multiple times + //it could be optimized by checking if we already created + //a message with similar key in this round and skipp the fillFieldList + //in this case + for (auto it=foundMappings.begin();it != foundMappings.end();it++){ + XdrMappingAndValue current=*it; + tN2kMsg n2kMsg; + switch (current.mapping.definition->category) + { + + case XDRFLUID: + if (fillFieldList(current, fields, 2)) + { + SetN2kPGN127505(n2kMsg, current.mapping.instanceId, + (tN2kFluidType)(current.mapping.definition->selector), + fields[0], + fields[1]); + send(n2kMsg, buildN2KKey(n2kMsg, current.mapping)); + } + break; + case XDRBAT: + if (fillFieldList(current, fields, 3)) + { + SetN2kPGN127508(n2kMsg, current.mapping.instanceId, + fields[0], fields[1], fields[2]); + send(n2kMsg, buildN2KKey(n2kMsg, current.mapping)); + } + break; + case XDRTEMP: + if (fillFieldList(current,fields,2)){ + SetN2kPGN130312(n2kMsg,1,current.mapping.instanceId, + (tN2kTempSource)(current.mapping.definition->selector), + fields[0],fields[1]); + send(n2kMsg,buildN2KKey(n2kMsg,current.mapping)); + } + break; + case XDRHUMIDITY: + if (fillFieldList(current,fields,2)){ + SetN2kPGN130313(n2kMsg,1,current.mapping.instanceId, + (tN2kHumiditySource)(current.mapping.definition->selector), + fields[0], + fields[1] + ); + send(n2kMsg,buildN2KKey(n2kMsg,current.mapping)); + } + break; + case XDRPRESSURE: + if (fillFieldList(current,fields,1)){ + SetN2kPGN130314(n2kMsg,1,current.mapping.instanceId, + (tN2kPressureSource)(current.mapping.definition->selector), + fields[0]); + send(n2kMsg,buildN2KKey(n2kMsg,current.mapping)); + } + break; + case XDRENGINE: + if (current.field() <= 9) + { + if (fillFieldList(current, fields, 10)) + { + SetN2kPGN127489(n2kMsg, current.mapping.instanceId, + fields[0], fields[1], fields[2], fields[3], fields[4], + fields[5], fields[6], fields[7], (int8_t)fields[8], (int8_t)fields[9], + tN2kEngineDiscreteStatus1(), tN2kEngineDiscreteStatus2()); + send(n2kMsg, buildN2KKey(n2kMsg, current.mapping)); + } + } + else{ + if (fillFieldList(current, fields, 12,13)){ + SetN2kPGN127488(n2kMsg,current.mapping.instanceId, + fields[10],fields[11],fields[12]); + send(n2kMsg, buildN2KKey(n2kMsg, current.mapping)); + } + } + break; + default: + continue; + } + } + } + void convertRMB(const SNMEA0183Msg &msg) { LOG_DEBUG(GwLog::DEBUG + 1, "convert RMB"); @@ -815,6 +974,11 @@ private: converters.registerConverter( 129283UL, String(F("XTE")), &NMEA0183DataToN2KFunctions::convertXTE); + unsigned long *xdrpgns=new unsigned long[7]{127505UL,127508UL,130312UL,130313UL,130314UL,127489UL,127488UL}; + converters.registerConverter( + 7, + xdrpgns, + F("XDR"), &NMEA0183DataToN2KFunctions::convertXDR); unsigned long *aispgns=new unsigned long[7]{129810UL,129809UL,129040UL,129039UL,129802UL,129794UL,129038UL}; converters.registerConverter(7,&aispgns[0], String(F("AIVDM")),&NMEA0183DataToN2KFunctions::convertAIVDX); @@ -858,10 +1022,13 @@ public: return converters.handledKeys(); } - NMEA0183DataToN2KFunctions(GwLog *logger, GwBoatData *boatData, N2kSender callback, unsigned long minSendInterval) + NMEA0183DataToN2KFunctions(GwLog *logger, GwBoatData *boatData, N2kSender callback, + GwXDRMappings *xdrMappings, + unsigned long minSendInterval) : NMEA0183DataToN2K(logger, boatData, callback) { this->minSendInterval=minSendInterval; + this->xdrMappings=xdrMappings; aisDecoder= new MyAisDecoder(logger,this->sender); registerConverters(); LOG_DEBUG(GwLog::LOG, "NMEA0183DataToN2KFunctions: registered %d converters", converters.numConverters()); @@ -869,7 +1036,8 @@ public: }; NMEA0183DataToN2K* NMEA0183DataToN2K::create(GwLog *logger,GwBoatData *boatData,N2kSender callback, + GwXDRMappings *xdrMappings, unsigned long minSendInterval){ - return new NMEA0183DataToN2KFunctions(logger, boatData,callback,minSendInterval); + return new NMEA0183DataToN2KFunctions(logger, boatData,callback,xdrMappings,minSendInterval); } diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.h b/lib/nmea0183ton2k/NMEA0183DataToN2K.h index b897bba..59ace4f 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.h +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.h @@ -3,6 +3,7 @@ #include "GwLog.h" #include "GwBoatData.h" #include "N2kMessages.h" +#include "GwXDRMappings.h" class NMEA0183DataToN2K{ public: @@ -18,6 +19,7 @@ class NMEA0183DataToN2K{ virtual int numConverters()=0; virtual String handledKeys()=0; static NMEA0183DataToN2K* create(GwLog *logger,GwBoatData *boatData,N2kSender callback, + GwXDRMappings *xdrMappings, unsigned long minSendInterval); }; #endif \ No newline at end of file diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp index cc3cee9..815eddd 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp @@ -1188,13 +1188,17 @@ private: double Level=N2kDoubleNA; double Capacity=N2kDoubleNA; if (ParseN2kPGN127505(N2kMsg,Instance,FluidType,Level,Capacity)) { - double flc= Level/Capacity*100; GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRFLUID,FluidType,0,Instance); - if (updateDouble(&mapping,flc)){ + if (updateDouble(&mapping,Level)){ LOG_DEBUG(GwLog::DEBUG+1,"found fluidlevel mapping %s",mapping.definition->toString().c_str()); - addToXdr(mapping.buildXdrEntry(flc)); - finalizeXdr(); + addToXdr(mapping.buildXdrEntry(Level)); } + mapping=xdrMappings->getMapping(XDRFLUID,FluidType,1,Instance); + if (updateDouble(&mapping,Capacity)){ + LOG_DEBUG(GwLog::DEBUG+1,"found fluid capacity mapping %s",mapping.definition->toString().c_str()); + addToXdr(mapping.buildXdrEntry(Capacity)); + } + finalizeXdr(); } } @@ -1213,13 +1217,13 @@ private: addToXdr(mapping.buildXdrEntry(BatteryVoltage)); i++; } - mapping=xdrMappings->getMapping(XDRBAT,1,0,BatteryInstance); + mapping=xdrMappings->getMapping(XDRBAT,0,1,BatteryInstance); if (updateDouble(&mapping,BatteryCurrent)){ LOG_DEBUG(GwLog::DEBUG+1,"found BatteryCurrent mapping %s",mapping.definition->toString().c_str()); addToXdr(mapping.buildXdrEntry(BatteryCurrent)); i++; } - mapping=xdrMappings->getMapping(XDRBAT,2,0,BatteryInstance); + mapping=xdrMappings->getMapping(XDRBAT,0,2,BatteryInstance); if (updateDouble(&mapping,BatteryTemperature)){ LOG_DEBUG(GwLog::DEBUG+1,"found BatteryTemperature mapping %s",mapping.definition->toString().c_str()); addToXdr(mapping.buildXdrEntry(BatteryTemperature)); @@ -1311,9 +1315,15 @@ private: return; } GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,0,TemperatureInstance); - if (! updateDouble(&mapping,Temperature)) return; - LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); - addToXdr(mapping.buildXdrEntry(Temperature)); + if (! updateDouble(&mapping,Temperature)){ + LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); + addToXdr(mapping.buildXdrEntry(Temperature)); + } + mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,1,TemperatureInstance); + if (! updateDouble(&mapping,setTemperature)){ + LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); + addToXdr(mapping.buildXdrEntry(setTemperature)); + } finalizeXdr(); } @@ -1328,9 +1338,15 @@ private: return; } GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRHUMIDITY,(int)HumiditySource,0,HumidityInstance); - if (! updateDouble(&mapping,ActualHumidity)) return; - LOG_DEBUG(GwLog::DEBUG+1,"found humidity mapping %s",mapping.definition->toString().c_str()); - addToXdr(mapping.buildXdrEntry(ActualHumidity)); + if (updateDouble(&mapping,ActualHumidity)){ + LOG_DEBUG(GwLog::DEBUG+1,"found humidity mapping %s",mapping.definition->toString().c_str()); + addToXdr(mapping.buildXdrEntry(ActualHumidity)); + } + mapping=xdrMappings->getMapping(XDRHUMIDITY,(int)HumiditySource,1,HumidityInstance); + if (updateDouble(&mapping,SetHumidity)){ + LOG_DEBUG(GwLog::DEBUG+1,"found humidity mapping %s",mapping.definition->toString().c_str()); + addToXdr(mapping.buildXdrEntry(SetHumidity)); + } finalizeXdr(); } @@ -1368,7 +1384,7 @@ private: for (int i=0;i< 2;i++){ GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRENGINE,0,i+8,instance); if (! updateDouble(&mapping,ivalues[i])) continue; - addToXdr(mapping.buildXdrEntry((double)values[i])); + addToXdr(mapping.buildXdrEntry((double)ivalues[i])); } finalizeXdr(); } @@ -1415,9 +1431,15 @@ private: return; } GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,0,TemperatureInstance); - if (! updateDouble(&mapping,Temperature)) return; - LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); - addToXdr(mapping.buildXdrEntry(Temperature)); + if (updateDouble(&mapping,Temperature)){ + LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); + addToXdr(mapping.buildXdrEntry(Temperature)); + } + mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,1,TemperatureInstance); + if (updateDouble(&mapping,setTemperature)){ + LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); + addToXdr(mapping.buildXdrEntry(setTemperature)); + } finalizeXdr(); } diff --git a/lib/xdrmappings/GwXDRMappings.h b/lib/xdrmappings/GwXDRMappings.h index 4e09d8e..599317d 100644 --- a/lib/xdrmappings/GwXDRMappings.h +++ b/lib/xdrmappings/GwXDRMappings.h @@ -171,6 +171,10 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ String getTransducerName(){ return definition->getTransducerName(instanceId); } + double valueFromXdr(double value){ + if (type->fromnmea) return (*(type->fromnmea))(value); + return value; + } XdrEntry buildXdrEntry(double value); //boat Data info virtual String getBoatItemName(){ diff --git a/src/main.cpp b/src/main.cpp index 7564c25..6c24c79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -726,6 +726,7 @@ void setup() { handleN2kMessage(msg,N2KT_MSGOUT); return true; }, + &xdrMappings, config.getInt(config.min2KInterval,50) ); diff --git a/web/xdrconfig.json b/web/xdrconfig.json index d7db833..6e528f7 100644 --- a/web/xdrconfig.json +++ b/web/xdrconfig.json @@ -63,11 +63,14 @@ "l": "ExhaustGasTemperature(14)", "v": "14" } + ], + "fields":[ + {"l":"ActualTemperature","v":0,"t":4}, + {"l":"SetTemperature","v":1,"t":4} ] }, "Humidity": { "id":1, - "type":5, "selector": [ { "l": "InsideHumidity(0)", @@ -81,6 +84,10 @@ "l": "Undef(0xff)", "v": "0xff" } + ], + "fields":[ + {"l":"ActualHumidity","v":0,"t":5}, + {"l":"SetHumidity","v":1,"t":5} ] }, "Pressure": {