Merge branch 'wellenvogel:master' into feature/env2
This commit is contained in:
commit
0bc071f856
lib
tools
web
|
@ -10,6 +10,16 @@ GwBoatData::~GwBoatData(){
|
|||
}
|
||||
}
|
||||
|
||||
template<class T> GwBoatItem<T> *GwBoatData::getOrCreate(T dummy, String name, String format,
|
||||
unsigned long invalidTime)
|
||||
{
|
||||
for (auto it=values.begin();it != values.end();it++){
|
||||
if ((*it)->getName() == name){
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
return new GwBoatItem<T>(name,format,invalidTime,&values);
|
||||
}
|
||||
String GwBoatData::toJson() const {
|
||||
unsigned long minTime=millis();
|
||||
GwBoatItemBase::GwBoatItemMap::const_iterator it;
|
||||
|
|
|
@ -55,6 +55,7 @@ class GwBoatItemBase{
|
|||
virtual size_t getJsonSize(){return JSON_OBJECT_SIZE(15);}
|
||||
virtual int getLastSource()=0;
|
||||
virtual void refresh(unsigned long ts=0){uls(ts);}
|
||||
String getName(){return name;}
|
||||
};
|
||||
class GwBoatData;
|
||||
template<class T> class GwBoatItem : public GwBoatItemBase{
|
||||
|
@ -253,8 +254,43 @@ class GwBoatData{
|
|||
public:
|
||||
GwBoatData(GwLog *logger);
|
||||
~GwBoatData();
|
||||
template<class T> GwBoatItem<T> *getOrCreate(T dummy,String name,String format,
|
||||
unsigned long invalidTime=GwBoatItemBase::INVALID_TIME);
|
||||
String toJson() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* class for lazy creation of a boat item
|
||||
* once we have someone that knows the name for it
|
||||
* and providing fast access without searching all the time trough the map
|
||||
* xdr mappings implement such a provider
|
||||
*/
|
||||
class GwBoatItemNameProvider
|
||||
{
|
||||
public:
|
||||
virtual String getBoatItemName() = 0;
|
||||
virtual String getBoatItemFormat() = 0;
|
||||
virtual ~GwBoatItemNameProvider() {}
|
||||
};
|
||||
template<class T> class GwBoatItemHolder{
|
||||
private:
|
||||
GwBoatItem<T> *item=NULL;
|
||||
GwBoatData *data;
|
||||
unsigned long invalidTime=GwBoatItemBase::INVALID_TIME;
|
||||
public:
|
||||
GwBoatItemHolder(GwBoatData *data,unsigned long invalidTime=GwBoatItemBase::INVALID_TIME){
|
||||
this->data=data;
|
||||
this->invalidTime=invalidTime;
|
||||
}
|
||||
GwBoatItem<T> *getItem(GwBoatItemNameProvider *provider){
|
||||
if (item) return item;
|
||||
T dummy;
|
||||
item=data->getOrCreate(dummy,
|
||||
provider->getBoatItemName() ,
|
||||
provider->getBoatItemFormat(),
|
||||
invalidTime);
|
||||
return item;
|
||||
}
|
||||
GwBoatItem<T> *getItem(){return item;}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
#define _GWXDRMAPPINGS_H
|
||||
#include "GwLog.h"
|
||||
#include "GWConfig.h"
|
||||
#include "GwBoatData.h"
|
||||
#include <WString.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
@ -141,7 +142,7 @@ class GwXDRMapping{
|
|||
typedef std::map<String,MappingList> N138Map;
|
||||
typedef std::map<unsigned long,MappingList> N2KMap;
|
||||
};
|
||||
class GwXDRFoundMapping{
|
||||
class GwXDRFoundMapping : public GwBoatItemNameProvider{
|
||||
public:
|
||||
GwXDRMappingDef *definition=NULL;
|
||||
GwXDRType *type=NULL;
|
||||
|
@ -163,6 +164,14 @@ class GwXDRFoundMapping{
|
|||
return definition->getTransducerName(instanceId);
|
||||
}
|
||||
String buildXdrEntry(double value);
|
||||
//boat Data info
|
||||
virtual String getBoatItemName(){
|
||||
return getTransducerName();
|
||||
};
|
||||
virtual String getBoatItemFormat(){
|
||||
return "formatXdr"+type->xdrunit; //TODO: use the type def for the correct format
|
||||
};
|
||||
virtual ~GwXDRFoundMapping(){}
|
||||
};
|
||||
|
||||
//the class GwXDRMappings is not intended to be deleted
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#! /bin/sh
|
||||
i=0
|
||||
[ "$2" != "" ] && i=$2
|
||||
if [ "$1" = "" ] ; then
|
||||
echo "usage: $0 num [start]"
|
||||
exit 1
|
||||
fi
|
||||
num=$1
|
||||
|
||||
while [ $num -gt 0 ]
|
||||
do
|
||||
echo '{"name": "XDR'$i'","label": "XDR'$i'","type": "xdr","default": "", "check": "checkXDR","category":"xdr'$i'"},'
|
||||
num=`expr $num - 1`
|
||||
i=`expr $i + 1`
|
||||
done
|
480
web/config.json
480
web/config.json
|
@ -6,16 +6,101 @@
|
|||
"default": "ESP32NMEA2K",
|
||||
"check": "checkSystemName",
|
||||
"description": "system name, used for the access point and for services",
|
||||
"category":"system"
|
||||
"category": "system"
|
||||
},
|
||||
{
|
||||
"name":"talkerId",
|
||||
"label":"NMEA0183 ID",
|
||||
"type":"list",
|
||||
"default":"GP",
|
||||
"list":["AB","AD","AG","AP","AI","AN","AR","AS","AT","AX","BI","BN","CA","CD","CR","CS","CT","CV","CX","DF","DU","DP","EC","EI","EP","ER","FD","FE","FR","FS","GA","GB","GI","GL","GN","GP","GQ","HC","HE","HF","HN","HD","HS","II","IN","JA","JB","JC","JD","JE","JF","JG","JH","LC","NL","NV","RA","RB","RC","RI","SA","SC","SD","SG","SN","SS","TC","TI","UP","VD","VM","VW","VA","VS","VT","VR","WD","WI","WL","YX","ZA","ZC","ZQ","ZV"],
|
||||
"description":"the talkerId used in generated NMEA0183 records",
|
||||
"category":"system"
|
||||
"name": "talkerId",
|
||||
"label": "NMEA0183 ID",
|
||||
"type": "list",
|
||||
"default": "GP",
|
||||
"list": [
|
||||
"AB",
|
||||
"AD",
|
||||
"AG",
|
||||
"AP",
|
||||
"AI",
|
||||
"AN",
|
||||
"AR",
|
||||
"AS",
|
||||
"AT",
|
||||
"AX",
|
||||
"BI",
|
||||
"BN",
|
||||
"CA",
|
||||
"CD",
|
||||
"CR",
|
||||
"CS",
|
||||
"CT",
|
||||
"CV",
|
||||
"CX",
|
||||
"DF",
|
||||
"DU",
|
||||
"DP",
|
||||
"EC",
|
||||
"EI",
|
||||
"EP",
|
||||
"ER",
|
||||
"FD",
|
||||
"FE",
|
||||
"FR",
|
||||
"FS",
|
||||
"GA",
|
||||
"GB",
|
||||
"GI",
|
||||
"GL",
|
||||
"GN",
|
||||
"GP",
|
||||
"GQ",
|
||||
"HC",
|
||||
"HE",
|
||||
"HF",
|
||||
"HN",
|
||||
"HD",
|
||||
"HS",
|
||||
"II",
|
||||
"IN",
|
||||
"JA",
|
||||
"JB",
|
||||
"JC",
|
||||
"JD",
|
||||
"JE",
|
||||
"JF",
|
||||
"JG",
|
||||
"JH",
|
||||
"LC",
|
||||
"NL",
|
||||
"NV",
|
||||
"RA",
|
||||
"RB",
|
||||
"RC",
|
||||
"RI",
|
||||
"SA",
|
||||
"SC",
|
||||
"SD",
|
||||
"SG",
|
||||
"SN",
|
||||
"SS",
|
||||
"TC",
|
||||
"TI",
|
||||
"UP",
|
||||
"VD",
|
||||
"VM",
|
||||
"VW",
|
||||
"VA",
|
||||
"VS",
|
||||
"VT",
|
||||
"VR",
|
||||
"WD",
|
||||
"WI",
|
||||
"WL",
|
||||
"YX",
|
||||
"ZA",
|
||||
"ZC",
|
||||
"ZQ",
|
||||
"ZV"
|
||||
],
|
||||
"description": "the talkerId used in generated NMEA0183 records",
|
||||
"category": "system"
|
||||
},
|
||||
{
|
||||
"name": "stopApTime",
|
||||
|
@ -23,7 +108,7 @@
|
|||
"default": "0",
|
||||
"check": "checkStopApTime",
|
||||
"description": "stop the access point after that many minutes if not used",
|
||||
"category":"system"
|
||||
"category": "system"
|
||||
},
|
||||
{
|
||||
"name": "apPassword",
|
||||
|
@ -31,8 +116,12 @@
|
|||
"default": "esp32nmea2k",
|
||||
"check": "checkApPass",
|
||||
"description": "set the password for the Wifi access point",
|
||||
"category":"system",
|
||||
"capabilities":{"hardwareReset":["true"]}
|
||||
"category": "system",
|
||||
"capabilities": {
|
||||
"hardwareReset": [
|
||||
"true"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "usbBaud",
|
||||
|
@ -40,8 +129,21 @@
|
|||
"type": "list",
|
||||
"default": "115200",
|
||||
"description": "baud rate for the USB port",
|
||||
"list": [1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800],
|
||||
"category":"usb port"
|
||||
"list": [
|
||||
1200,
|
||||
2400,
|
||||
4800,
|
||||
9600,
|
||||
14400,
|
||||
19200,
|
||||
28800,
|
||||
38400,
|
||||
57600,
|
||||
115200,
|
||||
230400,
|
||||
460800
|
||||
],
|
||||
"category": "usb port"
|
||||
},
|
||||
{
|
||||
"name": "sendUsb",
|
||||
|
@ -49,7 +151,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "send out NMEA data on the USB port",
|
||||
"category":"usb port"
|
||||
"category": "usb port"
|
||||
},
|
||||
{
|
||||
"name": "receiveUsb",
|
||||
|
@ -57,7 +159,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "receive NMEA data on the USB port",
|
||||
"category":"usb port"
|
||||
"category": "usb port"
|
||||
},
|
||||
{
|
||||
"name": "usbToN2k",
|
||||
|
@ -65,7 +167,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "convert NMEA0183 from the USB port to NMEA2000",
|
||||
"category":"usb port"
|
||||
"category": "usb port"
|
||||
},
|
||||
{
|
||||
"name": "usbReadFilter",
|
||||
|
@ -73,7 +175,7 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when reading from USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category":"usb port"
|
||||
"category": "usb port"
|
||||
},
|
||||
{
|
||||
"name": "usbWriteFilter",
|
||||
|
@ -81,17 +183,25 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when writing to USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category":"usb port"
|
||||
"category": "usb port"
|
||||
},
|
||||
{
|
||||
"name": "serialDirection",
|
||||
"label": "serial direction",
|
||||
"type": "list",
|
||||
"default": "receive",
|
||||
"list": ["send","receive","off"],
|
||||
"list": [
|
||||
"send",
|
||||
"receive",
|
||||
"off"
|
||||
],
|
||||
"description": "use the serial port to send or receive data",
|
||||
"capabilities":{"serialmode":["UNI"]},
|
||||
"category":"serial port"
|
||||
"capabilities": {
|
||||
"serialmode": [
|
||||
"UNI"
|
||||
]
|
||||
},
|
||||
"category": "serial port"
|
||||
},
|
||||
{
|
||||
"name": "serialBaud",
|
||||
|
@ -99,9 +209,29 @@
|
|||
"type": "list",
|
||||
"default": "115200",
|
||||
"description": "baud rate for the serial port",
|
||||
"list": [1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800],
|
||||
"capabilities":{"serialmode":["RX","TX","UNI","BI"]},
|
||||
"category":"serial port"
|
||||
"list": [
|
||||
1200,
|
||||
2400,
|
||||
4800,
|
||||
9600,
|
||||
14400,
|
||||
19200,
|
||||
28800,
|
||||
38400,
|
||||
57600,
|
||||
115200,
|
||||
230400,
|
||||
460800
|
||||
],
|
||||
"capabilities": {
|
||||
"serialmode": [
|
||||
"RX",
|
||||
"TX",
|
||||
"UNI",
|
||||
"BI"
|
||||
]
|
||||
},
|
||||
"category": "serial port"
|
||||
},
|
||||
{
|
||||
"name": "sendSerial",
|
||||
|
@ -109,8 +239,13 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "send out NMEA data on the serial port",
|
||||
"capabilities":{"serialmode":["TX","BI"]},
|
||||
"category":"serial port"
|
||||
"capabilities": {
|
||||
"serialmode": [
|
||||
"TX",
|
||||
"BI"
|
||||
]
|
||||
},
|
||||
"category": "serial port"
|
||||
},
|
||||
{
|
||||
"name": "receiveSerial",
|
||||
|
@ -118,8 +253,13 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "receive NMEA data on the serial port",
|
||||
"capabilities":{"serialmode":["RX","BI"]},
|
||||
"category":"serial port"
|
||||
"capabilities": {
|
||||
"serialmode": [
|
||||
"RX",
|
||||
"BI"
|
||||
]
|
||||
},
|
||||
"category": "serial port"
|
||||
},
|
||||
{
|
||||
"name": "serialToN2k",
|
||||
|
@ -127,8 +267,14 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "convert NMEA0183 from the serial port to NMEA2000",
|
||||
"capabilities":{"serialmode":["RX","BI","UNI"]},
|
||||
"category":"serial port"
|
||||
"capabilities": {
|
||||
"serialmode": [
|
||||
"RX",
|
||||
"BI",
|
||||
"UNI"
|
||||
]
|
||||
},
|
||||
"category": "serial port"
|
||||
},
|
||||
{
|
||||
"name": "serialReadF",
|
||||
|
@ -136,8 +282,14 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"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"]},
|
||||
"category":"serial port"
|
||||
"capabilities": {
|
||||
"serialmode": [
|
||||
"RX",
|
||||
"BI",
|
||||
"UNI"
|
||||
]
|
||||
},
|
||||
"category": "serial port"
|
||||
},
|
||||
{
|
||||
"name": "serialWriteF",
|
||||
|
@ -145,8 +297,14 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"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"]},
|
||||
"category":"serial port"
|
||||
"capabilities": {
|
||||
"serialmode": [
|
||||
"TX",
|
||||
"BI",
|
||||
"UNI"
|
||||
]
|
||||
},
|
||||
"category": "serial port"
|
||||
},
|
||||
{
|
||||
"name": "serverPort",
|
||||
|
@ -154,16 +312,16 @@
|
|||
"type": "number",
|
||||
"default": "10110",
|
||||
"description": "the TCP port we listen on",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "maxClients",
|
||||
"label": "max. TCP clients",
|
||||
"type": "number",
|
||||
"default": "10",
|
||||
"check":"checkMaxClients",
|
||||
"check": "checkMaxClients",
|
||||
"description": "the number of clients we allow to connect to us",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "sendTCP",
|
||||
|
@ -171,7 +329,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "send out NMEA data to connected TCP clients",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "readTCP",
|
||||
|
@ -179,7 +337,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "receive NMEA data from connected TCP clients",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "tcpToN2k",
|
||||
|
@ -187,7 +345,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "convert NMEA0183 from TCP clients to NMEA2000",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "tcpReadFilter",
|
||||
|
@ -195,7 +353,7 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when reading from TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "tcpWriteFilter",
|
||||
|
@ -203,7 +361,7 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when writing to TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "sendSeasmart",
|
||||
|
@ -211,7 +369,7 @@
|
|||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "send NMEA2000 as seasmart to connected TCP clients",
|
||||
"category":"TCP port"
|
||||
"category": "TCP port"
|
||||
},
|
||||
{
|
||||
"name": "wifiClient",
|
||||
|
@ -219,7 +377,7 @@
|
|||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "connect to an external WIFI network",
|
||||
"category":"wifi client"
|
||||
"category": "wifi client"
|
||||
},
|
||||
{
|
||||
"name": "wifiPass",
|
||||
|
@ -227,7 +385,7 @@
|
|||
"type": "password",
|
||||
"default": "",
|
||||
"description": "the password for an external WIFI network",
|
||||
"category":"wifi client"
|
||||
"category": "wifi client"
|
||||
},
|
||||
{
|
||||
"name": "wifiSSID",
|
||||
|
@ -236,7 +394,7 @@
|
|||
"default": "",
|
||||
"check": "checkSSID",
|
||||
"description": "the SSID for an external WIFI network",
|
||||
"category":"wifi client"
|
||||
"category": "wifi client"
|
||||
},
|
||||
{
|
||||
"name": "XDR1",
|
||||
|
@ -244,7 +402,7 @@
|
|||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category":"xdr1"
|
||||
"category": "xdr1"
|
||||
},
|
||||
{
|
||||
"name": "XDR2",
|
||||
|
@ -252,8 +410,230 @@
|
|||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category":"xdr2"
|
||||
"category": "xdr2"
|
||||
},
|
||||
{
|
||||
"name": "XDR3",
|
||||
"label": "XDR3",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr3"
|
||||
},
|
||||
{
|
||||
"name": "XDR4",
|
||||
"label": "XDR4",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr4"
|
||||
},
|
||||
{
|
||||
"name": "XDR5",
|
||||
"label": "XDR5",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr5"
|
||||
},
|
||||
{
|
||||
"name": "XDR6",
|
||||
"label": "XDR6",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr6"
|
||||
},
|
||||
{
|
||||
"name": "XDR7",
|
||||
"label": "XDR7",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr7"
|
||||
},
|
||||
{
|
||||
"name": "XDR8",
|
||||
"label": "XDR8",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr8"
|
||||
},
|
||||
{
|
||||
"name": "XDR9",
|
||||
"label": "XDR9",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr9"
|
||||
},
|
||||
{
|
||||
"name": "XDR10",
|
||||
"label": "XDR10",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr10"
|
||||
},
|
||||
{
|
||||
"name": "XDR11",
|
||||
"label": "XDR11",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr11"
|
||||
},
|
||||
{
|
||||
"name": "XDR12",
|
||||
"label": "XDR12",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr12"
|
||||
},
|
||||
{
|
||||
"name": "XDR13",
|
||||
"label": "XDR13",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr13"
|
||||
},
|
||||
{
|
||||
"name": "XDR14",
|
||||
"label": "XDR14",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr14"
|
||||
},
|
||||
{
|
||||
"name": "XDR15",
|
||||
"label": "XDR15",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr15"
|
||||
},
|
||||
{
|
||||
"name": "XDR16",
|
||||
"label": "XDR16",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr16"
|
||||
},
|
||||
{
|
||||
"name": "XDR17",
|
||||
"label": "XDR17",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr17"
|
||||
},
|
||||
{
|
||||
"name": "XDR18",
|
||||
"label": "XDR18",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr18"
|
||||
},
|
||||
{
|
||||
"name": "XDR19",
|
||||
"label": "XDR19",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr19"
|
||||
},
|
||||
{
|
||||
"name": "XDR20",
|
||||
"label": "XDR20",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr20"
|
||||
},
|
||||
{
|
||||
"name": "XDR21",
|
||||
"label": "XDR21",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr21"
|
||||
},
|
||||
{
|
||||
"name": "XDR22",
|
||||
"label": "XDR22",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr22"
|
||||
},
|
||||
{
|
||||
"name": "XDR23",
|
||||
"label": "XDR23",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr23"
|
||||
},
|
||||
{
|
||||
"name": "XDR24",
|
||||
"label": "XDR24",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr24"
|
||||
},
|
||||
{
|
||||
"name": "XDR25",
|
||||
"label": "XDR25",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr25"
|
||||
},
|
||||
{
|
||||
"name": "XDR26",
|
||||
"label": "XDR26",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr26"
|
||||
},
|
||||
{
|
||||
"name": "XDR27",
|
||||
"label": "XDR27",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr27"
|
||||
},
|
||||
{
|
||||
"name": "XDR28",
|
||||
"label": "XDR28",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr28"
|
||||
},
|
||||
{
|
||||
"name": "XDR29",
|
||||
"label": "XDR29",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr29"
|
||||
},
|
||||
{
|
||||
"name": "XDR30",
|
||||
"label": "XDR30",
|
||||
"type": "xdr",
|
||||
"default": "",
|
||||
"check": "checkXDR",
|
||||
"category": "xdr30"
|
||||
}
|
||||
|
||||
|
||||
]
|
|
@ -146,7 +146,15 @@ body{
|
|||
.xdrexample {
|
||||
max-width: 16em;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
.uploadXdr{
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
}
|
||||
button.addunassigned {
|
||||
margin-left: 1em;
|
||||
}
|
||||
.msgDetails .value {
|
||||
width: 5em;
|
||||
text-align: right;
|
||||
|
@ -162,6 +170,7 @@ body{
|
|||
right: 0;
|
||||
background-color: #80808070;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
div#overlay {
|
||||
|
|
|
@ -65,6 +65,8 @@
|
|||
<button id="resetForm">ReloadConfig</button>
|
||||
<button id="changeConfig">Save&Restart</button>
|
||||
<button id="loadUnassigned">Show Unmapped</button>
|
||||
<button id="exportXdr">Export</button>
|
||||
<button id="importXdr">Import</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabPage hidden" id="dashboardPage">
|
||||
|
@ -82,6 +84,40 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="xdrHelp" class="hidden">
|
||||
<h1>XDR Help</h1>
|
||||
<p>You can configure the mapping of various NMEA2000 entities to XDR records.</p>
|
||||
<p>To set up such a mapping you select the category, the source of data
|
||||
(if the category has different sources) and the field (if the category has multiple fields).</p>
|
||||
<p>You have to provide the name of the transducer for the XDR record.</p>
|
||||
<p>Many of the NMEA2000 messages have an instance id (0..255) to allow
|
||||
for different sources of the data. You need to decide how do you want to
|
||||
map such IDs to XDR records.
|
||||
</p>
|
||||
<ul>
|
||||
<li>IGNORE: the instance id will be ignored</li>
|
||||
<li>AUTO: the instance will be appended to the transducer name: #123</li>
|
||||
<li>SINGLE: just map exactlly one instance to this transducer name</li>
|
||||
</ul>
|
||||
<p>You can also decide if you want a both way mapping, i.e. also map
|
||||
received XDR records back to NMEA 2000.
|
||||
</p>
|
||||
<p>Once you create a mapping the system will show an example of the
|
||||
generated XDR records (and the accepted ones).
|
||||
</p>
|
||||
<p>To ease the set up the system will track received NMEA2000 data
|
||||
that it currently does not map.
|
||||
With clicking <span class="txtShowUnmapped">"Show Unmapped"</span> you bring up such a list and for each
|
||||
found mapping there is a "+" button that will allow you to create a mapping
|
||||
for this record.
|
||||
</p>
|
||||
<p>The set of handled PGNs that can be mapped to XDR records can be found
|
||||
in the documentation.
|
||||
</p>
|
||||
</div>
|
||||
<div class="hidden">
|
||||
<a id="downloadXdr"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
299
web/index.js
299
web/index.js
|
@ -110,9 +110,39 @@ function checkApPass(v) {
|
|||
return "password must be at least 8 characters";
|
||||
}
|
||||
}
|
||||
function checkXDR(v,allValues){
|
||||
if (! v) return;
|
||||
let parts=v.split(',');
|
||||
if (parseInt(parts[1]) == 0) return;
|
||||
if (parseInt(parts[1]) != 0 && ! parts[6]) return "missing transducer name";
|
||||
for (let k in allValues){
|
||||
if (! k.match(/^XDR/)) continue;
|
||||
let cmp=allValues[k];
|
||||
if (cmp == v){
|
||||
return "same mapping already defined in "+k;
|
||||
}
|
||||
let cmpParts=cmp.split(',');
|
||||
if (parseInt(cmpParts[1]) != 0 && parts[6] == cmpParts[6]){
|
||||
return "transducer "+parts[6]+" already defined in "+k;
|
||||
}
|
||||
//check similar mappings
|
||||
if (parts[0]==cmpParts[0] && parts[2] == cmpParts[2] && parts[3] == cmpParts[3]){
|
||||
if (parts[4] == cmpParts[4] && parts[5] == cmpParts[5]){
|
||||
return "mapping for the same entity already defined in "+k;
|
||||
}
|
||||
if ((parseInt(parts[4]) == 0 && parseInt(cmpParts[4]) == 1)||
|
||||
(parseInt(parts[4]) == 1 && parseInt(cmpParts[4]) == 0)
|
||||
){
|
||||
//ignore and single for the same mapping
|
||||
return "mapping for the same entity already defined in "+k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function changeConfig() {
|
||||
let url = "/api/setConfig?";
|
||||
let values = document.querySelectorAll('.configForm select , .configForm input');
|
||||
let allValues={};
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let v = values[i];
|
||||
let name = v.getAttribute('name');
|
||||
|
@ -121,7 +151,7 @@ function changeConfig() {
|
|||
let check = v.getAttribute('data-check');
|
||||
if (check) {
|
||||
if (typeof (self[check]) === 'function') {
|
||||
let res = self[check](v.value);
|
||||
let res = self[check](v.value,allValues);
|
||||
if (res) {
|
||||
let value = v.value;
|
||||
if (v.type === 'password') value = "******";
|
||||
|
@ -130,6 +160,7 @@ function changeConfig() {
|
|||
}
|
||||
}
|
||||
}
|
||||
allValues[name]=v.value;
|
||||
url += name + "=" + encodeURIComponent(v.value) + "&";
|
||||
}
|
||||
getJson(url)
|
||||
|
@ -236,8 +267,8 @@ function checkChange(el, row) {
|
|||
}
|
||||
}
|
||||
}
|
||||
let configDefinitions;
|
||||
let xdrConfig;
|
||||
let configDefinitions={};
|
||||
let xdrConfig={};
|
||||
function createInput(configItem, frame,clazz) {
|
||||
let el;
|
||||
if (configItem.type === 'boolean' || configItem.type === 'list') {
|
||||
|
@ -295,9 +326,11 @@ function createInput(configItem, frame,clazz) {
|
|||
|
||||
function updateSelectList(item,slist){
|
||||
item.innerHTML='';
|
||||
let idx=0;
|
||||
slist.forEach(function (sitem) {
|
||||
let sitemEl = addEl('option','',item,sitem.l);
|
||||
sitemEl.setAttribute('value', sitem.v);
|
||||
sitemEl.setAttribute('value', sitem.v !== undefined?sitem.v:idx);
|
||||
idx++;
|
||||
})
|
||||
}
|
||||
function getXdrCategories(){
|
||||
|
@ -309,25 +342,39 @@ function getXdrCategories(){
|
|||
}
|
||||
return rt;
|
||||
}
|
||||
function getXdrSelectors(category){
|
||||
category=parseInt(category);
|
||||
function getXdrCategoryName(cid){
|
||||
category=parseInt(cid);
|
||||
for (let c in xdrConfig){
|
||||
let base=xdrConfig[c];
|
||||
if (parseInt(base.id) == category){
|
||||
return base.selector || [];
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
function getXdrCategory(cid){
|
||||
category=parseInt(cid);
|
||||
for (let c in xdrConfig){
|
||||
let base=xdrConfig[c];
|
||||
if (parseInt(base.id) == category){
|
||||
return base;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
function getXdrSelectors(category){
|
||||
let base=getXdrCategory(category);
|
||||
return base.selector || [];
|
||||
}
|
||||
function getXdrFields(category){
|
||||
category=parseInt(category);
|
||||
for (let c in xdrConfig){
|
||||
let base=xdrConfig[c];
|
||||
if (parseInt(base.id) == category){
|
||||
return base.fields || [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
let base=getXdrCategory(category);
|
||||
if (!base.fields) return [];
|
||||
let rt=[];
|
||||
base.fields.forEach(function(f){
|
||||
if (f.t === undefined) return;
|
||||
if (parseInt(f.t) == 99) return; //unknown type
|
||||
rt.push(f);
|
||||
});
|
||||
return rt;
|
||||
}
|
||||
|
||||
function createXdrLine(parent,label){
|
||||
|
@ -421,10 +468,16 @@ function createXdrInput(configItem,frame){
|
|||
if (used) {
|
||||
el.classList.add('xdrcused');
|
||||
el.classList.remove('xdrcunused');
|
||||
forEl('.categoryAdd',function(add){
|
||||
add.textContent=xdrName.value;
|
||||
},el);
|
||||
}
|
||||
else {
|
||||
el.classList.remove('xdrcused');
|
||||
el.classList.add('xdrcunused');
|
||||
forEl('.categoryAdd',function(add){
|
||||
add.textContent='';
|
||||
},el);
|
||||
}
|
||||
if (modified){
|
||||
el.classList.add('changed');
|
||||
|
@ -443,7 +496,12 @@ function createXdrInput(configItem,frame){
|
|||
example.textContent='';
|
||||
}
|
||||
}
|
||||
let updateFunction = function () {
|
||||
let updateFunction = function (evt) {
|
||||
if (evt.target == category){
|
||||
selector.value=0;
|
||||
field.value=0;
|
||||
instance.value=0;
|
||||
}
|
||||
let txt=category.value+","+direction.value+","+
|
||||
selector.value+","+field.value+","+imode.value;
|
||||
let instanceValue=parseInt(instance.value||0);
|
||||
|
@ -471,7 +529,8 @@ function createXdrInput(configItem,frame){
|
|||
|
||||
function isXdrUsed(element){
|
||||
let parts=element.value.split(',');
|
||||
if (! parts[0]) return false;
|
||||
if (! parts[1]) return false;
|
||||
if (! parseInt(parts[1])) return false;
|
||||
if (! parts[6]) return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -520,6 +579,194 @@ function createFilterInput(configItem, frame) {
|
|||
data.setAttribute('name', configItem.name);
|
||||
return data;
|
||||
}
|
||||
let moreicons=['icon-more','icon-less'];
|
||||
|
||||
function collapseCategories(parent,expand){
|
||||
let doExpand=expand;
|
||||
forEl('.category',function(cat){
|
||||
if (typeof(expand) === 'function') doExpand=expand(cat);
|
||||
forEl('.content',function(content){
|
||||
if (doExpand){
|
||||
content.classList.remove('hidden');
|
||||
}
|
||||
else{
|
||||
content.classList.add('hidden');
|
||||
}
|
||||
},cat);
|
||||
forEl('.title .icon',function(icon){
|
||||
toggleClass(icon,doExpand?1:0,moreicons);
|
||||
},cat);
|
||||
},parent);
|
||||
}
|
||||
|
||||
function findFreeXdr(data){
|
||||
let page=document.getElementById('xdrPage');
|
||||
let el=undefined;
|
||||
collapseCategories(page,function(cat){
|
||||
if (el) return false;
|
||||
let vEl=cat.querySelector('.xdrvalue');
|
||||
if (!vEl) return false;
|
||||
if (isXdrUsed(vEl)) return false;
|
||||
el=vEl;
|
||||
if (data){
|
||||
el.value=data;
|
||||
let ev=new Event('change');
|
||||
el.dispatchEvent(ev);
|
||||
window.setTimeout(function(){
|
||||
cat.scrollIntoView();
|
||||
},50);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function convertUnassigned(value){
|
||||
let rt={};
|
||||
value=parseInt(value);
|
||||
if (isNaN(value)) return;
|
||||
//see GwXDRMappings::addUnknown
|
||||
let instance=value & 0x1ff;
|
||||
value = value >> 9;
|
||||
let field=value & 0x7f;
|
||||
value = value >> 7;
|
||||
let selector=value & 0x7f;
|
||||
value = value >> 7;
|
||||
let cid=value & 0x7f;
|
||||
let category=getXdrCategory(cid);
|
||||
let cname=getXdrCategoryName(cid);
|
||||
if (! cname) return rt;
|
||||
let fieldName="";
|
||||
(category.fields || []).forEach(function(f){
|
||||
if (parseInt(f.v) == field) fieldName=f.l;
|
||||
});
|
||||
let selectorName=selector+"";
|
||||
(category.selector ||[]).forEach(function(s){
|
||||
if (parseInt(s.v) == selector) selectorName=s.l;
|
||||
});
|
||||
rt.l=cname+","+selectorName+","+fieldName+","+instance;
|
||||
rt.v=cid+",1,"+selector+","+field+",1,"+instance+",";
|
||||
return rt;
|
||||
}
|
||||
|
||||
function unassignedAdd(ev) {
|
||||
let dv = ev.target.getAttribute('data-value');
|
||||
if (dv) {
|
||||
findFreeXdr(dv);
|
||||
hideOverlay();
|
||||
}
|
||||
}
|
||||
function loadUnassigned(){
|
||||
getText("/api/xdrUnmapped")
|
||||
.then(function(txt){
|
||||
let ot="";
|
||||
txt.split('\n').forEach(function(line){
|
||||
let cv=convertUnassigned(line);
|
||||
if (!cv || !cv.l) return;
|
||||
ot+='<div class="xdrunassigned"><span>'+
|
||||
cv.l+'</span>'+
|
||||
'<button class="addunassigned" data-value="'+
|
||||
cv.v+
|
||||
'">+</button></div>';
|
||||
})
|
||||
showOverlay(ot,true);
|
||||
forEl('.addunassigned',function(bt){
|
||||
bt.onclick=unassignedAdd;
|
||||
});
|
||||
})
|
||||
}
|
||||
function showXdrHelp(){
|
||||
let helpContent=document.getElementById('xdrHelp');
|
||||
if (helpContent){
|
||||
showOverlay(helpContent.innerHTML,true);
|
||||
}
|
||||
}
|
||||
function formatDate(d){
|
||||
if (! d) d=new Date();
|
||||
let rt=""+d.getFullYear();
|
||||
let v=d.getMonth();
|
||||
if (v < 10) rt+="0"+v;
|
||||
else rt+=v;
|
||||
v=d.getDate();
|
||||
if (v < 10) rt+="0"+v;
|
||||
else rt+=v;
|
||||
return rt;
|
||||
}
|
||||
function exportXdr(){
|
||||
let data={};
|
||||
forEl('.xdrvalue',function(el) {
|
||||
let name=el.getAttribute('name');
|
||||
let value=el.value;
|
||||
let err=checkXDR(value,data);
|
||||
if (err){
|
||||
alert("error in "+name+": "+value+"\n"+err);
|
||||
return;
|
||||
}
|
||||
data[name]=value;
|
||||
})
|
||||
let url="data:application/octet-stream,"+encodeURIComponent(JSON.stringify(data,undefined,2));
|
||||
let target=document.getElementById('downloadXdr');
|
||||
if (! target) return;
|
||||
target.setAttribute('href',url);
|
||||
target.setAttribute('download',"xdr"+formatDate()+".json");
|
||||
target.click();
|
||||
}
|
||||
function importXdr(){
|
||||
forEl('.uploadXdr',function(ul){
|
||||
ul.remove();
|
||||
});
|
||||
let ip=addEl('input','uploadXdr',document.body);
|
||||
ip.setAttribute('type','file');
|
||||
ip.addEventListener('change',function(ev){
|
||||
if (ip.files.length > 0){
|
||||
let f=ip.files[0];
|
||||
let reader=new FileReader();
|
||||
reader.onloadend=function(status){
|
||||
try{
|
||||
let idata=JSON.parse(reader.result);
|
||||
let hasOverwrites=false;
|
||||
for (let k in idata){
|
||||
if (! k.match(/^XDR[0-9][0-9]*/)){
|
||||
alert("file contains invalid key "+k);
|
||||
return;
|
||||
}
|
||||
let del=document.querySelector('input[name='+k+']');
|
||||
if (del){
|
||||
hasOverwrites=true;
|
||||
}
|
||||
}
|
||||
if (hasOverwrites){
|
||||
if (!confirm("overwrite existing data?")) return;
|
||||
}
|
||||
for (let k in idata){
|
||||
let del=document.querySelector('input[name='+k+']');
|
||||
if (del){
|
||||
del.value=idata[k];
|
||||
let ev=new Event('change');
|
||||
del.dispatchEvent(ev);
|
||||
}
|
||||
}
|
||||
}catch (error){
|
||||
alert("unable to parse upload: "+error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
reader.readAsBinaryString(f);
|
||||
}
|
||||
ip.remove();
|
||||
});
|
||||
ip.click();
|
||||
}
|
||||
function toggleClass(el,id,classList){
|
||||
let nc=classList[id];
|
||||
let rt=false;
|
||||
if (nc && !el.classList.contains(nc)) rt=true;
|
||||
for (let i in classList){
|
||||
if (i == id) continue;
|
||||
el.classList.remove(classList[i])
|
||||
}
|
||||
if (nc) el.classList.add(nc);
|
||||
return rt;
|
||||
}
|
||||
|
||||
function createConfigDefinitions(parent, capabilities, defs,includeXdr) {
|
||||
let category;
|
||||
|
@ -542,18 +789,17 @@ function createConfigDefinitions(parent, capabilities, defs,includeXdr) {
|
|||
let categoryTitle = addEl('div', 'title', categoryFrame);
|
||||
let categoryButton = addEl('span', 'icon icon-more', categoryTitle);
|
||||
addEl('span', 'label', categoryTitle, item.category);
|
||||
addEl('span','categoryAdd',categoryTitle);
|
||||
categoryEl = addEl('div', 'content', categoryFrame);
|
||||
categoryEl.classList.add('hidden');
|
||||
let currentEl = categoryEl;
|
||||
categoryTitle.addEventListener('click', function (ev) {
|
||||
let rs = currentEl.classList.toggle('hidden');
|
||||
if (rs) {
|
||||
categoryButton.classList.add('icon-more');
|
||||
categoryButton.classList.remove('icon-less');
|
||||
toggleClass(categoryButton,0,moreicons);
|
||||
}
|
||||
else {
|
||||
categoryButton.classList.remove('icon-more');
|
||||
categoryButton.classList.add('icon-less');
|
||||
toggleClass(categoryButton,1,moreicons);
|
||||
}
|
||||
})
|
||||
category = item.category;
|
||||
|
@ -591,7 +837,14 @@ function createConfigDefinitions(parent, capabilities, defs,includeXdr) {
|
|||
})
|
||||
bt = addEl('button', 'infoButton', btContainer, '?');
|
||||
bt.addEventListener('click', function (ev) {
|
||||
showOverlay(item.description);
|
||||
if (item.description){
|
||||
showOverlay(item.description);
|
||||
}
|
||||
else{
|
||||
if (item.category.match(/^xdr/)){
|
||||
showXdrHelp();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue