diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index ef5daf7..85ca067 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -116,26 +116,44 @@ int GwConfigHandler::getInt(const String name,int defaultv) const{ return i->asInt(); } +void GwNmeaFilter::handleToken(String token, int index){ + switch(index){ + case 0: + ais=token.toInt() != 0; + break; + case 1: + blacklist=token.toInt() != 0; + break; + case 2: + int found=0; + int last=0; + while ((found = token.indexOf(',',last)) >= 0){ + filter.push_back(token.substring(last,found)); + last=found+1; + } + if (last < token.length()){ + filter.push_back(token.substring(last)); + } + break; + } +} void GwNmeaFilter::parseFilter(){ + // "0:1:RMB,RMC" + // 0: AIS off, 1:whitelist, list of sentences if (isReady) return; int found=0; int last=0; + int index=0; String data=config->asString(); - while ((found = data.indexOf(',',last)) >= 0){ + 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); - } + handleToken(tok,index); last=found+1; + index++; } 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); - } - + handleToken(tok,index); } isReady=true; } @@ -144,27 +162,24 @@ 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; - } + if (buffer[0] == '!') return ais; + char sentence[4]; + strncpy(sentence,buffer+3,3); + sentence[3]=0; + for (auto it=filter.begin();it != filter.end();it++){ + if (strncmp(sentence,(*it).c_str(),3) == 0) return !blacklist; } - 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; - } + //if we have a whitelist we return false + //if nothing matches + return blacklist; +} +String GwNmeaFilter::toString(){ + parseFilter(); + String rt("NMEAFilter: "); + rt+="ais: "+String(ais); + rt+=", bl:"+String(blacklist); + for (auto it=filter.begin();it != filter.end();it++){ + rt+=","+*it; } - return !hasWhitelist; + return rt; } \ No newline at end of file diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index 307452c..babb54e 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -53,15 +53,18 @@ class GwNmeaFilter{ private: GwConfigInterface *config=NULL; bool isReady=false; - std::vector whitelist; - std::vector blacklist; + bool ais=true; + bool blacklist=true; + std::vector filter; + void handleToken(String token, int index); void parseFilter(); public: GwNmeaFilter(GwConfigInterface *config){ this->config=config; isReady=false; } - bool canPass(const char *buffer); + bool canPass(const char *buffer); + String toString(); }; diff --git a/src/main.cpp b/src/main.cpp index 3c83ee0..e46096b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -336,6 +336,9 @@ void setup() { socketServer.begin(); usbSerial->flush(); + logger.logDebug(GwLog::LOG,"usbRead: %s", usbReadFilter.toString().c_str()); + usbSerial->flush(); + webserver.registerMainHandler("/api/reset", [](AsyncWebServerRequest *request)->GwRequestMessage *{ return new ResetRequest(); }); diff --git a/web/config.json b/web/config.json index 8284413..5b24802 100644 --- a/web/config.json +++ b/web/config.json @@ -41,14 +41,14 @@ "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" + "description": "filter for NMEA0183 data when reading from USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" }, { "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" + "description": "filter for NMEA0183 data when writing to USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" }, { "name": "serialDirection", @@ -97,7 +97,7 @@ "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", + "description": "filter for NMEA0183 data when reading from serial\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", "capabilities":{"serialmode":["RX","BI","UNI"]} }, { @@ -105,7 +105,7 @@ "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", + "description": "filter for NMEA0183 data when writing to serial\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", "capabilities":{"serialmode":["TX","BI","UNI"]} }, { @@ -149,14 +149,14 @@ "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" + "description": "filter for NMEA0183 data when reading from TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" }, { "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" + "description": "filter for NMEA0183 data when writing to TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" }, { "name": "sendSeasmart", diff --git a/web/index.html b/web/index.html index c4c5a24..6df2586 100644 --- a/web/index.html +++ b/web/index.html @@ -58,7 +58,8 @@ let v=jsonData[k]; el.value=v; el.setAttribute('data-loaded',v); - checkChange(el); + let changeEvent=new Event('change'); + el.dispatchEvent(changeEvent); } } }); @@ -80,6 +81,9 @@ let values=document.querySelectorAll('.configForm select , .configForm input'); for (let i=0;i= 0) continue; let check=v.getAttribute('data-check'); if (check){ if (typeof(self[check]) === 'function'){ @@ -90,7 +94,7 @@ } } } - url+=v.getAttribute('name')+"="+encodeURIComponent(v.value)+"&"; + url+=name+"="+encodeURIComponent(v.value)+"&"; } getJson(url) .then(function(status){ @@ -152,18 +156,18 @@ let container=document.getElementById('overlayContainer'); container.classList.add('hidden'); } - function checkChange(el) { + function checkChange(el,row) { let loaded = el.getAttribute('data-loaded'); if (loaded !== undefined) { if (loaded != el.value) { - el.classList.add('changed'); + row.classList.add('changed'); } else { - el.classList.remove("changed"); + row.classList.remove("changed"); } } } - function createInput(configItem){ + function createInput(configItem,frame){ let el; if (configItem.type === 'boolean' || configItem.type === 'list'){ el=document.createElement('select') @@ -184,8 +188,56 @@ sitemEl.textContent=sitem.l; el.appendChild(sitemEl); }) + frame.appendChild(el); return el; } + if (configItem.type === 'filter'){ + el=document.createElement('div'); + el.classList.add('filter'); + let ais=createInput({ + type:'list', + name:configItem.name+"_ais", + list:['aison','aisoff'] + },el); + let mode=createInput({ + type:'list', + name:configItem.name+"_mode", + list: ['whitelist','blacklist'] + },el); + let sentences=createInput({ + type:'text', + name:configItem.name+"_sentences", + },el); + let data=document.createElement('input'); + data.setAttribute('type','hidden'); + el.appendChild(data); + let changeFunction=function(){ + let cv=data.value||""; + let parts=cv.split(":"); + ais.value=(parts[0]=='0')?"aisoff":"aison"; + mode.value=(parts[1]=='0')?"whitelist":"blacklist"; + sentences.value=parts[2]||""; + } + let updateFunction=function(){ + let nv=(ais.value == 'aison')?"1":"0"; + nv+=":"; + nv+=(mode.value == 'blacklist')?"1":"0"; + nv+=":"; + nv+=sentences.value; + data.value=nv; + let chev=new Event('change'); + data.dispatchEvent(chev); + } + mode.addEventListener('change',updateFunction); + ais.addEventListener("change",updateFunction); + sentences.addEventListener("change",updateFunction); + data.addEventListener('change',function(ev){ + changeFunction(); + }); + data.setAttribute('name',configItem.name); + frame.appendChild(el); + return data; + } el=document.createElement('input'); el.setAttribute('name',configItem.name) if (configItem.type === 'password'){ @@ -197,6 +249,7 @@ else{ el.setAttribute('type','text'); } + frame.appendChild(el); return el; } let configDefinitions; @@ -229,21 +282,21 @@ labelEl.classList.add('label'); labelEl.textContent = label; row.appendChild(labelEl); - let valueEl = createInput(item); + let valueEl = createInput(item,row); if (!valueEl) return; valueEl.setAttribute('data-default', item.default); valueEl.addEventListener('change', function (ev) { let el = ev.target; - checkChange(el); + checkChange(el,row); }) if (item.check) valueEl.setAttribute('data-check', item.check); - row.appendChild(valueEl); let bt = document.createElement('button'); bt.classList.add('defaultButton'); bt.setAttribute('data-default', item.default); bt.addEventListener('click', function (ev) { valueEl.value = valueEl.getAttribute('data-default'); - checkChange(valueEl); + let changeEvent=new Event('change'); + valueEl.dispatchEvent(changeEvent); }) bt.textContent = "X"; row.appendChild(bt); @@ -284,7 +337,10 @@ border: 1px solid grey; max-width: 40em; } -.changed { +.changed input{ + color: green +} +.changed select{ color: green } span.label { @@ -305,6 +361,9 @@ span#connected.ok{ .row { margin: 0.5em; } +.filter { + display: inline-block; +} .buttons { padding-left: 1em; }