From f0643b636a9991fc96be6bb10d94943305cd9d38 Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 24 Oct 2023 18:20:01 +0200 Subject: [PATCH] allow to set XDR mappings from user tasks --- lib/api/GwApi.h | 6 ++ lib/config/GWConfig.h | 1 + lib/exampletask/GwExampleTask.cpp | 35 +++++++- lib/exampletask/config.json | 27 +++++- lib/usercode/GwUserCode.cpp | 3 + lib/xdrmappings/GwXDRMappings.cpp | 138 ++++++++++++++++++------------ lib/xdrmappings/GwXDRMappings.h | 6 +- src/main.cpp | 7 ++ 8 files changed, 162 insertions(+), 61 deletions(-) diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h index 9511c06..6d42011 100644 --- a/lib/api/GwApi.h +++ b/lib/api/GwApi.h @@ -5,6 +5,7 @@ #include "NMEA0183Msg.h" #include "GWConfig.h" #include "GwBoatData.h" +#include "GwXDRMappings.h" #include //API to be used for additional tasks class GwApi{ @@ -151,6 +152,11 @@ class GwApi{ virtual void remove(int idx){} virtual TaskInterfaces * taskInterfaces()=0; + /** + * only allowed during init methods + */ + virtual bool addXdrMapping(const GwXDRMappingDef &); + /** * not thread safe methods * accessing boat data must only be executed from within the main thread diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h index c1225e1..8fdb77f 100644 --- a/lib/config/GWConfig.h +++ b/lib/config/GWConfig.h @@ -40,6 +40,7 @@ class GwConfigHandler: public GwConfigDefinitions{ static void toHex(unsigned long v,char *buffer,size_t bsize); unsigned long getSaltBase(){return saltBase;} ~GwConfigHandler(); + bool userChangesAllowed(){return allowChanges;} private: unsigned long saltBase=0; void populateConfigs(GwConfigInterface **); diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp index 164761a..d6901d8 100644 --- a/lib/exampletask/GwExampleTask.cpp +++ b/lib/exampletask/GwExampleTask.cpp @@ -6,7 +6,7 @@ #include "GwApi.h" #include "GWConfig.h" #include - +#include "N2kMessages.h" /** * INVALID!!! - the next interface declaration will not work * as it is not in the correct header file @@ -32,7 +32,24 @@ void exampleInit(GwApi *api){ //you could also compute this value from some own configuration //for this example it would make a lot of sense to declare a capability //to hide this config item from the UI - see header file - api->getConfig()->setValue(api->getConfig()->minXdrInterval,"50"); + api->getConfig()->setValue(api->getConfig()->minXdrInterval,"50"); + //check if we should simulate some voltage measurements + //add an XDR mapping in this case + String voltageTransducer=api->getConfig()->getString(GwConfigDefinitions::exTransducer); + if (!voltageTransducer.isEmpty()){ + int instance=api->getConfig()->getInt(GwConfigDefinitions::exInstanceId); + GwXDRMappingDef xdr; + xdr.category=GwXDRCategory::XDRBAT; + xdr.direction=GwXDRMappingDef::Direction::M_FROM2K; + xdr.field=0; //refer to xdrconfig.json to pick up the field id (the index in the field list) - in this case 0 for Voltage + xdr.selector=0; //refer to xdrconfig.json - there is no selector under Battery, so we can leave it empty + xdr.instanceMode=GwXDRMappingDef::IS_SINGLE; //we just map exactly our instance + xdr.instanceId=instance; + xdr.xdrName=voltageTransducer; + if (!api->addXdrMapping(xdr)){ + api->getLogger()->logDebug(GwLog::ERROR,"unable to set our xdr mapping %s",xdr.toString().c_str()); + } + } } #define INVALID_COORD -99999 class GetBoatDataRequest: public GwMessage{ @@ -120,6 +137,8 @@ void exampleTask(GwApi *api){ ExampleNotWorkingIf nw1; bool nwrs=apiSetExampleNotWorkingIf(api,nw1); LOG_DEBUG(GwLog::LOG,"exampleNotWorking update returned %d",(int)nwrs); + String voltageTransducer=api->getConfig()->getString(GwConfigDefinitions::exTransducer); + int voltageInstance=api->getConfig()->getInt(GwConfigDefinitions::exInstanceId); while(true){ delay(1000); /* @@ -227,6 +246,18 @@ void exampleTask(GwApi *api){ LOG_DEBUG(GwLog::LOG,"exampleIf update rs=%d,v=%d,s=%s",(int)rs,e1.count,e1.someValue.c_str()); ExampleTaskIf e3=apiGetExampleTaskIf(api,apiResult); LOG_DEBUG(GwLog::LOG,"exampleIf after update rs=%d,v=%d,s=%s",apiResult,e3.count,e3.someValue.c_str()); + if (!voltageTransducer.isEmpty()){ + //simulate some voltage measurements... + double offset=100.0*(double)std::rand()/RAND_MAX - 50.0; + double simVoltage=(1200.0+offset)/100; + LOG_DEBUG(GwLog::LOG,"simulated voltage %f",(float)simVoltage); + tN2kMsg msg; + SetN2kDCBatStatus(msg,voltageInstance,simVoltage); + //we send out an N2K message + //and as we added an XDR mapping, we will see this in the data dashboard + //and on the NMEA0183 stream + api->sendN2kMessage(msg); + } } vTaskDelete(NULL); diff --git a/lib/exampletask/config.json b/lib/exampletask/config.json index 9074b25..7913fb7 100644 --- a/lib/exampletask/config.json +++ b/lib/exampletask/config.json @@ -20,5 +20,30 @@ "capabilities": { "testboard":"true" } + }, + { + "name": "exTransducer", + "label": "voltage transducer name", + "type": "String", + "default": "", + "description": "set the name for the xdr transducer for the simulated voltage, leave empty to disable ", + "category": "example", + "capabilities": { + "testboard":"true" + } + }, + { + "name": "exInstanceId", + "label": "voltage instance id", + "type": "number", + "default": 99, + "description": "the N2K instance id for the simulated voltage ", + "category": "example", + "min": 0, + "max": 255, + "check": "checkMinMax", + "capabilities": { + "testboard":"true" + } } -] \ No newline at end of file +] diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp index 0c4bef9..a30ca2b 100644 --- a/lib/usercode/GwUserCode.cpp +++ b/lib/usercode/GwUserCode.cpp @@ -280,6 +280,9 @@ public: virtual TaskInterfaces * taskInterfaces(){ return interfaces; } + virtual bool addXdrMapping(const GwXDRMappingDef &def){ + return api->addXdrMapping(def); + } }; diff --git a/lib/xdrmappings/GwXDRMappings.cpp b/lib/xdrmappings/GwXDRMappings.cpp index 6516f45..b2eb0c2 100644 --- a/lib/xdrmappings/GwXDRMappings.cpp +++ b/lib/xdrmappings/GwXDRMappings.cpp @@ -1,4 +1,5 @@ #include "GwXDRMappings.h" +#include "GWConfig.h" #include "N2kMessages.h" double PtoBar(double v) @@ -96,7 +97,7 @@ static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field) return GwXDRType::UNKNOWN; } //category,direction,selector,field,instanceMode,instance,name -String GwXDRMappingDef::toString() +String GwXDRMappingDef::toString() const { String rt = ""; rt += String((int)category); @@ -233,6 +234,82 @@ GwXDRMappings::GwXDRMappings(GwLog *logger, GwConfigHandler *config) this->logger = logger; this->config = config; } +bool GwXDRMappings::addFixedMapping(const GwXDRMappingDef &mapping){ + GwXDRMappingDef *nm=new GwXDRMappingDef(mapping); + bool res=addMapping(nm); + if (! res){ + LOG_DEBUG(GwLog::ERROR,"unable to add fixed mapping %s",mapping.toString().c_str()); + return false; + } + return true; +} +bool GwXDRMappings::addMapping(GwXDRMappingDef *def) +{ + if (def) + { + int typeIndex = 0; + LOG_DEBUG(GwLog::LOG, "add 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::ERROR, "no type mapping for %s", def->toString().c_str()); + return false; + } + GwXDRType *type = findType(code, &typeIndex); + if (!type) + { + LOG_DEBUG(GwLog::ERROR, "no type definition for %s", def->toString().c_str()); + return false; + } + long n2kkey = def->n2kKey(); + auto it = n2kMap.find(n2kkey); + GwXDRMapping *mapping = new GwXDRMapping(def, type); + if (it == n2kMap.end()) + { + LOG_DEBUG(GwLog::LOG, "insert mapping with key %ld", n2kkey); + GwXDRMapping::MappingList mappings; + mappings.push_back(mapping); + n2kMap[n2kkey] = mappings; + } + else + { + LOG_DEBUG(GwLog::LOG, "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::LOG, "insert mapping with n183key %s", n183key.c_str()); + GwXDRMapping::MappingList mappings; + mappings.push_back(mapping); + n183Map[n183key] = mappings; + } + else + { + LOG_DEBUG(GwLog::LOG, "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); + } + return true; + } + else + { + return false; + } +} #define MAX_MAPPINGS 100 void GwXDRMappings::begin() @@ -258,61 +335,10 @@ void GwXDRMappings::begin() GwXDRMappingDef *def = GwXDRMappingDef::fromString(cfg->asCString()); if (def) { - int typeIndex = 0; - LOG_DEBUG(GwLog::DEBUG, "read xdr mapping %s from %s", - def->toString().c_str(),namebuf); - //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); + bool res=addMapping(def); + if (! res){ + LOG_DEBUG(GwLog::ERROR,"unable to add mapping from %s",cfg); + delete cfg; } } else{ diff --git a/lib/xdrmappings/GwXDRMappings.h b/lib/xdrmappings/GwXDRMappings.h index 44d1599..246ade5 100644 --- a/lib/xdrmappings/GwXDRMappings.h +++ b/lib/xdrmappings/GwXDRMappings.h @@ -1,7 +1,6 @@ #ifndef _GWXDRMAPPINGS_H #define _GWXDRMAPPINGS_H #include "GwLog.h" -#include "GWConfig.h" #include "GwBoatData.h" #include #include @@ -115,7 +114,7 @@ class GwXDRMappingDef{ category=XDRTEMP; } //category,direction,selector,field,instanceMode,instance,name - String toString(); + String toString() const; static GwXDRMappingDef *fromString(String s); //we allow 100 entities of code,selector and field nid static unsigned long n2kKey(GwXDRCategory category, int selector, int field) @@ -200,6 +199,7 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ //the class GwXDRMappings is not intended to be deleted //the deletion will leave memory leaks! +class GwConfigHandler; class GwXDRMappings{ static const int MAX_UNKNOWN=200; static const int ESIZE=13; @@ -212,8 +212,10 @@ class GwXDRMappings{ char *unknowAsString=NULL; GwXDRFoundMapping selectMapping(GwXDRMapping::MappingList *list,int instance,const char * key); bool addUnknown(GwXDRCategory category,int selector,int field=0,int instance=-1); + bool addMapping(GwXDRMappingDef *mapping); public: GwXDRMappings(GwLog *logger,GwConfigHandler *config); + bool addFixedMapping(const GwXDRMappingDef &mapping); void begin(); //get the mappings //the returned mapping will exactly contain one mapping def diff --git a/src/main.cpp b/src/main.cpp index bdb0261..3eb8136 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -319,6 +319,13 @@ public: } virtual ~ApiImpl(){} virtual TaskInterfaces *taskInterfaces(){ return nullptr;} + virtual bool addXdrMapping(const GwXDRMappingDef &mapping){ + if (! config.userChangesAllowed()){ + logger.logDebug(GwLog::ERROR,"trying to add an XDR mapping %s after the init phase",mapping.toString().c_str()); + return false; + } + return xdrMappings.addFixedMapping(mapping); + } }; bool delayedRestart(){