introduce NMEA filter
This commit is contained in:
parent
2028525cc9
commit
3cf67d387e
|
@ -1,5 +1,6 @@
|
||||||
#include "GWConfig.h"
|
#include "GWConfig.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define B(v) (v?"true":"false")
|
#define B(v) (v?"true":"false")
|
||||||
|
|
||||||
|
@ -113,4 +114,57 @@ int GwConfigHandler::getInt(const String name,int defaultv) const{
|
||||||
GwConfigInterface *i=getConfigItem(name,false);
|
GwConfigInterface *i=getConfigItem(name,false);
|
||||||
if (!i) return defaultv;
|
if (!i) return defaultv;
|
||||||
return i->asInt();
|
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;
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef _GWCONFIGITEM_H
|
#ifndef _GWCONFIGITEM_H
|
||||||
#define _GWCONFIGITEM_H
|
#define _GWCONFIGITEM_H
|
||||||
#include "WString.h"
|
#include "WString.h"
|
||||||
|
#include <vector>
|
||||||
class GwConfigInterface{
|
class GwConfigInterface{
|
||||||
public:
|
public:
|
||||||
virtual String asString() const=0;
|
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
|
#endif
|
23
src/main.cpp
23
src/main.cpp
|
@ -92,6 +92,22 @@ GwConfigInterface *n2kFromUSB=config.getConfigItem(config.usbToN2k,true);
|
||||||
GwConfigInterface *receiveSerial=config.getConfigItem(config.receiveSerial,true);
|
GwConfigInterface *receiveSerial=config.getConfigItem(config.receiveSerial,true);
|
||||||
GwConfigInterface *sendSerial=config.getConfigItem(config.sendSerial,true);
|
GwConfigInterface *sendSerial=config.getConfigItem(config.sendSerial,true);
|
||||||
GwConfigInterface *n2kFromSerial=config.getConfigItem(config.serialToN2k,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 serCanWrite=true;
|
||||||
bool serCanRead=true;
|
bool serCanRead=true;
|
||||||
|
@ -424,13 +440,13 @@ void setup() {
|
||||||
|
|
||||||
|
|
||||||
void sendBufferToChannels(const char * buffer, int sourceId){
|
void sendBufferToChannels(const char * buffer, int sourceId){
|
||||||
if (sendTCP->asBoolean()){
|
if (sendTCP->asBoolean() && checkFilter(buffer,MIN_TCP_CHANNEL_ID,false)){
|
||||||
socketServer.sendToClients(buffer,sourceId);
|
socketServer.sendToClients(buffer,sourceId);
|
||||||
}
|
}
|
||||||
if (sendUsb->asBoolean()){
|
if (sendUsb->asBoolean() && checkFilter(buffer,USB_CHANNEL_ID,false)){
|
||||||
usbSerial->sendToClients(buffer,sourceId);
|
usbSerial->sendToClients(buffer,sourceId);
|
||||||
}
|
}
|
||||||
if (serial1 && serCanWrite){
|
if (serial1 && serCanWrite && checkFilter(buffer,SERIAL1_CHANNEL_ID,false)){
|
||||||
serial1->sendToClients(buffer,sourceId);
|
serial1->sendToClients(buffer,sourceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -450,6 +466,7 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleReceivedNmeaMessage(const char *buf, int sourceId){
|
void handleReceivedNmeaMessage(const char *buf, int sourceId){
|
||||||
|
if (! checkFilter(buf,sourceId,true)) return;
|
||||||
if ((sourceId == USB_CHANNEL_ID && n2kFromUSB->asBoolean())||
|
if ((sourceId == USB_CHANNEL_ID && n2kFromUSB->asBoolean())||
|
||||||
(sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean())||
|
(sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean())||
|
||||||
(sourceId == SERIAL1_CHANNEL_ID && n2kFromSerial->asBoolean())
|
(sourceId == SERIAL1_CHANNEL_ID && n2kFromSerial->asBoolean())
|
||||||
|
|
|
@ -36,6 +36,20 @@
|
||||||
"default": "true",
|
"default": "true",
|
||||||
"description": "convert NMEA0183 from the USB port to NMEA2000"
|
"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",
|
"name": "serialDirection",
|
||||||
"label": "serial direction",
|
"label": "serial direction",
|
||||||
|
@ -78,6 +92,22 @@
|
||||||
"description": "convert NMEA0183 from the serial port to NMEA2000",
|
"description": "convert NMEA0183 from the serial port to NMEA2000",
|
||||||
"capabilities":{"serialmode":["RX","BI","UNI"]}
|
"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",
|
"name": "serverPort",
|
||||||
"label": "TCP port",
|
"label": "TCP port",
|
||||||
|
@ -114,6 +144,20 @@
|
||||||
"default": "true",
|
"default": "true",
|
||||||
"description": "convert NMEA0183 from TCP clients to NMEA2000"
|
"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",
|
"name": "sendSeasmart",
|
||||||
"label": "Seasmart to TCP",
|
"label": "Seasmart to TCP",
|
||||||
|
|
|
@ -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) {
|
function checkChange(el) {
|
||||||
let loaded = el.getAttribute('data-loaded');
|
let loaded = el.getAttribute('data-loaded');
|
||||||
if (loaded !== undefined) {
|
if (loaded !== undefined) {
|
||||||
|
@ -240,7 +250,7 @@
|
||||||
bt = document.createElement('button');
|
bt = document.createElement('button');
|
||||||
bt.classList.add('infoButton');
|
bt.classList.add('infoButton');
|
||||||
bt.addEventListener('click', function (ev) {
|
bt.addEventListener('click', function (ev) {
|
||||||
alert(item.description);
|
showOverlay(item.description);
|
||||||
});
|
});
|
||||||
bt.textContent = "?";
|
bt.textContent = "?";
|
||||||
row.appendChild(bt);
|
row.appendChild(bt);
|
||||||
|
@ -309,6 +319,34 @@ button.infoButton {
|
||||||
display: block;
|
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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -362,6 +400,16 @@ button.infoButton {
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue