add NMEA and NSk counter

This commit is contained in:
andreas 2021-11-07 13:12:38 +01:00
parent 2425006d7c
commit 3c4920d104
3 changed files with 252 additions and 60 deletions

61
lib/counter/GwCounter.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef _GWCOUNTER_H
#define _GWCOUNTER_H
#include <map>
#include "ArduinoJson.h"
template<class T> class GwCounter{
private:
typedef std::map<T,unsigned long> CounterMap;
CounterMap okCounter;
CounterMap failCounter;
unsigned long globalOk=0;
unsigned long globalFail=0;
String name;
public:
GwCounter(String name){
this->name=name;
};
void reset(){
okCounter.clear();
failCounter.clear();
globalFail=0;
globalOk=0;
}
void add(T key){
globalOk++;
auto it=okCounter.find(key);
if (it == okCounter.end()){
okCounter[key]=1;
}
else{
it->second++;
}
}
void addFail(T key){
globalFail++;
auto it=failCounter.find(key);
if (it == failCounter.end()){
failCounter[key]=1;
}
else{
it->second++;
}
}
int getJsonSize(){
return JSON_OBJECT_SIZE(4)+JSON_OBJECT_SIZE(okCounter.size()+1)+
JSON_OBJECT_SIZE(failCounter.size()+1);
}
void toJson(JsonDocument &json){
JsonObject jo=json.createNestedObject(name);
jo["sumOk"]=globalOk;
jo["sumFail"]=globalFail;
JsonObject jok=jo.createNestedObject("ok");
for (auto it=okCounter.begin();it!=okCounter.end();it++){
jok[String(it->first)]=it->second;
}
JsonObject jfail=jo.createNestedObject("fail");
for (auto it=failCounter.begin();it!=failCounter.end();it++){
jfail[String(it->first)]=it->second;
}
}
};
#endif

View File

@ -46,6 +46,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
#include "GwApi.h"
#include "GwButtons.h"
#include "GwLeds.h"
#include "GwCounter.h"
//NMEA message channels
@ -72,10 +73,6 @@ GwSocketServer socketServer(&config,&logger,MIN_TCP_CHANNEL_ID);
GwBoatData boatData(&logger);
//counter
int numCan=0;
int NodeAddress; // To store last Node Address
Preferences preferences; // Nonvolatile storage on ESP32 - To store LastDeviceAddress
@ -88,6 +85,41 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg,int id);
GwRequestQueue mainQueue(&logger,20);
GwWebServer webserver(&logger,&mainQueue,80);
GwCounter<unsigned long> countNMEA2KIn("count2Kin");
GwCounter<unsigned long> countNMEA2KOut("count2kout");
GwCounter<String> countUSBIn("countUSBin");
GwCounter<String> countUSBOut("countUSBout");
GwCounter<String> countTCPIn("countTCPin");
GwCounter<String> countTCPOut("countTCPout");
GwCounter<String> countSerialIn("countSerialIn");
GwCounter<String> countSerialOut("countSerialOut");
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<String> *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
@ -211,14 +243,30 @@ protected:
virtual void processRequest()
{
int numPgns = nmea0183Converter->numPgns();
DynamicJsonDocument status(256 + numPgns * 50);
status["numcan"] = numCan;
DynamicJsonDocument status(256 +
countNMEA2KIn.getJsonSize()+
countNMEA2KOut.getJsonSize() +
countUSBIn.getJsonSize()+
countUSBOut.getJsonSize()+
countSerialIn.getJsonSize()+
countSerialOut.getJsonSize()+
countTCPIn.getJsonSize()+
countTCPOut.getJsonSize()
);
status["version"] = VERSION;
status["wifiConnected"] = gwWifi.clientConnected();
status["clientIP"] = WiFi.localIP().toString();
status["numClients"] = socketServer.numClients();
status["apIp"] = gwWifi.apIP();
nmea0183Converter->toJson(status);
//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);
serializeJson(status, result);
}
};
@ -444,6 +492,7 @@ void setup() {
toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg)->bool{
logger.logDebug(GwLog::DEBUG+2,"send N2K %ld",msg.PGN);
countNMEA2KOut.add(msg.PGN);
NMEA2000.SendMsg(msg);
return true;
});
@ -494,7 +543,7 @@ void setup() {
NMEA2000.ExtendTransmitMessages(pgns);
NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns());
NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){
numCan++;
countNMEA2KIn.add(n2kMsg.PGN);
if ( sendSeasmart->asBoolean() ) {
char buf[MAX_NMEA2000_MESSAGE_SEASMART_SIZE];
if ( N2kToSeasmart(n2kMsg, millis(), buf, MAX_NMEA2000_MESSAGE_SEASMART_SIZE) == 0 ) return;
@ -519,12 +568,15 @@ void setup() {
void sendBufferToChannels(const char * buffer, int sourceId){
if (sendTCP->asBoolean() && checkFilter(buffer,MIN_TCP_CHANNEL_ID,false)){
socketServer.sendToClients(buffer,sourceId);
updateNMEACounter(MIN_TCP_CHANNEL_ID,buffer,false);
}
if (sendUsb->asBoolean() && checkFilter(buffer,USB_CHANNEL_ID,false)){
usbSerial->sendToClients(buffer,sourceId);
updateNMEACounter(USB_CHANNEL_ID,buffer,false);
}
if (serial1 && serCanWrite && checkFilter(buffer,SERIAL1_CHANNEL_ID,false)){
serial1->sendToClients(buffer,sourceId);
updateNMEACounter(SERIAL1_CHANNEL_ID,buffer,false);
}
}
@ -543,6 +595,7 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId) {
}
void handleReceivedNmeaMessage(const char *buf, int sourceId){
updateNMEACounter(sourceId,buf,true);
if (! checkFilter(buf,sourceId,true)) return;
if ((sourceId == USB_CHANNEL_ID && n2kFromUSB->asBoolean())||
(sourceId >= MIN_TCP_CHANNEL_ID && n2kFromTCP->asBoolean())||

View File

@ -15,6 +15,12 @@
if (parent)parent.appendChild(el);
return el;
}
function forEl(query,callback){
let all=document.querySelectorAll(query);
for (let i=0;i<all.length;i++){
callback(all[i]);
}
}
function alertRestart(){
reloadConfig=true;
alert("Board reset triggered, reconnect WLAN if necessary");
@ -41,8 +47,18 @@
getJson('/api/status')
.then(function(jsonData){
for (let k in jsonData){
if (k == 'cnv'){
updateCanDetails(jsonData[k]);
if (typeof(jsonData[k]) === 'object'){
for (let sk in jsonData[k]){
let key=k+"."+sk;
if (typeof(jsonData[k][sk]) === 'object'){
//msg details
updateMsgDetails(key,jsonData[k][sk]);
}
else{
let el=document.getElementById(key);
if (el) el.textContent=jsonData[k][sk];
}
}
}
else{
let el=document.getElementById(k);
@ -131,35 +147,23 @@
alertRestart();
})
}
function showCanDetails(on){
let el=document.getElementById('canDetails');
if (!el) return;
if (on) el.classList.add('visible');
else(el.classList).remove('visible');
}
function updateCanDetails(details){
let frame=document.getElementById('canDetails');
if (! frame) return;
function updateMsgDetails(key, details) {
forEl('.msgDetails', function (frame) {
if (frame.getAttribute('id') !== key) return;
for (let k in details) {
let el = frame.querySelector("[data-id=\"" + k + "\"] ");
if (!el) {
el=document.createElement('div');
el.classList.add('row');
let cv=document.createElement('span');
cv.classList.add('label');
cv.textContent="PGN"+k;
el.appendChild(cv);
cv=document.createElement('span');
cv.classList.add('value');
el = addEl('div','row',frame);
let cv = addEl('span','label',el,k);
cv = addEl('span','value',el,details[k]);
cv.setAttribute('data-id', k);
cv.textContent=details[k];
el.appendChild(cv);
frame.appendChild(el);
}
else {
el.textContent = details[k];
}
}
});
}
function showOverlay(text,isHtml){
let el=document.getElementById('overlayContent');
@ -451,9 +455,15 @@
let be=buttons[i];
be.onclick=window[be.id]; //assume a function with the button id
}
let cd=document.getElementById("showCanDetails");
forEl('.showMsgDetails',function(cd){
cd.addEventListener('change',function(ev){
showCanDetails(ev.target.checked);
let key=ev.target.getAttribute('data-key');
if (! key) return;
let el=document.getElementById(key);
if (!el) return;
if (ev.target.checked) el.classList.remove('hidden');
else(el.classList).add('hidden');
});
});
let tabs=document.querySelectorAll('.tab');
for (let i=0;i<tabs.length;i++){
@ -544,13 +554,13 @@ button.infoButton {
.hidden{
display: none !important;
}
#canDetails{
display:none;
.msgDetails .value {
width: 5em;
text-align: right;
}
#canDetails.visible{
display: block;
.msgDetails .label {
width: 5em;
}
.overlayContainer {
position: fixed;
top: 0;
@ -670,28 +680,96 @@ div#dashboardPage {
<span class="value" id="apIp">---</span>
</div>
<div class="row ">
<span class="label"># NMEA2000 messages</span>
<span class="value" id="numcan">---</span>
<span class="label">wifi client connected</span>
<span class="value" id="wifiConnected">---</span>
</div>
<div class="row even">
<span class="label">NMEA2000 details</span>
<input type="checkbox" id="showCanDetails"></span>
<span class="label">wifi client IP</span>
<span class="value" id="clientIP">---</span>
</div>
<div class="row even" id="canDetails">
<div class="row">
<span class="label"># NMEA2000 in</span>
<span class="value" id="count2Kin.sumOk">---</span>
</div>
<div class="row even">
<span class="label">NMEA2000 in details</span>
<input type="checkbox" class="showMsgDetails" data-key="count2Kin.ok"></span>
</div>
<div class="row even msgDetails hidden" id="count2Kin.ok" >
</div>
<div class="row">
<span class="label"># NMEA2000 out</span>
<span class="value" id="count2Kout.sumOk">---</span>
</div>
<div class="row even">
<span class="label">NMEA2000 out details</span>
<input type="checkbox" class="showMsgDetails" data-key="count2Kout.ok"></span>
</div>
<div class="row even msgDetails hidden" id="count2Kout.ok" >
</div>
<div class="row">
<span class="label"># TCP clients</span>
<span class="value" id="numClients">---</span>
</div>
<div class="row even">
<span class="label">wifi client connected</span>
<span class="value" id="wifiConnected">---</span>
<span class="label"># TCP in</span>
<span class="value" id="countTCPin.sumOk">---</span>
</div>
<div class="row ">
<span class="label">wifi client IP</span>
<span class="value" id="clientIP">---</span>
<span class="label">TCP in details</span>
<input type="checkbox" class="showMsgDetails" data-key="countTCPin.ok"></span>
</div>
<div class="row msgDetails hidden" id="countTCPin.ok" >
</div>
<div class="row even">
<span class="label"># TCP out</span>
<span class="value" id="countTCPout.sumOk">---</span>
</div>
<div class="row ">
<span class="label">TCP out details</span>
<input type="checkbox" class="showMsgDetails" data-key="countTCPout.ok"></span>
</div>
<div class="row msgDetails hidden" id="countTCPout.ok" >
</div>
<div class="row even">
<span class="label"># USB in</span>
<span class="value" id="countUSBin.sumOk">---</span>
</div>
<div class="row ">
<span class="label">USB in details</span>
<input type="checkbox" class="showMsgDetails" data-key="countUSBin.ok"></span>
</div>
<div class="row msgDetails hidden" id="countUSBin.ok" >
</div>
<div class="row even">
<span class="label"># USB out</span>
<span class="value" id="countUSBout.sumOk">---</span>
</div>
<div class="row ">
<span class="label">USB out details</span>
<input type="checkbox" class="showMsgDetails" data-key="countUSBout.ok"></span>
</div>
<div class="row msgDetails hidden" id="countUSBout.ok" >
</div>
<div class="row even">
<span class="label"># Serial in</span>
<span class="value" id="countSerialin.sumOk">---</span>
</div>
<div class="row ">
<span class="label">Serial in details</span>
<input type="checkbox" class="showMsgDetails" data-key="countSerialin.ok"></span>
</div>
<div class="row msgDetails hidden" id="countSerialin.ok" >
</div>
<div class="row even">
<span class="label"># Serial out</span>
<span class="value" id="countSerialout.sumOk">---</span>
</div>
<div class="row ">
<span class="label">Serial out details</span>
<input type="checkbox" class="showMsgDetails" data-key="countSerialout.ok"></span>
</div>
<div class="row msgDetails hidden" id="countSerialout.ok" >
</div>
<button id="reset" >Reset</button>
</div>