diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index 2fd4801..ef5daf7 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -1,5 +1,6 @@ #include "GWConfig.h" #include +#include #define B(v) (v?"true":"false") @@ -113,4 +114,57 @@ int GwConfigHandler::getInt(const String name,int defaultv) const{ GwConfigInterface *i=getConfigItem(name,false); if (!i) return defaultv; return i->asInt(); +} + +void GwNmeaFilter::parseFilter(){ + if (isReady) return; + int found=0; + int last=0; + String data=config->asString(); + while ((found = data.indexOf(',',last)) >= 0){ + String tok=data.substring(last,found); + if (tok != ""){ + if (tok.startsWith("^")) blacklist.push_back(tok); + else whitelist.push_back(tok); + } + last=found+1; + } + if (last < data.length()){ + String tok=data.substring(last); + if (tok != "" && tok != "^" ){ + if (tok.startsWith("^")) blacklist.push_back(tok.substring(1)); + else whitelist.push_back(tok); + } + + } + isReady=true; +} + +bool GwNmeaFilter::canPass(const char *buffer){ + size_t len=strlen(buffer); + if (len < 5) return false; //invalid NMEA + if (!isReady) parseFilter(); + bool hasWhitelist=false; + for (auto it=blacklist.begin();it != blacklist.end();it++){ + if (buffer[0] == '$'){ + if ((strncmp(buffer,(*it).c_str(),1) == 0) && + (strncmp(buffer+3,(*it).c_str()+1,it->length()-1) == 0) + ) return false; + } + else{ + if (strncmp(buffer,(*it).c_str(),it->length()) == 0) return false; + } + } + for (auto it=whitelist.begin();it != whitelist.end();it++){ + hasWhitelist=true; + if (buffer[0] == '$'){ + if ((strncmp(buffer,(*it).c_str(),1) == 0) && + (strncmp(buffer+3,(*it).c_str()+1,it->length()-1) == 0) + ) return true; + } + else{ + if (strncmp(buffer,(*it).c_str(),it->length()) == 0) return true; + } + } + return !hasWhitelist; } \ No newline at end of file diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index 528a1ce..307452c 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -1,6 +1,7 @@ #ifndef _GWCONFIGITEM_H #define _GWCONFIGITEM_H #include "WString.h" +#include class GwConfigInterface{ public: virtual String asString() const=0; @@ -48,5 +49,20 @@ class GwConfigItem: public GwConfigInterface{ } }; +class GwNmeaFilter{ + private: + GwConfigInterface *config=NULL; + bool isReady=false; + std::vector whitelist; + std::vector blacklist; + void parseFilter(); + public: + GwNmeaFilter(GwConfigInterface *config){ + this->config=config; + isReady=false; + } + bool canPass(const char *buffer); +}; + #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a1f8bbc..3c83ee0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,6 +92,22 @@ 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.serialReadFilter,true)); +GwNmeaFilter serialWriteFilter(config.getConfigItem(config.serialWriteFilter,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); +} bool serCanWrite=true; bool serCanRead=true; @@ -424,13 +440,13 @@ void setup() { void sendBufferToChannels(const char * buffer, int sourceId){ - if (sendTCP->asBoolean()){ + if (sendTCP->asBoolean() && checkFilter(buffer,MIN_TCP_CHANNEL_ID,false)){ socketServer.sendToClients(buffer,sourceId); } - if (sendUsb->asBoolean()){ + if (sendUsb->asBoolean() && checkFilter(buffer,USB_CHANNEL_ID,false)){ usbSerial->sendToClients(buffer,sourceId); } - if (serial1 && serCanWrite){ + if (serial1 && serCanWrite && checkFilter(buffer,SERIAL1_CHANNEL_ID,false)){ serial1->sendToClients(buffer,sourceId); } } @@ -450,6 +466,7 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId) { } void handleReceivedNmeaMessage(const char *buf, int sourceId){ + if (! checkFilter(buf,sourceId,true)) return; if ((sourceId == USB_CHANNEL_ID && n2kFromUSB->asBoolean())|| (sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean())|| (sourceId == SERIAL1_CHANNEL_ID && n2kFromSerial->asBoolean()) diff --git a/web/config.json b/web/config.json index 906ff2d..8284413 100644 --- a/web/config.json +++ b/web/config.json @@ -36,6 +36,20 @@ "default": "true", "description": "convert NMEA0183 from the USB port to NMEA2000" }, + { + "name": "usbReadFilter", + "label": "USB read Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when reading from USB\nempty to let all data pass\nexamples:\n! \t: all AIS\n$RMB,$APB \t: only NMEA RMB and APB\n^! \t: no AIS but other NMEA" + }, + { + "name": "usbWriteFilter", + "label": "USB write Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when writing to USB\nempty to let all data pass\nexamples:\n! \t: all AIS\n$RMB,$APB \t: only NMEA RMB and APB\n^! \t: no AIS but other NMEA" + }, { "name": "serialDirection", "label": "serial direction", @@ -78,6 +92,22 @@ "description": "convert NMEA0183 from the serial port to NMEA2000", "capabilities":{"serialmode":["RX","BI","UNI"]} }, + { + "name": "serialReadFilter", + "label": "serial read Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when reading from serial\nempty to let all data pass\nexamples:\n! \t: all AIS\n$RMB,$APB \t: only NMEA RMB and APB\n^! \t: no AIS but other NMEA", + "capabilities":{"serialmode":["RX","BI","UNI"]} + }, + { + "name": "serialWriteFilter", + "label": "serial write Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when writing to serial\nempty to let all data pass\nexamples:\n! \t: all AIS\n$RMB,$APB \t: only NMEA RMB and APB\n^! \t: no AIS but other NMEA", + "capabilities":{"serialmode":["TX","BI","UNI"]} + }, { "name": "serverPort", "label": "TCP port", @@ -114,6 +144,20 @@ "default": "true", "description": "convert NMEA0183 from TCP clients to NMEA2000" }, + { + "name": "tcpReadFilter", + "label": "TCP read Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when reading from TCP\nempty to let all data pass\nexamples:\n! \t: all AIS\n$RMB,$APB \t: only NMEA RMB and APB\n^! \t: no AIS but other NMEA" + }, + { + "name": "tcpWriteFilter", + "label": "TCP write Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when writing to TCP\nempty to let all data pass\nexamples:\n! \t: all AIS\n$RMB,$APB \t: only NMEA RMB and APB\n^! \t: no AIS but other NMEA" + }, { "name": "sendSeasmart", "label": "Seasmart to TCP", diff --git a/web/index.html b/web/index.html index b778deb..c4c5a24 100644 --- a/web/index.html +++ b/web/index.html @@ -142,6 +142,16 @@ } } } + function showOverlay(text){ + let el=document.getElementById('overlayContent'); + el.textContent=text; + let container=document.getElementById('overlayContainer'); + container.classList.remove('hidden'); + } + function hideOverlay(){ + let container=document.getElementById('overlayContainer'); + container.classList.add('hidden'); + } function checkChange(el) { let loaded = el.getAttribute('data-loaded'); if (loaded !== undefined) { @@ -240,7 +250,7 @@ bt = document.createElement('button'); bt.classList.add('infoButton'); bt.addEventListener('click', function (ev) { - alert(item.description); + showOverlay(item.description); }); bt.textContent = "?"; row.appendChild(bt); @@ -309,6 +319,34 @@ button.infoButton { display: block; } +.overlayContainer { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: #80808070; + display: flex; +} +.overlayContainer.hidden{ + display: none; +} +div#overlay { + margin: auto; + background-color: white; + padding: 0.5em; +} +div#overlayContent { + padding: 0.5em; + white-space: pre; +} +.overlayButtons { + border-top: 1px solid grey; + padding-top: 0.5em; + display: flex; + flex-direction: row; + justify-content: end; +} @@ -362,6 +400,16 @@ button.infoButton { + +