From 6cdaab4d600153d2068e86b5b372251c1e69fcdd Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 14 Oct 2023 19:20:21 +0200 Subject: [PATCH] allow a counter for user tasks, reorganize generated config handling --- extra_script.py | 108 +++++++++++++++--------------- lib/api/GwApi.h | 25 ++++++- lib/channel/GwChannelList.cpp | 5 ++ lib/config/GWConfig.cpp | 27 +++++--- lib/config/GWConfig.h | 9 +-- lib/counter/GwCounter.h | 5 +- lib/exampletask/GwExampleTask.cpp | 6 +- lib/exampletask/GwExampleTask.h | 31 ++------- lib/usercode/GwUserCode.cpp | 80 ++++++++++++++++++---- lib/usercode/GwUserCode.h | 22 ++++-- src/main.cpp | 6 +- 11 files changed, 205 insertions(+), 119 deletions(-) diff --git a/extra_script.py b/extra_script.py index 00cf107..6650b29 100644 --- a/extra_script.py +++ b/extra_script.py @@ -14,6 +14,7 @@ GEN_DIR='lib/generated' CFG_FILE='web/config.json' XDR_FILE='web/xdrconfig.json' CFG_INCLUDE='GwConfigDefinitions.h' +CFG_INCLUDE_IMPL='GwConfigDefImpl.h' XDR_INCLUDE='GwXdrTypeMappings.h' TASK_INCLUDE='GwUserTasks.h' EMBEDDED_INCLUDE="GwEmbeddedFiles.h" @@ -112,66 +113,62 @@ def generateMergedConfig(inFile,outFile,addDirs=[]): data=json.dumps(config,indent=2) writeFileIfChanged(outFile,data) -def generateCfg(inFile,outFile,addDirs=[]): +def generateCfg(inFile,outFile,impl): 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) + config=json.load(ch) 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+=' static constexpr const char* %s="%s";\n'%(n,n) - data+=' protected:\n' - data+=' GwConfigInterface *configs[%d]={\n'%(l) - first=True - for item in config: - name=item.get('name') - if name is None: - continue - if not first: - data+=',\n' - first=False - secret="false"; - if item.get('type') == 'password': - secret="true" - data+=" #undef __CFGMODE\n" - data+=" #ifdef CFGMODE_%s\n"%(name) - data+=" #define __CFGMODE CFGMODE_%s\n"%(name) - data+=" #else\n" - data+=" #define __CFGMODE GwConfigInterface::NORMAL\n" - data+=" #endif\n" - data+=" #ifdef CFGDEFAULT_%s\n"%(name) - data+=" new GwConfigInterface(%s,CFGDEFAULT_%s,%s,__CFGMODE)\n"%(name,name,secret) - data+=" #else\n" - data+=" new GwConfigInterface(%s,\"%s\",%s,__CFGMODE)\n"%(name,item.get('default'),secret) - data+=" #endif\n" - - data+='};\n' - data+='};\n' - data+="#ifdef CFG_MESSAGES\n" - for item in config: - name=item.get('name') - if name is None: - continue - data+="#ifdef CFGMODE_%s\n"%(name) - data+=" __MSG(\"CFGMODE_%s=\" __XSTR(CFGMODE_%s))\n"%(name,name) - data+="#endif\n" - data+="#ifdef CFGDEFAULT_%s\n"%(name) - data+=" __MSG(\"CFGDEFAULT_%s=\" CFGDEFAULT_%s)\n"%(name,name) - data+="#endif\n" - data+="#endif" - + idx=0 + if not impl: + data+='#include "GwConfigItem.h"\n' + 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+=' static constexpr const char* %s="%s";\n'%(n,n) + data+="};\n" + else: + data+='void GwConfigHandler::populateConfigs(GwConfigInterface **config){\n' + for item in config: + name=item.get('name') + if name is None: + continue + data+=' configs[%d]=\n'%(idx) + idx+=1 + secret="false"; + if item.get('type') == 'password': + secret="true" + data+=" #undef __CFGMODE\n" + data+=" #ifdef CFGMODE_%s\n"%(name) + data+=" #define __CFGMODE CFGMODE_%s\n"%(name) + data+=" #else\n" + data+=" #define __CFGMODE GwConfigInterface::NORMAL\n" + data+=" #endif\n" + data+=" #ifdef CFGDEFAULT_%s\n"%(name) + data+=" new GwConfigInterface(%s,CFGDEFAULT_%s,%s,__CFGMODE)\n"%(name,name,secret) + data+=" #else\n" + data+=" new GwConfigInterface(%s,\"%s\",%s,__CFGMODE)\n"%(name,item.get('default'),secret) + data+=" #endif\n" + data+=";\n" + data+='}\n' + for item in config: + name=item.get('name') + if name is None: + continue + data+="#ifdef CFGMODE_%s\n"%(name) + data+=" __MSG(\"CFGMODE_%s=\" __XSTR(CFGMODE_%s))\n"%(name,name) + data+="#endif\n" + data+="#ifdef CFGDEFAULT_%s\n"%(name) + data+=" __MSG(\"CFGDEFAULT_%s=\" CFGDEFAULT_%s)\n"%(name,name) + data+="#endif\n" writeFileIfChanged(outFile,data) @@ -273,7 +270,8 @@ def prebuild(env): 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)) + generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False) + generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE_IMPL),True) embedded=getEmbeddedFiles(env) filedefs=[] for ef in embedded: diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h index b6be1cc..8021ac0 100644 --- a/lib/api/GwApi.h +++ b/lib/api/GwApi.h @@ -35,6 +35,12 @@ class GwApi{ class Status{ public: + typedef enum{ + OFF, + PRESSED, + PRESSED_5, //5...10s + PRESSED_10 //>10s...30s + } ButtonState; bool wifiApOn=false; bool wifiClientOn=false; bool wifiClientConnected=false; @@ -47,6 +53,8 @@ class GwApi{ unsigned long usbTx=0; unsigned long serRx=0; unsigned long serTx=0; + unsigned long ser2Rx=0; + unsigned long ser2Tx=0; unsigned long tcpSerRx=0; unsigned long tcpSerTx=0; int tcpClients=0; @@ -55,6 +63,8 @@ class GwApi{ bool tcpClientConnected=false; unsigned long n2kRx=0; unsigned long n2kTx=0; + ButtonState button=OFF; + unsigned long buttonPresses=0; void empty(){ wifiApOn=false; wifiClientOn=false; @@ -68,6 +78,8 @@ class GwApi{ usbTx=0; serRx=0; serTx=0; + ser2Rx=0; + ser2Tx=0; tcpSerRx=0; tcpSerTx=0; tcpClients=0; @@ -75,7 +87,9 @@ class GwApi{ tcpClTx=0; tcpClientConnected=false; n2kRx=0; - n2kTx=0; + n2kTx=0; + button=OFF; + buttonPresses=0; } }; /** @@ -109,7 +123,14 @@ class GwApi{ /** * fill the status information */ - virtual void getStatus(Status &status); + virtual void getStatus(Status &status)=0; + /** + * access to counters for a task + * thread safe + */ + virtual void setCounterDisplayName(const String &){} + virtual void increment(const String &name,bool failed=false){} + virtual void reset(){} /** * not thread safe methods * accessing boat data must only be executed from within the main thread diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 6d803c4..c094a0a 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -322,6 +322,11 @@ void GwChannelList::fillStatus(GwApi::Status &status){ status.serRx=channel->countRx(); status.serTx=channel->countTx(); } + channel=getChannelById(SERIAL2_CHANNEL_ID); + if (channel){ + status.ser2Rx=channel->countRx(); + status.ser2Tx=channel->countTx(); + } channel=getChannelById(MIN_TCP_CHANNEL_ID); if (channel){ status.tcpSerRx=channel->countRx(); diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index 641b564..8215872 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -1,8 +1,11 @@ #define CFG_MESSAGES +#include #include "GWConfig.h" #include #include #include +#include "GwHardware.h" +#include "GwConfigDefImpl.h" #define B(v) (v?"true":"false") @@ -56,14 +59,20 @@ GwConfigInterface * GwConfigHandler::getConfigItem(const String name, bool dummy GwConfigHandler::GwConfigHandler(GwLog *logger): GwConfigDefinitions(){ this->logger=logger; saltBase=esp_random(); + configs=new GwConfigInterface*[getNumConfig()]; + populateConfigs(configs); + prefs=new Preferences(); +} +GwConfigHandler::~GwConfigHandler(){ + delete prefs; } bool GwConfigHandler::loadConfig(){ - prefs.begin(PREF_NAME,true); + prefs->begin(PREF_NAME,true); for (int i=0;igetName().c_str(),configs[i]->getDefault()); + String v=prefs->getString(configs[i]->getName().c_str(),configs[i]->getDefault()); configs[i]->value=v; } - prefs.end(); + prefs->end(); return true; } @@ -78,19 +87,19 @@ bool GwConfigHandler::updateValue(String name, String value){ return false; } LOG_DEBUG(GwLog::LOG,"update config %s=>%s",name.c_str(),i->isSecret()?"***":value.c_str()); - prefs.begin(PREF_NAME,false); - prefs.putString(i->getName().c_str(),value); - prefs.end(); + prefs->begin(PREF_NAME,false); + prefs->putString(i->getName().c_str(),value); + prefs->end(); } return true; } bool GwConfigHandler::reset(){ LOG_DEBUG(GwLog::LOG,"reset config"); - prefs.begin(PREF_NAME,false); + prefs->begin(PREF_NAME,false); for (int i=0;igetName().c_str(),configs[i]->getDefault()); + prefs->putString(configs[i]->getName().c_str(),configs[i]->getDefault()); } - prefs.end(); + prefs->end(); return true; } String GwConfigHandler::getString(const String name, String defaultv) const{ diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h index 796ae25..c1225e1 100644 --- a/lib/config/GWConfig.h +++ b/lib/config/GWConfig.h @@ -1,21 +1,20 @@ #ifndef _GWCONFIG_H #define _GWCONFIG_H #include -#include #include "GwLog.h" #include "GwConfigItem.h" -#include "GwHardware.h" #include "GwConfigDefinitions.h" #include #include - +class Preferences; class GwConfigHandler: public GwConfigDefinitions{ private: - Preferences prefs; + Preferences *prefs; GwLog *logger; typedef std::map StringMap; boolean allowChanges=true; + GwConfigInterface **configs; public: public: GwConfigHandler(GwLog *logger); @@ -40,7 +39,9 @@ class GwConfigHandler: public GwConfigDefinitions{ bool setValue(String name, String value); static void toHex(unsigned long v,char *buffer,size_t bsize); unsigned long getSaltBase(){return saltBase;} + ~GwConfigHandler(); private: unsigned long saltBase=0; + void populateConfigs(GwConfigInterface **); }; #endif \ No newline at end of file diff --git a/lib/counter/GwCounter.h b/lib/counter/GwCounter.h index 2280328..a8d4267 100644 --- a/lib/counter/GwCounter.h +++ b/lib/counter/GwCounter.h @@ -11,9 +11,12 @@ template class GwCounter{ unsigned long globalFail=0; String name; public: - GwCounter(String name){ + GwCounter(const String &name){ this->name=name; }; + void setName(const String &name){ + this->name=name; + } void reset(){ okCounter.clear(); failCounter.clear(); diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp index 70dc9fa..883be56 100644 --- a/lib/exampletask/GwExampleTask.cpp +++ b/lib/exampletask/GwExampleTask.cpp @@ -3,6 +3,7 @@ #ifdef BOARD_TEST #include "GwExampleTask.h" #include "GwApi.h" +#include "GWConfig.h" #include /** @@ -97,6 +98,7 @@ void exampleTask(GwApi *api){ GwApi::BoatValue *testValue=new GwApi::BoatValue(boatItemName); GwApi::BoatValue *valueList[]={longitude,latitude,testValue}; GwApi::Status status; + api->setCounterDisplayName("usertest"); while(true){ delay(1000); /* @@ -193,7 +195,9 @@ void exampleTask(GwApi *api){ status.tcpClRx, status.tcpClTx, status.n2kRx, - status.n2kTx); + status.n2kTx); + //increment some counter + api->increment("Test"); } vTaskDelete(NULL); diff --git a/lib/exampletask/GwExampleTask.h b/lib/exampletask/GwExampleTask.h index 643c478..f7761b2 100644 --- a/lib/exampletask/GwExampleTask.h +++ b/lib/exampletask/GwExampleTask.h @@ -2,32 +2,11 @@ #include "GwApi.h" //we only compile for some boards #ifdef BOARD_TEST - -#define ESP32_CAN_TX_PIN GPIO_NUM_22 -#define ESP32_CAN_RX_PIN GPIO_NUM_19 -//if using tail485 -#define GWSERIAL_TX 26 -#define GWSERIAL_RX 32 -#define GWSERIAL_MODE "UNI" - -#define GWSERIAL2_TX 14 -#define GWSERIAL2_RX 15 -#define GWSERIAL2_MODE "BI" - -#define GWBUTTON_PIN GPIO_NUM_39 -#define GWBUTTON_ACTIVE LOW -//if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown -#define GWBUTTON_PULLUPDOWN -//led handling -//if we define GWLED_FASTNET the arduino fastnet lib is used -#define GWLED_FASTLED -#define GWLED_TYPE SK6812 -//color schema for fastled -#define GWLED_SCHEMA GRB -#define GWLED_PIN GPIO_NUM_27 -//brightness 0...255 -#define GWLED_BRIGHTNESS 64 -#define USBSerial Serial +//we could add the following defines also in our local platformio.ini +//CAN base +#define M5_CAN_KIT +//RS485 on groove +#define SERIAL_GROOVE_485 void exampleTask(GwApi *param); void exampleInit(GwApi *param); diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp index 1e36e13..9b4bc04 100644 --- a/lib/usercode/GwUserCode.cpp +++ b/lib/usercode/GwUserCode.cpp @@ -1,8 +1,15 @@ +#define DECLARE_USERTASK(task) GwUserTaskDef __##task##__(task,#task); +#define DECLARE_USERTASK_PARAM(task,...) GwUserTaskDef __##task##__(task,#task,__VA_ARGS__); +#define DECLARE_INITFUNCTION(task) GwInitTask __Init##task##__(task,#task); +#define DECLARE_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,#value); +#define DECLARE_STRING_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,value); + #include "GwUserCode.h" #include "GwSynchronized.h" #include #include #include +#include "GwCounter.h" //user task handling @@ -38,25 +45,24 @@ class GwUserCapability{ userCapabilities[name]=value; } }; -#define DECLARE_USERTASK(task) GwUserTaskDef __##task##__(task,#task); -#define DECLARE_USERTASK_PARAM(task,...) GwUserTaskDef __##task##__(task,#task,__VA_ARGS__); -#define DECLARE_INITFUNCTION(task) GwInitTask __Init##task##__(task,#task); -#define DECLARE_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,#value); -#define DECLARE_STRING_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,value); -#include "GwApi.h" #include "GwUserTasks.h" -class TaskApi : public GwApi +class TaskApi : public GwApiInternal { - GwApi *api; + GwApiInternal *api; int sourceId; SemaphoreHandle_t *mainLock; + SemaphoreHandle_t localLock; + GwCounter *counter=NULL; + bool counterUsed=false; public: - TaskApi(GwApi *api, int sourceId, SemaphoreHandle_t *mainLock) + TaskApi(GwApiInternal *api, int sourceId, SemaphoreHandle_t *mainLock, const String &name) { this->sourceId = sourceId; this->api = api; this->mainLock=mainLock; + localLock=xSemaphoreCreateMutex(); + counter=new GwCounter("count"+name); } virtual GwRequestQueue *getQueue() { @@ -104,10 +110,39 @@ public: GWSYNCHRONIZED(mainLock); api->getStatus(status); } - virtual ~TaskApi(){}; + virtual ~TaskApi(){ + vSemaphoreDelete(localLock); + delete counter; + }; + virtual void fillStatus(GwJsonDocument &status){ + GWSYNCHRONIZED(&localLock); + if (! counterUsed) return; + return counter->toJson(status); + }; + virtual int getJsonSize(){ + GWSYNCHRONIZED(&localLock); + if (! counterUsed) return 0; + return counter->getJsonSize(); + }; + virtual void increment(const String &name,bool failed=false){ + GWSYNCHRONIZED(&localLock); + counterUsed=true; + if (failed) counter->addFail(name); + else (counter->add(name)); + }; + virtual void reset(){ + GWSYNCHRONIZED(&localLock); + counterUsed=true; + counter->reset(); + }; + virtual void setCounterDisplayName(const String &name){ + GWSYNCHRONIZED(&localLock); + counterUsed=true; + counter->setName("count"+name); + } }; -GwUserCode::GwUserCode(GwApi *api,SemaphoreHandle_t *mainLock){ +GwUserCode::GwUserCode(GwApiInternal *api,SemaphoreHandle_t *mainLock){ this->logger=api->getLogger(); this->api=api; this->mainLock=mainLock; @@ -123,8 +158,8 @@ void userTaskStart(void *p){ delete task->api; task->api=NULL; } -void GwUserCode::startAddOnTask(GwApi *api,GwUserTask *task,int sourceId,String name){ - task->api=new TaskApi(api,sourceId,mainLock); +void GwUserCode::startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name){ + task->api=new TaskApi(api,sourceId,mainLock,name); xTaskCreate(userTaskStart,name.c_str(),task->stackSize,task,3,NULL); } void GwUserCode::startUserTasks(int baseId){ @@ -139,7 +174,7 @@ void GwUserCode::startInitTasks(int baseId){ LOG_DEBUG(GwLog::DEBUG,"starting %d user init tasks",initTasks.size()); for (auto it=initTasks.begin();it != initTasks.end();it++){ LOG_DEBUG(GwLog::LOG,"starting user init task %s with id %d",it->name.c_str(),baseId); - it->api=new TaskApi(api,baseId,mainLock); + it->api=new TaskApi(api,baseId,mainLock,it->name); userTaskStart(&(*it)); baseId++; } @@ -152,4 +187,21 @@ void GwUserCode::startAddonTask(String name, TaskFunction_t task, int id){ GwUserCode::Capabilities * GwUserCode::getCapabilities(){ return &userCapabilities; +} + +void GwUserCode::fillStatus(GwJsonDocument &status){ + for (auto it=userTasks.begin();it != userTasks.end();it++){ + if (it->api){ + it->api->fillStatus(status); + } + } +} +int GwUserCode::getJsonSize(){ + int rt=0; + for (auto it=userTasks.begin();it != userTasks.end();it++){ + if (it->api){ + rt+=it->api->getJsonSize(); + } + } + return rt; } \ No newline at end of file diff --git a/lib/usercode/GwUserCode.h b/lib/usercode/GwUserCode.h index 47bc07e..e9b45f5 100644 --- a/lib/usercode/GwUserCode.h +++ b/lib/usercode/GwUserCode.h @@ -2,16 +2,24 @@ #define _GWUSERCODE_H #include #include +#include "GwApi.h" +#include "GwJsonDocument.h" class GwLog; -class GwApi; typedef void (*GwUserTaskFunction)(GwApi *); + +class GwApiInternal : public GwApi{ + public: + ~GwApiInternal(){} + virtual void fillStatus(GwJsonDocument &status){}; + virtual int getJsonSize(){return 0;}; +}; class GwUserTask{ public: String name; TaskFunction_t task=NULL; GwUserTaskFunction usertask=NULL; bool isUserTask=false; - GwApi *api=NULL; + GwApiInternal *api=NULL; int stackSize=2000; GwUserTask(String name,TaskFunction_t task,int stackSize=2000){ this->name=name; @@ -25,17 +33,21 @@ class GwUserTask{ this->stackSize=stackSize; } }; + + class GwUserCode{ GwLog *logger; - GwApi *api; + GwApiInternal *api; SemaphoreHandle_t *mainLock; - void startAddOnTask(GwApi *api,GwUserTask *task,int sourceId,String name); + void startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name); public: typedef std::map Capabilities; - GwUserCode(GwApi *api, SemaphoreHandle_t *mainLock); + GwUserCode(GwApiInternal *api, SemaphoreHandle_t *mainLock); void startUserTasks(int baseId); void startInitTasks(int baseId); void startAddonTask(String name,TaskFunction_t task, int id); Capabilities *getCapabilities(); + void fillStatus(GwJsonDocument &status); + int getJsonSize(); }; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b928ad4..5d46bf8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -235,7 +235,7 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId,bool conv }); } -class ApiImpl : public GwApi +class ApiImpl : public GwApiInternal { private: int sourceId = -1; @@ -374,7 +374,8 @@ protected: GwJsonDocument status(300 + countNMEA2KIn.getJsonSize()+ countNMEA2KOut.getJsonSize() + - channels.getJsonSize() + channels.getJsonSize()+ + userCodeHandler.getJsonSize() ); status["version"] = VERSION; status["wifiConnected"] = gwWifi.clientConnected(); @@ -406,6 +407,7 @@ protected: countNMEA2KIn.toJson(status); countNMEA2KOut.toJson(status); channels.toJson(status); + userCodeHandler.fillStatus(status); serializeJson(status, result); } };