add NMEA and NSk counter
This commit is contained in:
parent
2425006d7c
commit
3c4920d104
|
@ -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
|
69
src/main.cpp
69
src/main.cpp
|
@ -46,6 +46,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
|
||||||
#include "GwApi.h"
|
#include "GwApi.h"
|
||||||
#include "GwButtons.h"
|
#include "GwButtons.h"
|
||||||
#include "GwLeds.h"
|
#include "GwLeds.h"
|
||||||
|
#include "GwCounter.h"
|
||||||
|
|
||||||
|
|
||||||
//NMEA message channels
|
//NMEA message channels
|
||||||
|
@ -72,10 +73,6 @@ GwSocketServer socketServer(&config,&logger,MIN_TCP_CHANNEL_ID);
|
||||||
GwBoatData boatData(&logger);
|
GwBoatData boatData(&logger);
|
||||||
|
|
||||||
|
|
||||||
//counter
|
|
||||||
int numCan=0;
|
|
||||||
|
|
||||||
|
|
||||||
int NodeAddress; // To store last Node Address
|
int NodeAddress; // To store last Node Address
|
||||||
|
|
||||||
Preferences preferences; // Nonvolatile storage on ESP32 - To store LastDeviceAddress
|
Preferences preferences; // Nonvolatile storage on ESP32 - To store LastDeviceAddress
|
||||||
|
@ -88,6 +85,41 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg,int id);
|
||||||
GwRequestQueue mainQueue(&logger,20);
|
GwRequestQueue mainQueue(&logger,20);
|
||||||
GwWebServer webserver(&logger,&mainQueue,80);
|
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
|
//configs that we need in main
|
||||||
|
|
||||||
|
|
||||||
|
@ -211,14 +243,30 @@ protected:
|
||||||
virtual void processRequest()
|
virtual void processRequest()
|
||||||
{
|
{
|
||||||
int numPgns = nmea0183Converter->numPgns();
|
int numPgns = nmea0183Converter->numPgns();
|
||||||
DynamicJsonDocument status(256 + numPgns * 50);
|
DynamicJsonDocument status(256 +
|
||||||
status["numcan"] = numCan;
|
countNMEA2KIn.getJsonSize()+
|
||||||
|
countNMEA2KOut.getJsonSize() +
|
||||||
|
countUSBIn.getJsonSize()+
|
||||||
|
countUSBOut.getJsonSize()+
|
||||||
|
countSerialIn.getJsonSize()+
|
||||||
|
countSerialOut.getJsonSize()+
|
||||||
|
countTCPIn.getJsonSize()+
|
||||||
|
countTCPOut.getJsonSize()
|
||||||
|
);
|
||||||
status["version"] = VERSION;
|
status["version"] = VERSION;
|
||||||
status["wifiConnected"] = gwWifi.clientConnected();
|
status["wifiConnected"] = gwWifi.clientConnected();
|
||||||
status["clientIP"] = WiFi.localIP().toString();
|
status["clientIP"] = WiFi.localIP().toString();
|
||||||
status["numClients"] = socketServer.numClients();
|
status["numClients"] = socketServer.numClients();
|
||||||
status["apIp"] = gwWifi.apIP();
|
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);
|
serializeJson(status, result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -444,6 +492,7 @@ void setup() {
|
||||||
|
|
||||||
toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg)->bool{
|
toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg)->bool{
|
||||||
logger.logDebug(GwLog::DEBUG+2,"send N2K %ld",msg.PGN);
|
logger.logDebug(GwLog::DEBUG+2,"send N2K %ld",msg.PGN);
|
||||||
|
countNMEA2KOut.add(msg.PGN);
|
||||||
NMEA2000.SendMsg(msg);
|
NMEA2000.SendMsg(msg);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -494,7 +543,7 @@ void setup() {
|
||||||
NMEA2000.ExtendTransmitMessages(pgns);
|
NMEA2000.ExtendTransmitMessages(pgns);
|
||||||
NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns());
|
NMEA2000.ExtendReceiveMessages(nmea0183Converter->handledPgns());
|
||||||
NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){
|
NMEA2000.SetMsgHandler([](const tN2kMsg &n2kMsg){
|
||||||
numCan++;
|
countNMEA2KIn.add(n2kMsg.PGN);
|
||||||
if ( sendSeasmart->asBoolean() ) {
|
if ( sendSeasmart->asBoolean() ) {
|
||||||
char buf[MAX_NMEA2000_MESSAGE_SEASMART_SIZE];
|
char buf[MAX_NMEA2000_MESSAGE_SEASMART_SIZE];
|
||||||
if ( N2kToSeasmart(n2kMsg, millis(), buf, MAX_NMEA2000_MESSAGE_SEASMART_SIZE) == 0 ) return;
|
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){
|
void sendBufferToChannels(const char * buffer, int sourceId){
|
||||||
if (sendTCP->asBoolean() && checkFilter(buffer,MIN_TCP_CHANNEL_ID,false)){
|
if (sendTCP->asBoolean() && checkFilter(buffer,MIN_TCP_CHANNEL_ID,false)){
|
||||||
socketServer.sendToClients(buffer,sourceId);
|
socketServer.sendToClients(buffer,sourceId);
|
||||||
|
updateNMEACounter(MIN_TCP_CHANNEL_ID,buffer,false);
|
||||||
}
|
}
|
||||||
if (sendUsb->asBoolean() && checkFilter(buffer,USB_CHANNEL_ID,false)){
|
if (sendUsb->asBoolean() && checkFilter(buffer,USB_CHANNEL_ID,false)){
|
||||||
usbSerial->sendToClients(buffer,sourceId);
|
usbSerial->sendToClients(buffer,sourceId);
|
||||||
|
updateNMEACounter(USB_CHANNEL_ID,buffer,false);
|
||||||
}
|
}
|
||||||
if (serial1 && serCanWrite && checkFilter(buffer,SERIAL1_CHANNEL_ID,false)){
|
if (serial1 && serCanWrite && checkFilter(buffer,SERIAL1_CHANNEL_ID,false)){
|
||||||
serial1->sendToClients(buffer,sourceId);
|
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){
|
void handleReceivedNmeaMessage(const char *buf, int sourceId){
|
||||||
|
updateNMEACounter(sourceId,buf,true);
|
||||||
if (! checkFilter(buf,sourceId,true)) return;
|
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())||
|
||||||
|
|
158
web/index.html
158
web/index.html
|
@ -15,6 +15,12 @@
|
||||||
if (parent)parent.appendChild(el);
|
if (parent)parent.appendChild(el);
|
||||||
return 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(){
|
function alertRestart(){
|
||||||
reloadConfig=true;
|
reloadConfig=true;
|
||||||
alert("Board reset triggered, reconnect WLAN if necessary");
|
alert("Board reset triggered, reconnect WLAN if necessary");
|
||||||
|
@ -41,8 +47,18 @@
|
||||||
getJson('/api/status')
|
getJson('/api/status')
|
||||||
.then(function(jsonData){
|
.then(function(jsonData){
|
||||||
for (let k in jsonData){
|
for (let k in jsonData){
|
||||||
if (k == 'cnv'){
|
if (typeof(jsonData[k]) === 'object'){
|
||||||
updateCanDetails(jsonData[k]);
|
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{
|
else{
|
||||||
let el=document.getElementById(k);
|
let el=document.getElementById(k);
|
||||||
|
@ -131,35 +147,23 @@
|
||||||
alertRestart();
|
alertRestart();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function showCanDetails(on){
|
|
||||||
let el=document.getElementById('canDetails');
|
function updateMsgDetails(key, details) {
|
||||||
if (!el) return;
|
forEl('.msgDetails', function (frame) {
|
||||||
if (on) el.classList.add('visible');
|
if (frame.getAttribute('id') !== key) return;
|
||||||
else(el.classList).remove('visible');
|
|
||||||
}
|
|
||||||
function updateCanDetails(details){
|
|
||||||
let frame=document.getElementById('canDetails');
|
|
||||||
if (! frame) return;
|
|
||||||
for (let k in details) {
|
for (let k in details) {
|
||||||
let el = frame.querySelector("[data-id=\"" + k + "\"] ");
|
let el = frame.querySelector("[data-id=\"" + k + "\"] ");
|
||||||
if (!el) {
|
if (!el) {
|
||||||
el=document.createElement('div');
|
el = addEl('div','row',frame);
|
||||||
el.classList.add('row');
|
let cv = addEl('span','label',el,k);
|
||||||
let cv=document.createElement('span');
|
cv = addEl('span','value',el,details[k]);
|
||||||
cv.classList.add('label');
|
|
||||||
cv.textContent="PGN"+k;
|
|
||||||
el.appendChild(cv);
|
|
||||||
cv=document.createElement('span');
|
|
||||||
cv.classList.add('value');
|
|
||||||
cv.setAttribute('data-id', k);
|
cv.setAttribute('data-id', k);
|
||||||
cv.textContent=details[k];
|
|
||||||
el.appendChild(cv);
|
|
||||||
frame.appendChild(el);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
el.textContent = details[k];
|
el.textContent = details[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function showOverlay(text,isHtml){
|
function showOverlay(text,isHtml){
|
||||||
let el=document.getElementById('overlayContent');
|
let el=document.getElementById('overlayContent');
|
||||||
|
@ -451,9 +455,15 @@
|
||||||
let be=buttons[i];
|
let be=buttons[i];
|
||||||
be.onclick=window[be.id]; //assume a function with the button id
|
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){
|
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');
|
let tabs=document.querySelectorAll('.tab');
|
||||||
for (let i=0;i<tabs.length;i++){
|
for (let i=0;i<tabs.length;i++){
|
||||||
|
@ -544,13 +554,13 @@ button.infoButton {
|
||||||
.hidden{
|
.hidden{
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
#canDetails{
|
.msgDetails .value {
|
||||||
display:none;
|
width: 5em;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
#canDetails.visible{
|
.msgDetails .label {
|
||||||
display: block;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayContainer {
|
.overlayContainer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -670,28 +680,96 @@ div#dashboardPage {
|
||||||
<span class="value" id="apIp">---</span>
|
<span class="value" id="apIp">---</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row ">
|
<div class="row ">
|
||||||
<span class="label"># NMEA2000 messages</span>
|
<span class="label">wifi client connected</span>
|
||||||
<span class="value" id="numcan">---</span>
|
<span class="value" id="wifiConnected">---</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row even">
|
<div class="row even">
|
||||||
<span class="label">NMEA2000 details</span>
|
<span class="label">wifi client IP</span>
|
||||||
<input type="checkbox" id="showCanDetails"></span>
|
<span class="value" id="clientIP">---</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
<div class="row even" id="canDetails">
|
<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>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<span class="label"># TCP clients</span>
|
<span class="label"># TCP clients</span>
|
||||||
<span class="value" id="numClients">---</span>
|
<span class="value" id="numClients">---</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row even">
|
<div class="row even">
|
||||||
<span class="label">wifi client connected</span>
|
<span class="label"># TCP in</span>
|
||||||
<span class="value" id="wifiConnected">---</span>
|
<span class="value" id="countTCPin.sumOk">---</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row ">
|
<div class="row ">
|
||||||
<span class="label">wifi client IP</span>
|
<span class="label">TCP in details</span>
|
||||||
<span class="value" id="clientIP">---</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>
|
</div>
|
||||||
<button id="reset" >Reset</button>
|
<button id="reset" >Reset</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue