introduce NMEA filter

This commit is contained in:
andreas 2021-11-03 19:36:28 +01:00
parent 2028525cc9
commit 3cf67d387e
5 changed files with 183 additions and 4 deletions

View File

@ -1,5 +1,6 @@
#include "GWConfig.h"
#include <ArduinoJson.h>
#include <string.h>
#define B(v) (v?"true":"false")
@ -114,3 +115,56 @@ int GwConfigHandler::getInt(const String name,int defaultv) const{
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;
}

View File

@ -1,6 +1,7 @@
#ifndef _GWCONFIGITEM_H
#define _GWCONFIGITEM_H
#include "WString.h"
#include <vector>
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<String> whitelist;
std::vector<String> blacklist;
void parseFilter();
public:
GwNmeaFilter(GwConfigInterface *config){
this->config=config;
isReady=false;
}
bool canPass(const char *buffer);
};
#endif

View File

@ -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())

View File

@ -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",

View File

@ -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;
}
</style>
</head>
<body>
@ -362,6 +400,16 @@ button.infoButton {
</div>
</div>
</div>
<div class="overlayContainer hidden" id="overlayContainer">
<div id="overlay">
<div id="overlayContent">
AHA
</div>
<div class="overlayButtons">
<button id="hideOverlay">Close</button>
</div>
</div>
</div>
</body>
</html>