From 54ccb5dcd224e2ef2aa222124688f88344b5a774 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 16:07:27 +0100 Subject: [PATCH 1/8] introduce write lock for USB channel --- lib/channel/GwChannelList.cpp | 1 + lib/queue/GwSynchronized.h | 18 +++++++++++++++--- lib/serial/GwSerial.cpp | 13 +++++++++---- lib/serial/GwSerial.h | 5 +++++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 7861f3d..c9db748 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -427,6 +427,7 @@ void GwChannelList::begin(bool fallbackSerial){ if (! fallbackSerial){ GwSerial *usbSerial=createSerialImpl(config, logger,USB_CHANNEL_ID,GWUSB_RX,GWUSB_TX,true); if (usbSerial != nullptr){ + usbSerial->enableWriteLock(); //as it is used for logging we need this additionally GwChannel *usbChannel=createChannel(logger,config,USB_CHANNEL_ID,usbSerial,GWSERIAL_TYPE_BI); if (usbChannel != nullptr){ addChannel(usbChannel); diff --git a/lib/queue/GwSynchronized.h b/lib/queue/GwSynchronized.h index 786b5f0..08c36c6 100644 --- a/lib/queue/GwSynchronized.h +++ b/lib/queue/GwSynchronized.h @@ -3,14 +3,26 @@ class GwSynchronized{ private: - SemaphoreHandle_t *locker; + SemaphoreHandle_t locker=nullptr; + void lock(){ + if (locker != nullptr) xSemaphoreTake(locker, portMAX_DELAY); + } public: + /** + * deprecated + * as SemaphoreHandle_t is already a pointer just use this directly + */ GwSynchronized(SemaphoreHandle_t *locker){ + if (locker == nullptr) return; + this->locker=*locker; + lock(); + } + GwSynchronized(SemaphoreHandle_t locker){ this->locker=locker; - if (locker != nullptr) xSemaphoreTake(*locker, portMAX_DELAY); + lock(); } ~GwSynchronized(){ - if (locker != nullptr) xSemaphoreGive(*locker); + if (locker != nullptr) xSemaphoreGive(locker); } }; diff --git a/lib/serial/GwSerial.cpp b/lib/serial/GwSerial.cpp index c0fb06e..b23c52f 100644 --- a/lib/serial/GwSerial.cpp +++ b/lib/serial/GwSerial.cpp @@ -63,6 +63,7 @@ GwSerial::~GwSerial() { delete buffer; if (readBuffer) delete readBuffer; + if (lock != nullptr) vSemaphoreDelete(lock); } String GwSerial::getMode(){ @@ -87,10 +88,14 @@ size_t GwSerial::enqueue(const uint8_t *data, size_t len, bool partial) } GwBuffer::WriteStatus GwSerial::write(){ if (! isInitialized()) return GwBuffer::ERROR; - size_t numWrite=availableForWrite(); - size_t rt=buffer->fetchData(numWrite,[](uint8_t *buffer,size_t len, void *p){ - return ((GwSerial *)p)->stream->write(buffer,len); - },this); + size_t rt=0; + { + GWSYNCHRONIZED(lock); + size_t numWrite=availableForWrite(); + rt=buffer->fetchData(numWrite,[](uint8_t *buffer,size_t len, void *p){ + return ((GwSerial *)p)->stream->write(buffer,len); + },this); + } if (rt != 0){ LOG_DEBUG(GwLog::DEBUG+1,"Serial %d write %d",id,rt); } diff --git a/lib/serial/GwSerial.h b/lib/serial/GwSerial.h index 5646299..1932878 100644 --- a/lib/serial/GwSerial.h +++ b/lib/serial/GwSerial.h @@ -4,6 +4,7 @@ #include "GwLog.h" #include "GwBuffer.h" #include "GwChannelInterface.h" +#include "GwSynchronized.h" #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 #include "hal/usb_serial_jtag_ll.h" #endif @@ -26,8 +27,12 @@ class GwSerial : public GwChannelInterface{ virtual long getFlushTimeout(){return 2000;} virtual int availableForWrite()=0; int type=0; + SemaphoreHandle_t lock=nullptr; public: GwSerial(GwLog *logger,Stream *stream,int id,int type,bool allowRead=true); + void enableWriteLock(){ + lock=xSemaphoreCreateMutex(); + } virtual ~GwSerial(); bool isInitialized(); virtual size_t sendToClients(const char *buf,int sourceId,bool partial=false); From bfa38fe2e4f8d994d9ef574bd6aebf5d7f47efa3 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 16:16:35 +0100 Subject: [PATCH 2/8] directly use SemaphoreHandle_t as pointer --- lib/exampletask/GwExampleTask.cpp | 4 ++-- lib/socketserver/GwTcpClient.cpp | 6 +++--- lib/usercode/GwUserCode.h | 5 +++-- src/main.cpp | 13 +++++++------ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp index 9b4cbcd..821dd57 100644 --- a/lib/exampletask/GwExampleTask.cpp +++ b/lib/exampletask/GwExampleTask.cpp @@ -156,11 +156,11 @@ class ExampleWebData{ vSemaphoreDelete(lock); } void set(int v){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); data=v; } int get(){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); return data; } }; diff --git a/lib/socketserver/GwTcpClient.cpp b/lib/socketserver/GwTcpClient.cpp index e5f914b..2792300 100644 --- a/lib/socketserver/GwTcpClient.cpp +++ b/lib/socketserver/GwTcpClient.cpp @@ -246,7 +246,7 @@ void GwTcpClient::resolveHost(String host) { LOG_DEBUG(GwLog::LOG,"start resolving %s",host.c_str()); { - GWSYNCHRONIZED(&locker); + GWSYNCHRONIZED(locker); resolvedAddress.resolved = false; } state = C_RESOLVING; @@ -283,12 +283,12 @@ void GwTcpClient::resolveHost(String host) void GwTcpClient::setResolved(IPAddress addr, bool valid){ LOG_DEBUG(GwLog::LOG,"setResolved %s, valid=%s", addr.toString().c_str(),(valid?"true":"false")); - GWSYNCHRONIZED(&locker); + GWSYNCHRONIZED(locker); resolvedAddress.address=addr; resolvedAddress.resolved=valid; state=C_RESOLVED; } GwTcpClient::ResolvedAddress GwTcpClient::getResolved(){ - GWSYNCHRONIZED(&locker); + GWSYNCHRONIZED(locker); return resolvedAddress; } \ No newline at end of file diff --git a/lib/usercode/GwUserCode.h b/lib/usercode/GwUserCode.h index 94e745d..5007d3e 100644 --- a/lib/usercode/GwUserCode.h +++ b/lib/usercode/GwUserCode.h @@ -38,13 +38,14 @@ class TaskInterfacesStorage; class GwUserCode{ GwLog *logger; GwApiInternal *api; - SemaphoreHandle_t *mainLock; + SemaphoreHandle_t mainLock=nullptr; TaskInterfacesStorage *taskData; void startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name); public: ~GwUserCode(); typedef std::map Capabilities; - GwUserCode(GwApiInternal *api, SemaphoreHandle_t *mainLock); + GwUserCode(GwApiInternal *api); + void begin(SemaphoreHandle_t mainLock){this->mainLock=mainLock;} void startUserTasks(int baseId); void startInitTasks(int baseId); void startAddonTask(String name,TaskFunction_t task, int id); diff --git a/src/main.cpp b/src/main.cpp index ae8f3d5..e781ada 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -235,17 +235,17 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId,bool conv class CalibrationValues { using Map=std::map; Map values; - SemaphoreHandle_t lock; + SemaphoreHandle_t lock=nullptr; public: CalibrationValues(){ lock=xSemaphoreCreateMutex(); } void set(const String &name,double value){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); values[name]=value; } bool get(const String &name, double &value){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); auto it=values.find(name); if (it==values.end()) return false; value=it->second; @@ -373,7 +373,7 @@ bool delayedRestart(){ },"reset",2000,&logger,0,NULL) == pdPASS; } ApiImpl *apiImpl=new ApiImpl(MIN_USER_TASK); -GwUserCode userCodeHandler(apiImpl,&mainLock); +GwUserCode userCodeHandler(apiImpl); #define JSON_OK "{\"status\":\"OK\"}" #define JSON_INVALID_PASS F("{\"status\":\"invalid password\"}") @@ -788,6 +788,7 @@ void setup() { logger.setWriter(new DefaultLogWriter()); #endif boatData.begin(); + userCodeHandler.begin(mainLock); userCodeHandler.startInitTasks(MIN_USER_TASK); channels.preinit(); config.stopChanges(); @@ -937,7 +938,7 @@ void setup() { logger.logDebug(GwLog::LOG,"starting addon tasks"); logger.flush(); { - GWSYNCHRONIZED(&mainLock); + GWSYNCHRONIZED(mainLock); userCodeHandler.startUserTasks(MIN_USER_TASK); } timers.addAction(HEAP_REPORT_TIME,[](){ @@ -967,7 +968,7 @@ void handleSendAndRead(bool handleRead){ void loopRun() { //logger.logDebug(GwLog::DEBUG,"main loop start"); monitor.reset(); - GWSYNCHRONIZED(&mainLock); + GWSYNCHRONIZED(mainLock); logger.flush(); monitor.setTime(1); gwWifi.loop(); From bada311f83bcc70d16c490a5a5a1c34d8657f749 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 16:27:13 +0100 Subject: [PATCH 3/8] directly use SemaphoreHandle_t as pointer (finalize merge) --- lib/usercode/GwUserCode.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp index bf4502b..aebc671 100644 --- a/lib/usercode/GwUserCode.cpp +++ b/lib/usercode/GwUserCode.cpp @@ -113,7 +113,7 @@ class TaskInterfacesStorage{ lock=xSemaphoreCreateMutex(); } bool set(const String &file, const String &name, const String &task,GwApi::TaskInterfaces::Ptr v){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); auto it=registrations().find(name); if (it == registrations().end()){ LOG_DEBUG(GwLog::ERROR,"TaskInterfaces: invalid set %s not known",name.c_str()); @@ -141,7 +141,7 @@ class TaskInterfacesStorage{ return true; } GwApi::TaskInterfaces::Ptr get(const String &name, int &result){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); auto it = values.find(name); if (it == values.end()) { @@ -188,7 +188,7 @@ class TaskApi : public GwApiInternal { GwApiInternal *api=nullptr; int sourceId; - SemaphoreHandle_t *mainLock; + SemaphoreHandle_t mainLock; SemaphoreHandle_t localLock; std::map> counter; std::map webHandlers; @@ -200,7 +200,7 @@ class TaskApi : public GwApiInternal public: TaskApi(GwApiInternal *api, int sourceId, - SemaphoreHandle_t *mainLock, + SemaphoreHandle_t mainLock, const String &name, TaskInterfacesStorage *s, bool init=false) @@ -264,14 +264,14 @@ public: vSemaphoreDelete(localLock); }; virtual void fillStatus(GwJsonDocument &status){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); if (! counterUsed) return; for (auto it=counter.begin();it != counter.end();it++){ it->second.toJson(status); } }; virtual int getJsonSize(){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); if (! counterUsed) return 0; int rt=0; for (auto it=counter.begin();it != counter.end();it++){ @@ -280,7 +280,7 @@ public: return rt; }; virtual void increment(int idx,const String &name,bool failed=false){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counterUsed=true; auto it=counter.find(idx); if (it == counter.end()) return; @@ -288,18 +288,18 @@ public: else (it->second.add(name)); }; virtual void reset(int idx){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counterUsed=true; auto it=counter.find(idx); if (it == counter.end()) return; it->second.reset(); }; virtual void remove(int idx){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counter.erase(idx); } virtual int addCounter(const String &name){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counterUsed=true; counterIdx++; //avoid the need for an empty counter constructor @@ -317,7 +317,7 @@ public: return api->addXdrMapping(def); } virtual void registerRequestHandler(const String &url,HandlerFunction handler){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); webHandlers[url]=handler; } virtual void addCapability(const String &name, const String &value){ @@ -344,7 +344,7 @@ public: { GwApi::HandlerFunction handler; { - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); auto it = webHandlers.find(url); if (it == webHandlers.end()) { @@ -359,10 +359,9 @@ public: } }; -GwUserCode::GwUserCode(GwApiInternal *api,SemaphoreHandle_t *mainLock){ +GwUserCode::GwUserCode(GwApiInternal *api){ this->logger=api->getLogger(); this->api=api; - this->mainLock=mainLock; this->taskData=new TaskInterfacesStorage(this->logger); } GwUserCode::~GwUserCode(){ From d132cf56ea8cf662761fe912336605074a4fb771 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 17:25:16 +0100 Subject: [PATCH 4/8] allow to show unmapped XDR transducer values --- lib/config/GwConverterConfig.h | 4 ++++ lib/nmea0183ton2k/NMEA0183DataToN2K.cpp | 15 ++++++++++++- lib/xdrmappings/GwXDRMappings.cpp | 28 +++++++++++++++++++----- lib/xdrmappings/GwXDRMappings.h | 29 ++++++++++++++++++++----- web/config.json | 8 +++++++ 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/lib/config/GwConverterConfig.h b/lib/config/GwConverterConfig.h index d0dbc6a..4a93a71 100644 --- a/lib/config/GwConverterConfig.h +++ b/lib/config/GwConverterConfig.h @@ -71,9 +71,12 @@ class GwConverterConfig{ int rmcInterval=1000; int rmcCheckTime=4000; int winst312=256; + bool unmappedXdr=false; + unsigned long xdrTimeout=60000; std::vector windMappings; void init(GwConfigHandler *config, GwLog*logger){ minXdrInterval=config->getInt(GwConfigDefinitions::minXdrInterval,100); + xdrTimeout=config->getInt(GwConfigDefinitions::timoSensor); starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0); portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1); min2KInterval=config->getInt(GwConfigDefinitions::min2KInterval,50); @@ -83,6 +86,7 @@ class GwConverterConfig{ rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000); if (rmcInterval < 0) rmcInterval=0; if (rmcInterval > 0 && rmcInterval <100) rmcInterval=100; + unmappedXdr=config->getBool(GwConfigDefinitions::unknownXdr); winst312=config->getInt(GwConfigDefinitions::winst312,256); for (auto && it:windConfigs){ String cfg=config->getString(it.second); diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp index bd593d5..668a8ef 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -189,6 +189,7 @@ private: if (N2kIsNA(v)) return N2kInt8NA; return v; } + void convertXDR(const SNMEA0183Msg &msg){ XdrMappingList foundMappings; for (int offset=0;offset <= (msg.FieldCount()-4);offset+=4){ @@ -199,7 +200,19 @@ private: String unit=msg.Field(offset+2); String transducerName=msg.Field(offset+3); GwXDRFoundMapping found=xdrMappings->getMapping(transducerName,type,unit); - if (found.empty) continue; + if (found.empty) { + if (config.unmappedXdr){ + const GwXDRType *typeDef=xdrMappings->findType(type,unit); + GwXdrUnknownMapping mapping(transducerName,unit,typeDef,config.xdrTimeout); + value=mapping.valueFromXdr(value); + if (boatData->update(value,msg.sourceId,&mapping)){ + //TODO: potentially update the format + LOG_DEBUG(GwLog::DEBUG+1,"found unmapped XDR %s:%s, value %f", + transducerName.c_str(),mapping.getBoatItemFormat().c_str(),value); + } + } + continue; + } value=found.valueFromXdr(value); if (!boatData->update(value,msg.sourceId,&found)) continue; LOG_DEBUG(GwLog::DEBUG+1,"found mapped XDR %s:%s, value %f", diff --git a/lib/xdrmappings/GwXDRMappings.cpp b/lib/xdrmappings/GwXDRMappings.cpp index ad26bc8..28f88c2 100644 --- a/lib/xdrmappings/GwXDRMappings.cpp +++ b/lib/xdrmappings/GwXDRMappings.cpp @@ -58,6 +58,7 @@ GwXDRType *types[] = { new GwXDRType(GwXDRType::DISPLACEMENTD, "A", "D",DegToRad,RadToDeg,"rd"), new GwXDRType(GwXDRType::RPM,"T","R") }; +static GwXDRType genericType(GwXDRType::GENERIC, "G", ""); template int GetArrLength(T(&)[size]){return size;} static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) @@ -82,6 +83,19 @@ static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) return NULL; } +static GwXDRType *findType(const String &typeString, const String &unitString) +{ + int len=GetArrLength(types); + for (int i=0; i< len; i++) + { + if (types[i]->xdrtype == typeString && types[i]->xdrunit == unitString) + { + return types[i]; + } + } + return &genericType; +} + #include "GwXdrTypeMappings.h" static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field) @@ -199,7 +213,7 @@ GwXDRMappingDef *GwXDRMappingDef::fromString(String s) } return rt; } -String GwXDRMappingDef::getTransducerName(int instance) +String GwXDRMappingDef::getTransducerName(int instance) const { String name = xdrName; if (instanceMode == GwXDRMappingDef::IS_AUTO) @@ -257,7 +271,7 @@ bool GwXDRMappings::addMapping(GwXDRMappingDef *def) LOG_DEBUG(GwLog::ERROR, "no type mapping for %s", def->toString().c_str()); return false; } - GwXDRType *type = findType(code, &typeIndex); + GwXDRType *type = ::findType(code, &typeIndex); if (!type) { LOG_DEBUG(GwLog::ERROR, "no type definition for %s", def->toString().c_str()); @@ -298,7 +312,7 @@ bool GwXDRMappings::addMapping(GwXDRMappingDef *def) LOG_DEBUG(GwLog::LOG, "append mapping with n183key %s", n183key.c_str()); it->second.push_back(mapping); } - type = findType(code, &typeIndex); + type = ::findType(code, &typeIndex); if (!type) break; mapping = new GwXDRMapping(def, type); @@ -471,7 +485,7 @@ String GwXDRMappings::getXdrEntry(String mapping, double value,int instance){ { return rt; } - GwXDRType *type = findType(code, &typeIndex); + GwXDRType *type = ::findType(code, &typeIndex); bool first=true; unsigned long invalidTime=config->getInt(GwConfigDefinitions::timoSensor); while (type){ @@ -480,8 +494,12 @@ String GwXDRMappings::getXdrEntry(String mapping, double value,int instance){ if (first) first=false; else rt+=","; rt+=found.buildXdrEntry(value).entry; - type = findType(code, &typeIndex); + type = ::findType(code, &typeIndex); } delete def; return rt; } + +const GwXDRType * GwXDRMappings::findType(const String &typeString, const String &unitString) const{ + return ::findType(typeString,unitString); +} \ No newline at end of file diff --git a/lib/xdrmappings/GwXDRMappings.h b/lib/xdrmappings/GwXDRMappings.h index 7643bee..198a729 100644 --- a/lib/xdrmappings/GwXDRMappings.h +++ b/lib/xdrmappings/GwXDRMappings.h @@ -140,7 +140,7 @@ class GwXDRMappingDef{ rt += xdrUnit; return rt; } - String getTransducerName(int instance); + String getTransducerName(int instance) const; private: static bool handleToken(String tok,int index,GwXDRMappingDef *def); }; @@ -163,12 +163,12 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ String entry; String transducer; }; - GwXDRMappingDef *definition=NULL; - GwXDRType *type=NULL; + const GwXDRMappingDef *definition=NULL; + const GwXDRType *type=NULL; int instanceId=-1; bool empty=true; unsigned long timeout=0; - GwXDRFoundMapping(GwXDRMappingDef *definition,GwXDRType *type, unsigned long timeout){ + GwXDRFoundMapping(const GwXDRMappingDef *definition,const GwXDRType *type, unsigned long timeout){ this->definition=definition; this->type=type; this->timeout=timeout; @@ -182,7 +182,7 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ empty=false; } GwXDRFoundMapping(){} - String getTransducerName(){ + virtual String getTransducerName(){ return definition->getTransducerName(instanceId); } double valueFromXdr(double value){ @@ -203,6 +203,24 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ } }; +class GwXdrUnknownMapping : public GwXDRFoundMapping{ + String name; + String unit; + public: + GwXdrUnknownMapping(const String &xdrName, const String &xdrUnit,const GwXDRType *type,unsigned long timeout): + name(xdrName),unit(xdrUnit), GwXDRFoundMapping(nullptr,type,timeout){ + + } + virtual String getTransducerName(){ + return name; + } + virtual String getBoatItemFormat(){ + String rt=GwXDRFoundMapping::getBoatItemFormat(); + if (type->xdrunit.isEmpty()) rt+=unit; + return rt; + } +}; + //the class GwXDRMappings is not intended to be deleted //the deletion will leave memory leaks! class GwConfigHandler; @@ -229,6 +247,7 @@ class GwXDRMappings{ GwXDRFoundMapping getMapping(GwXDRCategory category,int selector,int field=0,int instance=-1); String getXdrEntry(String mapping, double value,int instance=0); const char * getUnMapped(); + const GwXDRType * findType(const String &typeString, const String &unitString) const; }; diff --git a/web/config.json b/web/config.json index 0b75d27..26d3166 100644 --- a/web/config.json +++ b/web/config.json @@ -210,6 +210,14 @@ "description":"send out the converted data on the NMEA2000 bus\nIf set to off the converted data will still be shown at the data tab.", "category":"converter" }, + { + "name":"unknownXdr", + "label":"show unknown XDR", + "type":"boolean", + "default":"false", + "description":"show received XDR transducer values in data display if there is no XDR mapping for them", + "category":"converter" + }, { "name":"sendRMCi", "label":"send RMC interval", From 3c198194aa32da35b7bad9ebb2a8fbd52ddbe55f Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 18:05:37 +0100 Subject: [PATCH 5/8] better log when wifi is connecting --- lib/gwwifi/GwWifi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gwwifi/GwWifi.cpp b/lib/gwwifi/GwWifi.cpp index 69715d2..6cd7f72 100644 --- a/lib/gwwifi/GwWifi.cpp +++ b/lib/gwwifi/GwWifi.cpp @@ -108,7 +108,7 @@ void GwWifi::loop(){ } else{ if (! clientIsConnected){ - LOG_DEBUG(GwLog::LOG,"wifiClient %s now connected to",wifiSSID->asCString()); + LOG_DEBUG(GwLog::LOG,"wifiClient now connected to %s at %s",wifiSSID->asCString(),WiFi.localIP().toString().c_str()); clientIsConnected=true; } } From 7fead48b9d76facd441a1678615338faa9261474 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 18:31:46 +0100 Subject: [PATCH 6/8] do not remove dashboard page accidently when restarting --- web/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/index.js b/web/index.js index 538a51a..39401ec 100644 --- a/web/index.js +++ b/web/index.js @@ -1798,7 +1798,7 @@ let id = el.getAttribute('id'); if (id) { if (!names[id.replace(/^frame_/, '')]) { - el.parentElement.remove(); + el.remove(); } } }); From ca60d3fbac21884f77c4aa568fa4457b1cb59e3a Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 19:09:56 +0100 Subject: [PATCH 7/8] =?UTF-8?q?remove=20useless=20unit=20=C2=B0=20for=20fo?= =?UTF-8?q?rmatLat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/index.js b/web/index.js index 39401ec..bbc96e3 100644 --- a/web/index.js +++ b/web/index.js @@ -1578,7 +1578,7 @@ if (isNaN(x)) return '-----'; return formatLonLatsDecimal(x, 'lat'); }, - u: '°' + u: '' }, formatLongitude: { f: function (v) { From d81a56aec7ed61006710b4704b3edad458be8b88 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Nov 2024 19:10:21 +0100 Subject: [PATCH 8/8] correctly handle invalid/empty fields in RMB message --- lib/nmea0183ton2k/NMEA0183DataToN2K.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp index 668a8ef..cf335df 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -320,7 +320,7 @@ private: return; } tN2kMsg n2kMsg; - if (boatData->XTE->update(rmb.xte,msg.sourceId)){ + if (updateDouble(boatData->XTE,rmb.xte,msg.sourceId)){ tN2kXTEMode mode=N2kxtem_Autonomous; if (msg.FieldCount() > 13){ const char *modeChar=msg.Field(13); @@ -331,10 +331,10 @@ private: } uint8_t destinationId=getWaypointId(rmb.destID); uint8_t sourceId=getWaypointId(rmb.originID); - if (boatData->DTW->update(rmb.dtw,msg.sourceId) - && boatData->BTW->update(rmb.btw,msg.sourceId) - && boatData->WPLat->update(rmb.latitude,msg.sourceId) - && boatData->WPLon->update(rmb.longitude,msg.sourceId) + if (updateDouble(boatData->DTW,rmb.dtw,msg.sourceId) + && updateDouble(boatData->BTW,rmb.btw,msg.sourceId) + && updateDouble(boatData->WPLat,rmb.latitude,msg.sourceId) + && updateDouble(boatData->WPLon,rmb.longitude,msg.sourceId) ){ SetN2kNavigationInfo(n2kMsg,1,rmb.dtw,N2khr_true, false,