From 01dae66459f9ba38efa1d60704c5b720d8e5c8c2 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Wed, 29 Dec 2021 19:52:36 +0100 Subject: [PATCH] introduce channel config abstraction --- lib/channel/GwChannelConfig.cpp | 102 +++++++++++++++++ lib/channel/GwChannelConfig.h | 50 ++++++++ lib/config/GWConfig.cpp | 13 ++- lib/config/GwConfigItem.h | 4 +- src/main.cpp | 194 ++++++++++++++++---------------- 5 files changed, 258 insertions(+), 105 deletions(-) create mode 100644 lib/channel/GwChannelConfig.cpp create mode 100644 lib/channel/GwChannelConfig.h diff --git a/lib/channel/GwChannelConfig.cpp b/lib/channel/GwChannelConfig.cpp new file mode 100644 index 0000000..8ef8c34 --- /dev/null +++ b/lib/channel/GwChannelConfig.cpp @@ -0,0 +1,102 @@ +#include "GwChannelConfig.h" + +GwChannelConfig::GwChannelConfig(GwLog *logger,String name){ + this->logger = logger; + this->name=name; + this->countIn=new GwCounter(String("count")+name+String("in")); + this->countOut=new GwCounter(String("count")+name+String("out")); +} +void GwChannelConfig::begin( + bool enabled, + bool nmeaOut, + bool nmeaIn, + String readFilter, + String writeFilter, + bool seaSmartOut, + bool toN2k) +{ + this->enabled = enabled; + this->NMEAout = nmeaOut; + this->NMEAin = nmeaIn; + this->readFilter=readFilter.isEmpty()? + NULL: + new GwNmeaFilter(readFilter); + this->writeFilter=writeFilter.isEmpty()? + NULL: + new GwNmeaFilter(writeFilter); +} +void GwChannelConfig::updateCounter(const char *msg, bool out) +{ + char key[6]; + if (msg[0] == '$') + { + strncpy(key, &msg[3], 3); + key[3] = 0; + } + else if (msg[0] == '!') + { + strncpy(key, &msg[1], 5); + key[5] = 0; + } + else{ + return; + } + if (out){ + countOut->add(key); + } + else{ + countIn->add(key); + } +} +bool GwChannelConfig::canSendOut(unsigned long pgn){ + if (! enabled) return false; + if (! NMEAout) return false; + countOut->add(String(pgn)); + return true; +} +bool GwChannelConfig::canReceive(unsigned long pgn){ + if (!enabled) return false; + if (!NMEAin) return false; + countIn->add(String(pgn)); + return true; +} + +bool GwChannelConfig::canSendOut(const char *buffer){ + if (! enabled) return false; + if (! NMEAout) return false; + if (writeFilter && ! writeFilter->canPass(buffer)) return false; + updateCounter(buffer,true); + return true; +} + +bool GwChannelConfig::canReceive(const char *buffer){ + if (! enabled) return false; + if (! NMEAin) return false; + if (readFilter && ! readFilter->canPass(buffer)) return false; + updateCounter(buffer,false); + return true; +} + +int GwChannelConfig::getJsonSize(){ + if (! enabled) return 0; + int rt=2; + if (countIn) rt+=countIn->getJsonSize(); + if (countOut) rt+=countOut->getJsonSize(); + return rt; +} +void GwChannelConfig::toJson(GwJsonDocument &doc){ + if (! enabled) return; + if (countOut) countOut->toJson(doc); + if (countIn) countIn->toJson(doc); +} +String GwChannelConfig::toString(){ + String rt="CH:"+name; + rt+=enabled?"[ena]":"[dis]"; + rt+=NMEAin?"in,":""; + rt+=NMEAout?"out,":""; + if (readFilter) rt+="RF:"+ readFilter->toString(); + if (writeFilter) rt+="WF:"+ writeFilter->toString(); + rt+=","+ toN2k?"n2k":""; + rt+=","+ seaSmartOut?"SM":""; + return rt; +} diff --git a/lib/channel/GwChannelConfig.h b/lib/channel/GwChannelConfig.h new file mode 100644 index 0000000..1cacf95 --- /dev/null +++ b/lib/channel/GwChannelConfig.h @@ -0,0 +1,50 @@ +#pragma once +#include "GwConfigItem.h" +#include "GwLog.h" +#include "GWConfig.h" +#include "GwCounter.h" +#include "GwJsonDocument.h" + +class GwChannelConfig{ + bool enabled=false; + bool NMEAout=false; + bool NMEAin=false; + GwNmeaFilter* readFilter=NULL; + GwNmeaFilter* writeFilter=NULL; + bool seaSmartOut=false; + bool toN2k=false; + GwLog *logger; + String name; + GwCounter *countIn=NULL; + GwCounter *countOut=NULL; + void updateCounter(const char *msg, bool out); + public: + GwChannelConfig( + GwLog *logger, + String name); + void begin( + bool enabled, + bool nmeaOut, + bool nmeaIn, + String readFilter, + String writeFilter, + bool seaSmartOut, + bool toN2k + ); + + void enable(bool enabled){ + this->enabled=enabled; + } + bool isEnabled(){return enabled;} + bool shouldRead(){return enabled && NMEAin;} + bool canSendOut(unsigned long pgn); + bool canReceive(unsigned long pgn); + bool canSendOut(const char *buffer); + bool canReceive(const char *buffer); + bool sendSeaSmart(){ return seaSmartOut;} + bool sendToN2K(){return toN2k;} + int getJsonSize(); + void toJson(GwJsonDocument &doc); + String toString(); +}; + diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index 7aef68b..c6c8532 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -140,18 +140,21 @@ void GwNmeaFilter::parseFilter(){ // "0:1:RMB,RMC" // 0: AIS off, 1:whitelist, list of sentences if (isReady) return; + if (config.isEmpty()){ + isReady=true; + return; + } int found=0; int last=0; int index=0; - String data=config->asString(); - while ((found = data.indexOf(':',last)) >= 0){ - String tok=data.substring(last,found); + while ((found = config.indexOf(':',last)) >= 0){ + String tok=config.substring(last,found); handleToken(tok,index); last=found+1; index++; } - if (last < data.length()){ - String tok=data.substring(last); + if (last < config.length()){ + String tok=config.substring(last); handleToken(tok,index); } isReady=true; diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index cef920a..f97bbd5 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -46,7 +46,7 @@ class GwConfigInterface{ class GwNmeaFilter{ private: - GwConfigInterface *config=NULL; + String config; bool isReady=false; bool ais=true; bool blacklist=true; @@ -54,7 +54,7 @@ class GwNmeaFilter{ void handleToken(String token, int index); void parseFilter(); public: - GwNmeaFilter(GwConfigInterface *config){ + GwNmeaFilter(String config){ this->config=config; isReady=false; } diff --git a/src/main.cpp b/src/main.cpp index be18d13..4c5d6a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,13 +61,16 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include "GwUserCode.h" #include "GwStatistics.h" #include "GwUpdate.h" +#include "GwTcpClient.h" +#include "GwChannelConfig.h" //NMEA message channels #define N2K_CHANNEL_ID 0 #define USB_CHANNEL_ID 1 #define SERIAL1_CHANNEL_ID 2 -#define MIN_TCP_CHANNEL_ID 3 +#define TCP_CLIENT_CHANNEL_ID 3 +#define MIN_TCP_CHANNEL_ID 4 #define MIN_USER_TASK 200 @@ -130,12 +133,20 @@ GwWebServer webserver(&logger,&mainQueue,80); GwCounter countNMEA2KIn("count2Kin"); GwCounter countNMEA2KOut("count2Kout"); -GwCounter countUSBIn("countUSBin"); -GwCounter countUSBOut("countUSBout"); -GwCounter countTCPIn("countTCPin"); -GwCounter countTCPOut("countTCPout"); -GwCounter countSerialIn("countSerialIn"); -GwCounter countSerialOut("countSerialOut"); +GwChannelConfig usbChannel(&logger,"USB"); +GwChannelConfig actisenseChannel(&logger,"USB"); +GwChannelConfig tcpChannel(&logger,"TCPServer"); +GwChannelConfig serialChannel(&logger,"TCPClient"); +GwChannelConfig tclChannel(&logger,"TCPClient"); + +GwChannelConfig * channelFromSource(int source){ + if (source == USB_CHANNEL_ID) return &usbChannel; + if (source == SERIAL1_CHANNEL_ID) return &serialChannel; + if (source == TCP_CLIENT_CHANNEL_ID) return &tclChannel; + if (source >= MIN_TCP_CHANNEL_ID && source < MIN_USER_TASK) return &tcpChannel; + return NULL; +} + unsigned long saltBase=esp_random(); @@ -180,64 +191,12 @@ bool checkPass(String hash){ GwUpdate updater(&logger,&webserver,&checkPass); -void updateNMEACounter(int id,const char *msg,bool incoming,bool fail=false){ - //we rely on the msg being long enough - char key[6]; - if (msg[0] == '$') { - strncpy(key,&msg[3],3); - key[3]=0; - } - else if(msg[0] == '!'){ - strncpy(key,&msg[1],5); - key[5]=0; - } - else return; - GwCounter *counter=NULL; - if (id == USB_CHANNEL_ID) counter=incoming?&countUSBIn:&countUSBOut; - if (id == SERIAL1_CHANNEL_ID) counter=incoming?&countSerialIn:&countSerialOut; - if (id >= MIN_TCP_CHANNEL_ID) counter=incoming?&countTCPIn:&countTCPOut; - if (! counter) return; - if (fail){ - counter->addFail(key); - } - else{ - counter->add(key); - } -} + //configs that we need in main -GwConfigInterface *sendUsb=config.getConfigItem(config.sendUsb,true); -GwConfigInterface *readUsb=config.getConfigItem(config.receiveUsb,true); -GwConfigInterface *usbActisense=config.getConfigItem(config.usbActisense,true); -GwConfigInterface *usbSendActisens=config.getConfigItem(config.usbActSend,true); -GwConfigInterface *sendTCP=config.getConfigItem(config.sendTCP,true); -GwConfigInterface *readTCP=config.getConfigItem(config.readTCP,true); -GwConfigInterface *sendSeasmart=config.getConfigItem(config.sendSeasmart,true); GwConfigInterface *systemName=config.getConfigItem(config.systemName,true); -GwConfigInterface *n2kFromTCP=config.getConfigItem(config.tcpToN2k,true); -GwConfigInterface *n2kFromUSB=config.getConfigItem(config.usbToN2k,true); -GwConfigInterface *receiveSerial=config.getConfigItem(config.receiveSerial,true); -GwConfigInterface *sendSerial=config.getConfigItem(config.sendSerial,true); -GwConfigInterface *n2kFromSerial=config.getConfigItem(config.serialToN2k,true); -GwNmeaFilter usbReadFilter(config.getConfigItem(config.usbReadFilter,true)); -GwNmeaFilter usbWriteFilter(config.getConfigItem(config.usbWriteFilter,true)); -GwNmeaFilter serialReadFilter(config.getConfigItem(config.serialReadF,true)); -GwNmeaFilter serialWriteFilter(config.getConfigItem(config.serialWriteF,true)); -GwNmeaFilter tcpReadFilter(config.getConfigItem(config.tcpReadFilter,true)); -GwNmeaFilter tcpWriteFilter(config.getConfigItem(config.tcpWriteFilter,true)); - -bool checkFilter(const char *buffer,int channelId,bool read){ - GwNmeaFilter *filter=NULL; - if (channelId == USB_CHANNEL_ID) filter=read?&usbReadFilter:&usbWriteFilter; - else if (channelId == SERIAL1_CHANNEL_ID) filter=read?&serialReadFilter:&serialWriteFilter; - else if (channelId >= MIN_TCP_CHANNEL_ID) filter=read?&tcpReadFilter:&tcpWriteFilter; - if (!filter) return true; - if (filter->canPass(buffer)) return true; - logger.logDebug(GwLog::DEBUG,"%s filter for channel %d dropped %s",(read?"read":"write"),channelId,buffer); - return false; -} bool serCanWrite=true; bool serCanRead=true; @@ -246,17 +205,14 @@ GwSerial *usbSerial = new GwSerial(NULL, 0, USB_CHANNEL_ID); GwSerial *serial1=NULL; void sendBufferToChannels(const char * buffer, int sourceId){ - if (sendTCP->asBoolean() && checkFilter(buffer,MIN_TCP_CHANNEL_ID,false)){ + if (sourceId < MIN_TCP_CHANNEL_ID && tcpChannel.canSendOut(buffer)){ socketServer.sendToClients(buffer,sourceId); - updateNMEACounter(MIN_TCP_CHANNEL_ID,buffer,false); } - if (! actisenseReader && sendUsb->asBoolean() && checkFilter(buffer,USB_CHANNEL_ID,false)){ + if (sourceId != USB_CHANNEL_ID && usbChannel.canSendOut(buffer)){ usbSerial->sendToClients(buffer,sourceId); - updateNMEACounter(USB_CHANNEL_ID,buffer,false); } - if (serial1 && serCanWrite && checkFilter(buffer,SERIAL1_CHANNEL_ID,false)){ + if (serial1 && sourceId != SERIAL1_CHANNEL_ID && serialChannel.canSendOut(buffer)){ serial1->sendToClients(buffer,sourceId); - updateNMEACounter(SERIAL1_CHANNEL_ID,buffer,false); } } typedef enum { @@ -272,16 +228,17 @@ void handleN2kMessage(const tN2kMsg &n2kMsg,N2K_MsgDirection direction) if (direction == N2KT_MSGIN){ countNMEA2KIn.add(n2kMsg.PGN); } - if (sendSeasmart->asBoolean()) + if (tcpChannel.sendSeaSmart() || tclChannel.sendSeaSmart()) { char buf[MAX_NMEA2000_MESSAGE_SEASMART_SIZE]; if (N2kToSeasmart(n2kMsg, millis(), buf, MAX_NMEA2000_MESSAGE_SEASMART_SIZE) == 0) return; - socketServer.sendToClients(buf, N2K_CHANNEL_ID); + if (tcpChannel.sendSeaSmart()){ + socketServer.sendToClients(buf, N2K_CHANNEL_ID); + } } - if (actisenseReader && direction != N2KT_MSGACT && usbStream && usbSendActisens->asBoolean()) + if (actisenseReader && direction != N2KT_MSGACT && usbStream && actisenseChannel.canSendOut(n2kMsg.PGN)) { - countUSBOut.add(String(n2kMsg.PGN)); n2kMsg.SendInActisenseFormat(usbStream); } if (direction != N2KT_MSGOUT){ @@ -294,14 +251,11 @@ void handleN2kMessage(const tN2kMsg &n2kMsg,N2K_MsgDirection direction) }; void handleReceivedNmeaMessage(const char *buf, int sourceId){ - if (! checkFilter(buf,sourceId,true)) return; - updateNMEACounter(sourceId,buf,true); - if ( (sourceId >= MIN_USER_TASK) || - (sourceId == USB_CHANNEL_ID && n2kFromUSB->asBoolean())|| - (sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean())|| - (sourceId == SERIAL1_CHANNEL_ID && n2kFromSerial->asBoolean()) - ) + GwChannelConfig *channel=channelFromSource(sourceId); + if (channel && ! channel->canReceive(buf)) return; + if (! channel || channel->sendToN2K()){ toN2KConverter->parseAndSend(buf,sourceId); + } sendBufferToChannels(buf,sourceId); } @@ -474,12 +428,11 @@ protected: GwJsonDocument status(256 + countNMEA2KIn.getJsonSize()+ countNMEA2KOut.getJsonSize() + - countUSBIn.getJsonSize()+ - countUSBOut.getJsonSize()+ - countSerialIn.getJsonSize()+ - countSerialOut.getJsonSize()+ - countTCPIn.getJsonSize()+ - countTCPOut.getJsonSize() + usbChannel.getJsonSize()+ + tcpChannel.getJsonSize()+ + serialChannel.getJsonSize()+ + actisenseChannel.getJsonSize()+ + tclChannel.getJsonSize() ); status["version"] = VERSION; status["wifiConnected"] = gwWifi.clientConnected(); @@ -495,12 +448,11 @@ protected: //nmea0183Converter->toJson(status); countNMEA2KIn.toJson(status); countNMEA2KOut.toJson(status); - countUSBIn.toJson(status); - countUSBOut.toJson(status); - countSerialIn.toJson(status); - countSerialOut.toJson(status); - countTCPIn.toJson(status); - countTCPOut.toJson(status); + usbChannel.toJson(status); + actisenseChannel.toJson(status); + serialChannel.toJson(status); + tcpChannel.toJson(status); + tclChannel.toJson(status); serializeJson(status, result); } }; @@ -748,8 +700,8 @@ void setup() { if (serialMode != String("UNI")){ serialDirection=String(""); //if mode is UNI it depends on the selection - serCanRead=receiveSerial->asBoolean(); - serCanWrite=sendSerial->asBoolean(); + serCanRead=config.getBool(config.receiveSerial); + serCanWrite=config.getBool(config.sendSerial); } if (serialDirection == "receive" || serialDirection == "off" || serialMode == "RX") serCanWrite=false; if (serialDirection == "send" || serialDirection == "off" || serialMode == "TX") serCanRead=false; @@ -772,8 +724,54 @@ void setup() { // Start TCP server socketServer.begin(); logger.flush(); - - logger.logDebug(GwLog::LOG,"usbRead: %s", usbReadFilter.toString().c_str()); + usbChannel.begin(!config.getBool(config.usbActisense), + config.getBool(config.sendUsb), + config.getBool(config.receiveUsb), + config.getString(config.usbReadFilter), + config.getString(config.usbWriteFilter), + false, + config.getBool(config.usbToN2k)); + logger.logDebug(GwLog::LOG,"%s",usbChannel.toString().c_str()); + actisenseChannel.begin( + config.getBool(config.usbActisense), + config.getBool(config.usbActSend), + true, + "", + "", + false, + true + ); + logger.logDebug(GwLog::LOG,"ACT:%s",actisenseChannel.toString().c_str()); + tcpChannel.begin( + true, + config.getBool(config.sendTCP), + config.getBool(config.readTCP), + config.getString(config.tcpReadFilter), + config.getString(config.tcpWriteFilter), + config.getBool(config.sendSeasmart), + config.getBool(config.tcpToN2k) + ); + logger.logDebug(GwLog::LOG,"%s",tcpChannel.toString().c_str()); + serialChannel.begin( + serCanRead || serCanWrite, + serCanWrite, + serCanRead, + config.getString(config.serialReadF), + config.getString(config.serialWriteF), + false, + config.getBool(config.serialToN2k) + ); + logger.logDebug(GwLog::LOG,"%s",serialChannel.toString().c_str()); + tclChannel.begin( + config.getBool(config.tclEnabled), + config.getBool(config.sendTCL), + config.getBool(config.readTCL), + config.getString(config.tclReadFilter), + config.getString(config.tclReadFilter), + config.getBool(config.tclSeasmart), + config.getBool(config.tclToN2k) + ); + logger.logDebug(GwLog::LOG,"%s",tclChannel.toString().c_str()); logger.flush(); webserver.registerMainHandler("/api/reset", [](AsyncWebServerRequest *request)->GwRequestMessage *{ @@ -892,12 +890,12 @@ void setup() { } NMEA2000.ExtendTransmitMessages(pgns); NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns()); - if (usbActisense->asBoolean()){ + if (config.getBool(config.usbActisense)){ actisenseReader=new tActisenseReader(); usbStream=usbSerial->getStream(false); actisenseReader->SetReadStream(usbStream); actisenseReader->SetMsgHandler([](const tN2kMsg &msg){ - countUSBIn.add(String(msg.PGN)); + actisenseChannel.canReceive(msg.PGN); //count only handleN2kMessage(msg,N2KT_MSGACT); }); } @@ -1010,13 +1008,13 @@ void loop() { monitor.setTime(10); //read channels - if (readTCP->asBoolean()) socketServer.readMessages(&receiver); + if (tcpChannel.shouldRead()) socketServer.readMessages(&receiver); monitor.setTime(11); receiver.id=USB_CHANNEL_ID; - if (! actisenseReader && readUsb->asBoolean()) usbSerial->readMessages(&receiver); + if (usbChannel.shouldRead()) usbSerial->readMessages(&receiver); monitor.setTime(12); receiver.id=SERIAL1_CHANNEL_ID; - if (serial1 && serCanRead ) serial1->readMessages(&receiver); + if (serial1 && serialChannel.shouldRead() ) serial1->readMessages(&receiver); monitor.setTime(13); if (actisenseReader){ actisenseReader->ParseMessages();