intermediate: externalize config definitions

This commit is contained in:
andreas 2021-10-28 20:43:05 +02:00
parent b5aa62ff49
commit b4fe48744b
7 changed files with 212 additions and 92 deletions

View File

@ -4,13 +4,18 @@ import shutil
import os
import sys
import inspect
import json
GEN_DIR='generated'
FILES=['web/index.html']
CFG_FILE='web/config.json'
FILES=['web/index.html',CFG_FILE]
CFG_INCLUDE='GwConfigDefinitions.h'
def basePath():
#see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined
return os.path.dirname(inspect.getfile(lambda: None))
def outPath():
#see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined
return os.path.join(os.path.dirname(inspect.getfile(lambda: None)),GEN_DIR)
return os.path.join(basePath(),GEN_DIR)
def checkDir():
dn=outPath()
if not os.path.exists(dn):
@ -20,21 +25,59 @@ def checkDir():
return False
return True
def compressFile(inFile):
outfile=os.path.basename(inFile)+".gz"
outfile=os.path.join(outPath(),outfile)
def isCurrent(infile,outfile):
if os.path.exists(outfile):
otime=os.path.getmtime(outfile)
itime=os.path.getmtime(inFile)
itime=os.path.getmtime(infile)
if (otime >= itime):
print("%s is newer then %s, no need to recreate"%(outfile,inFile))
return
print("%s is newer then %s, no need to recreate"%(outfile,infile))
return True
return False
def compressFile(inFile):
outfile=os.path.basename(inFile)+".gz"
inFile=os.path.join(basePath(),inFile)
outfile=os.path.join(outPath(),outfile)
if isCurrent(inFile,outfile):
return
with open(inFile, 'rb') as f_in:
with gzip.open(outfile, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
def generateCfg():
outfile=os.path.join(outPath(),CFG_INCLUDE)
infile=os.path.join(basePath(),CFG_FILE)
if isCurrent(infile,outfile):
return
print("creating %s"%CFG_INCLUDE)
with open(CFG_FILE,'rb') as ch:
config=json.load(ch)
with open(outfile,'w') as oh:
oh.write("//generated from %s\n"%CFG_FILE)
oh.write('#include "GwConfigItem.h"\n')
l=len(config)
oh.write('class GwConfigDefinitions{\n')
oh.write(' public:\n')
oh.write(' int getNumConfig() const{return %d;}\n'%(l))
for item in config:
n=item.get('name')
if n is None:
continue
oh.write(' const String %s=F("%s");\n'%(n,n))
oh.write(' protected:\n')
oh.write(' GwConfigItem *configs[%d]={\n'%(l))
first=True
for item in config:
if not first:
oh.write(',\n')
first=False
oh.write(" new GwConfigItem(%s,\"%s\")"%(item.get('name'),item.get('default')))
oh.write('};\n')
oh.write('};\n')
oh.close()
if not checkDir():
sys.exit(1)
for f in FILES:
print("compressing %s"%f)
compressFile(f)
compressFile(f)
generateCfg()

View File

@ -54,7 +54,7 @@ GwConfigInterface * GwConfigHandler::getConfigItem(const String name, bool dummy
return &dummyConfig;
}
#define PREF_NAME "gwprefs"
GwConfigHandler::GwConfigHandler(GwLog *logger){
GwConfigHandler::GwConfigHandler(GwLog *logger): GwConfigDefinitions(){
this->logger=logger;
}
bool GwConfigHandler::loadConfig(){

View File

@ -3,74 +3,16 @@
#include <Arduino.h>
#include <Preferences.h>
#include "GwLog.h"
class GwConfigInterface{
public:
virtual String asString() const=0;
virtual const char * asCString() const =0;
virtual bool asBoolean() const = 0;
virtual int asInt() const = 0;
};
class GwConfigItem: public GwConfigInterface{
private:
String name;
String initialValue;
String value;
public:
GwConfigItem(const String &name, const String initialValue){
this->name=name;
this->initialValue=initialValue;
this->value=initialValue;
}
virtual String asString() const{
return value;
}
virtual const char * asCString() const{
return value.c_str();
};
virtual void fromString(const String v){
value=v;
};
virtual bool asBoolean() const{
return strcasecmp(value.c_str(),"true") == 0;
}
virtual int asInt() const{
return (int)value.toInt();
}
String getName() const{
return name;
}
virtual void reset(){
value=initialValue;
}
bool changed() const{
return value != initialValue;
}
String getDefault() const {
return initialValue;
}
};
#include "GwConfigItem.h"
#include "GwConfigDefinitions.h"
class GwConfigHandler{
class GwConfigHandler: public GwConfigDefinitions{
private:
Preferences prefs;
GwLog *logger;
public:
public:
const String sendUsb=F("sendUsb");
const String receiveUsb=F("receiveUsb");
const String wifiClient=F("wifiClient");
const String wifiPass=F("wifiPass");
const String wifiSSID=F("wifiSSID");
const String serverPort=F("serverPort");
const String maxClients=F("maxClients");
const String sendTCP=F("sendTCP");
const String readTCP=F("receiveTCP");
const String sendSeasmart=F("sendSeasmart");
const String usbBaud=F("usbBaud");
const String systemName=F("systemName");
const String stopApTime=F("stopApTime");
GwConfigHandler(GwLog *logger);
bool loadConfig();
bool saveConfig();
@ -85,23 +27,5 @@ class GwConfigHandler{
GwConfigItem * findConfig(const String name, bool dummy=false);
GwConfigInterface * getConfigItem(const String name, bool dummy=false) const;
private:
GwConfigItem* configs[13]={
new GwConfigItem(sendUsb,"true"),
new GwConfigItem (receiveUsb,"false"),
new GwConfigItem (wifiClient,"false"),
new GwConfigItem (wifiSSID,""),
new GwConfigItem (wifiPass,""),
new GwConfigItem (serverPort,"2222"),
new GwConfigItem (maxClients, "10"),
new GwConfigItem (sendTCP,"true"),
new GwConfigItem (readTCP,"true"),
new GwConfigItem (sendSeasmart,"false"),
new GwConfigItem (usbBaud,"115200"),
new GwConfigItem (systemName,"ESP32NMEA2K"),
new GwConfigItem (stopApTime,"0")
};
int getNumConfig() const{
return 13;
}
};
#endif

52
lib/config/GwConfigItem.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef _GWCONFIGITEM_H
#define _GWCONFIGITEM_H
#include "WString.h"
class GwConfigInterface{
public:
virtual String asString() const=0;
virtual const char * asCString() const =0;
virtual bool asBoolean() const = 0;
virtual int asInt() const = 0;
};
class GwConfigItem: public GwConfigInterface{
private:
String name;
String initialValue;
String value;
public:
GwConfigItem(const String &name, const String initialValue){
this->name=name;
this->initialValue=initialValue;
this->value=initialValue;
}
virtual String asString() const{
return value;
}
virtual const char * asCString() const{
return value.c_str();
};
virtual void fromString(const String v){
value=v;
};
virtual bool asBoolean() const{
return strcasecmp(value.c_str(),"true") == 0;
}
virtual int asInt() const{
return (int)value.toInt();
}
String getName() const{
return name;
}
virtual void reset(){
value=initialValue;
}
bool changed() const{
return value != initialValue;
}
String getDefault() const {
return initialValue;
}
};
#endif

View File

@ -19,6 +19,7 @@ lib_deps =
ottowinter/ESPAsyncWebServer-esphome@^2.0.1
board_build.embed_files =
generated/index.html.gz
generated/config.json.gz
extra_scripts = extra_script.py
build_flags=
-Igenerated

View File

@ -12,7 +12,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define VERSION "0.1.1"
#define VERSION "0.1.2"
#include "GwHardware.h"
#include <Arduino.h>
@ -154,6 +154,7 @@ class EmbeddedFile {
const EmbeddedFile fileName##_##fileExt##_Config(#fileName "." #fileExt,(const uint8_t*)fileName##_##fileExt##_File,(int)fileName##_##fileExt##_FileLen);
EMBED_GZ_FILE(index,html)
EMBED_GZ_FILE(config,json)
void sendEmbeddedFile(String name,String contentType,AsyncWebServerRequest *request){
std::map<String,EmbeddedFile*>::iterator it=embeddedFiles.find(name);

99
web/config.json Normal file
View File

@ -0,0 +1,99 @@
[
{
"name": "systemName",
"label": "system name",
"type": "string",
"default": "ESP32NMEA2K",
"check": "checkSystemName",
"description": "system name, used for the access point and for services"
},
{
"name": "usbBaud",
"label": "USB baud rate",
"type": "list",
"default": "115200",
"description": "baud rate for the USB port",
"list": [1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800]
},
{
"name": "sendUsb",
"label": "NMEA to USB",
"type": "boolean",
"default": "true",
"description": "send out NMEA data on the USB port"
},
{
"name": "receiveUsb",
"label": "NMEA from USB",
"type": "boolean",
"default": "true",
"description": "receive NMEA data on the USB port"
},
{
"name": "serverPort",
"label": "TCP port",
"type": "number",
"default": "2222",
"description": "the TCP port we listen on"
},
{
"name": "maxClients",
"label": "max. TCP clients",
"type": "number",
"default": "10",
"check":"checkMaxClients",
"description": "the number of clients we allow to connect to us"
},
{
"name": "sendTCP",
"label": "NMEA to TCP",
"type": "boolean",
"default": "true",
"description": "send out NMEA data to connected TCP clients"
},
{
"name": "readTCP",
"label": "NMEA from TCP",
"type": "boolean",
"default": "true",
"description": "receive NMEA data from connected TCP clients"
},
{
"name": "sendSeasmart",
"label": "Seasmart to TCP",
"type": "boolean",
"default": "false",
"description": "send NMEA2000 as seasmart to connected TCP clients"
},
{
"name": "wifiClient",
"label": "wifi client",
"type": "boolean",
"default": "false",
"description": "connect to an external WIFI network"
},
{
"name": "wifiPass",
"label": "wifi client password",
"type": "password",
"default": "",
"description": "the password for an external WIFI network"
},
{
"name": "wifiSSID",
"label": "wifi client SSID",
"type": "string",
"default": "",
"check": "checkSSID",
"description": "the SSID for an external WIFI network"
},
{
"name": "stopApTime",
"type": "number",
"default": "0",
"check": "checkStopApTime",
"description": "stop the access point after that many minutes if not used"
}
]