diff --git a/extra_script.py b/extra_script.py
index c8d81a8..4372b1e 100644
--- a/extra_script.py
+++ b/extra_script.py
@@ -91,31 +91,57 @@ def writeFileIfChanged(fileName,data):
with open(fileName,"w") as oh:
oh.write(data)
-def generateCfg(ch,oh,inFile=''):
- config=json.load(ch)
- oh.write("//generated from %s\n"%inFile)
- 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
- if len(n) > 15:
- raise Exception("%s: config names must be max 15 caracters"%n)
- 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')
+def mergeConfig(base,other):
+ for bdir in other:
+ cname=os.path.join(bdir,"config.json")
+ if os.path.exists(cname):
+ print("merge config %s"%cname)
+ with open(cname,'rb') as ah:
+ merge=json.load(ah)
+ base=base+merge
+ return base
+
+def generateMergedConfig(inFile,outFile,addDirs=[]):
+ if not os.path.exists(inFile):
+ raise Exception("unable to read cfg file %s"%inFile)
+ data=""
+ with open(inFile,'rb') as ch:
+ config=json.load(ch)
+ config=mergeConfig(config,addDirs)
+ data=json.dumps(config,indent=2)
+ writeFileIfChanged(outFile,data)
+
+def generateCfg(inFile,outFile,addDirs=[]):
+ if not os.path.exists(inFile):
+ raise Exception("unable to read cfg file %s"%inFile)
+ data=""
+ with open(inFile,'rb') as ch:
+ config=json.load(ch)
+ config=mergeConfig(config,addDirs)
+ data+="//generated from %s\n"%inFile
+ data+='#include "GwConfigItem.h"\n'
+ l=len(config)
+ data+='class GwConfigDefinitions{\n'
+ data+=' public:\n'
+ data+=' int getNumConfig() const{return %d;}\n'%(l)
+ for item in config:
+ n=item.get('name')
+ if n is None:
+ continue
+ if len(n) > 15:
+ raise Exception("%s: config names must be max 15 caracters"%n)
+ data+=' const String %s=F("%s");\n'%(n,n)
+ data+=' protected:\n'
+ data+=' GwConfigItem *configs[%d]={\n'%(l)
+ first=True
+ for item in config:
+ if not first:
+ data+=',\n'
+ first=False
+ data+=" new GwConfigItem(%s,\"%s\")"%(item.get('name'),item.get('default'))
+ data+='};\n'
+ data+='};\n'
+ writeFileIfChanged(outFile,data)
def generateXdrMappings(fp,oh,inFile=''):
@@ -157,10 +183,17 @@ def generateXdrMappings(fp,oh,inFile=''):
oh.write("\n")
oh.write("};\n")
-def genereateUserTasks(outfile):
- includes=[]
+userTaskDirs=[]
+
+def getUserTaskDirs():
+ rt=[]
taskdirs=glob.glob(os.path.join('lib','*task*'))
for task in taskdirs:
+ rt.append(task)
+ return rt
+def genereateUserTasks(outfile):
+ includes=[]
+ for task in userTaskDirs:
#print("##taskdir=%s"%task)
base=os.path.basename(task)
includeNames=[base.lower()+".h",'gw'+base.lower()+'.h']
@@ -201,9 +234,11 @@ def getContentType(fn):
return "application/octet-stream"
def prebuild(env):
+ global userTaskDirs
print("#prebuild running")
if not checkDir():
sys.exit(1)
+ userTaskDirs=getUserTaskDirs()
embedded=getEmbeddedFiles(env)
filedefs=[]
for ef in embedded:
@@ -223,7 +258,10 @@ def prebuild(env):
print("#WARNING: infile %s for %s not found"%(inFile,ef))
generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
- generateFile(os.path.join(basePath(),CFG_FILE),os.path.join(outPath(),CFG_INCLUDE),generateCfg)
+ mergedConfig=os.path.join(outPath(),os.path.basename(CFG_FILE))
+ generateMergedConfig(os.path.join(basePath(),CFG_FILE),mergedConfig,userTaskDirs)
+ compressFile(mergedConfig,mergedConfig+".gz")
+ generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE))
generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
version="dev"+datetime.now().strftime("%Y%m%d")
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h
index 6ecac9e..1a4fd2f 100644
--- a/lib/api/GwApi.h
+++ b/lib/api/GwApi.h
@@ -19,4 +19,7 @@ class GwApi{
#ifndef DECLARE_USERTASK
#define DECLARE_USERTASK(task)
#endif
+#ifndef DECLARE_CAPABILITY
+#define DECLARE_CAPABILITY(name,value)
+#endif
#endif
diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp
index 7acd041..8ab422c 100644
--- a/lib/exampletask/GwExampleTask.cpp
+++ b/lib/exampletask/GwExampleTask.cpp
@@ -35,10 +35,15 @@ class GetBoatDataRequest: public GwMessage{
void exampleTask(void *param){
GwApi *api=(GwApi*)param;
GwLog *logger=api->getLogger();
+ //get some configuration data
+ bool exampleSwitch=api->getConfig()->getConfigItem(
+ api->getConfig()->exampleConfig,
+ true)->asBoolean();
//------
//initialization goes here
//------
bool hasPosition=false;
+ LOG_DEBUG(GwLog::DEBUG,"example switch ist %s",exampleSwitch?"true":"false");
while(true){
delay(1000);
/*
@@ -65,14 +70,14 @@ void exampleTask(void *param){
}
if (r->latitude == INVALID_COORD || r->longitude == INVALID_COORD){
if (hasPosition){
- logger->logDebug(GwLog::ERROR,"position lost...");
+ if (exampleSwitch) logger->logDebug(GwLog::ERROR,"position lost...");
hasPosition=false;
}
}
else{
//do something with the data we have from boatData
if (! hasPosition){
- logger->logDebug(GwLog::LOG,"postion now available lat=%f, lon=%f",
+ if (exampleSwitch) logger->logDebug(GwLog::LOG,"postion now available lat=%f, lon=%f",
r->latitude,r->longitude);
hasPosition=true;
}
diff --git a/lib/exampletask/GwExampleTask.h b/lib/exampletask/GwExampleTask.h
index 048991b..267e0e2 100644
--- a/lib/exampletask/GwExampleTask.h
+++ b/lib/exampletask/GwExampleTask.h
@@ -7,5 +7,9 @@
void exampleTask(void *param);
//make the task known to the core
DECLARE_USERTASK(exampleTask);
+//we declare a capability that we can
+//use in config.json to only show some
+//elements when this capability is set correctly
+DECLARE_CAPABILITY(testboard,true);
#endif
#endif
\ No newline at end of file
diff --git a/lib/exampletask/Readme.md b/lib/exampletask/Readme.md
index 5842405..d9c3abf 100644
--- a/lib/exampletask/Readme.md
+++ b/lib/exampletask/Readme.md
@@ -1,8 +1,9 @@
Extending the Core
==================
This directory contains an example on how you can extend the base functionality of the gateway.
-Basically you can define own boards here and can add one or more tasks that will be started by the core.
-You can also add additional libraries that will be used for your task.
+Maybe you have another interesting hardware or need some additional functions but would like to use the base functionality of the gateway.
+You can define own hardware configurations (environments) here and can add one or more tasks that will be started by the core.
+You can also add additional libraries that will be used to build your task.
In this example we define an addtional board (environment) with the name "testboard".
When building for this board we add the -DTEST_BOARD to the compilation - see [platformio.ini](platformio.ini).
The additional task that we defined will only be compiled and started for this environment (see the #ifdef TEST_BOARD in the code).
@@ -10,22 +11,41 @@ You can add your own directory below "lib". The name of the directory must conta
Files
-----
- * [platformio.ini](platformio.ini)
- extend the base configuration - we add a dummy library here and define our buil environment (board)
- * [GwExampleTask.h](GwExampleTask.h) the name of this include must match the name of the directory (ignoring case) with a "gw" in front. This file includes our special hardware definitions and registers our task at the core (DECLARE_USERTASK in the code).
+ * [platformio.ini](platformio.ini)
+ This file is completely optional.
+ You only need this if you want to
+ extend the base configuration - we add a dummy library here and define one additional build environment (board)
+ * [GwExampleTask.h](GwExampleTask.h) the name of this include must match the name of the directory (ignoring case) with a "gw" in front. This file includes our special hardware definitions and registers our task at the core (DECLARE_USERTASK in the code). Optionally it can define some capabilities (using DECLARE_CAPABILITY) that can be used in the config UI (see below).
+ Avoid including headers from other libraries in this file as this could interfere with the main code. Just only include them in your .cpp files (or in other headers).
* [GwExampleTaks.cpp](GwExampleTask.cpp) includes the implementation of our task. This tasks runs in an own thread - see the comments in the code.
+ We can have as many cpp (and header files) as we need to structure our code.
* [GwExampleHardware.h](GwExampleHardware.h) includes our pin definitions for the board.
+ * [config.json](config.json)
+ This file allows to add some config definitions that are needed for our task. For the possible options have a look at the global [config.json](../../web/config.json). Be careful not to overwrite config defitions from the global file. A good practice wood be to prefix the names of definitions with parts of the library name. Always put them in a separate category so that they do not interfere with the system ones.
+ The defined config items can later be accessed in the code (see the example in [GwExampleTask.cpp](GwExampleTask.cpp)).
Hints
-----
- Just be careful not to interfere with names from the core - so it is a good practice to prefix your files and class like in the example.
+ Just be careful not to interfere with C symbols from the core - so it is a good practice to prefix your files and class like in the example.
Developing
----------
To develop I recommend forking the gateway repository and adding your own directory below lib (with the string task in it's name).
As your code goes into a separate directory it should be very easy to fetch upstream changes without the need to adapt your code.
+ Typically after forking the repo on github (https://github.com/wellenvogel/esp32-nmea2000) and initially cloning it you will add my repository as an "upstream repo":
+ ```
+ git remote add upstream https://github.com/wellenvogel/esp32-nmea2000.git
+ ```
+ To merge in a new version use:
+ ```
+ git fetch upstream
+ git merge upstream/master
+ ```
+ Refer to https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork
+
+ By following the hints in this doc the merge should always succeed without conflicts.
Future Plans
------------
- If there will be a need we can extend this extension API by means of adding config items and specific java script code and css for the UI.
+ If there will be a need we can extend this extension API by means of adding specific java script code and css for the UI.
diff --git a/lib/exampletask/config.json b/lib/exampletask/config.json
new file mode 100644
index 0000000..3ecaf3b
--- /dev/null
+++ b/lib/exampletask/config.json
@@ -0,0 +1,13 @@
+[
+ {
+ "name": "exampleConfig",
+ "label": "logging on",
+ "type": "boolean",
+ "default": "false",
+ "description": "switch on logging of position acquired/failed",
+ "category": "example",
+ "capabilities": {
+ "testboard":"true"
+ }
+ }
+]
\ No newline at end of file
diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp
index e1249e3..1e82af6 100644
--- a/lib/usercode/GwUserCode.cpp
+++ b/lib/usercode/GwUserCode.cpp
@@ -1,6 +1,7 @@
#include "GwUserCode.h"
#include
#include
+#include