introduce a type safe task adta exchange interface
This commit is contained in:
parent
b5210a79e8
commit
6758c59a3a
|
@ -17,6 +17,7 @@ CFG_INCLUDE='GwConfigDefinitions.h'
|
||||||
CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
|
CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
|
||||||
XDR_INCLUDE='GwXdrTypeMappings.h'
|
XDR_INCLUDE='GwXdrTypeMappings.h'
|
||||||
TASK_INCLUDE='GwUserTasks.h'
|
TASK_INCLUDE='GwUserTasks.h'
|
||||||
|
TASK_INCLUDE_IF='GwUserTasksIf.h'
|
||||||
EMBEDDED_INCLUDE="GwEmbeddedFiles.h"
|
EMBEDDED_INCLUDE="GwEmbeddedFiles.h"
|
||||||
|
|
||||||
def getEmbeddedFiles(env):
|
def getEmbeddedFiles(env):
|
||||||
|
@ -219,23 +220,28 @@ def getUserTaskDirs():
|
||||||
for task in taskdirs:
|
for task in taskdirs:
|
||||||
rt.append(task)
|
rt.append(task)
|
||||||
return rt
|
return rt
|
||||||
def genereateUserTasks(outfile):
|
|
||||||
|
def checkAndAdd(file,names,ilist):
|
||||||
|
if not file.endswith('.h'):
|
||||||
|
return
|
||||||
|
match=False
|
||||||
|
for cmp in names:
|
||||||
|
#print("##check %s<->%s"%(f.lower(),cmp))
|
||||||
|
if file.lower() == cmp:
|
||||||
|
match=True
|
||||||
|
if not match:
|
||||||
|
return
|
||||||
|
ilist.append(file)
|
||||||
|
def genereateUserTasks(outfile,forIf=False):
|
||||||
includes=[]
|
includes=[]
|
||||||
for task in userTaskDirs:
|
for task in userTaskDirs:
|
||||||
#print("##taskdir=%s"%task)
|
#print("##taskdir=%s"%task)
|
||||||
base=os.path.basename(task)
|
base=os.path.basename(task)
|
||||||
includeNames=[base.lower()+".h",'gw'+base.lower()+'.h']
|
includeNames=[base.lower()+".h",'gw'+base.lower()+'.h']
|
||||||
|
if forIf:
|
||||||
|
includeNames=["i"+base.lower()+".h",'gwi'+base.lower()+'.h']
|
||||||
for f in os.listdir(task):
|
for f in os.listdir(task):
|
||||||
if not f.endswith('.h'):
|
checkAndAdd(f,includeNames,includes)
|
||||||
continue
|
|
||||||
match=False
|
|
||||||
for cmp in includeNames:
|
|
||||||
#print("##check %s<->%s"%(f.lower(),cmp))
|
|
||||||
if f.lower() == cmp:
|
|
||||||
match=True
|
|
||||||
if not match:
|
|
||||||
continue
|
|
||||||
includes.append(f)
|
|
||||||
includeData=""
|
includeData=""
|
||||||
for i in includes:
|
for i in includes:
|
||||||
print("#task include %s"%i)
|
print("#task include %s"%i)
|
||||||
|
@ -291,6 +297,7 @@ def prebuild(env):
|
||||||
print("#WARNING: infile %s for %s not found"%(inFile,ef))
|
print("#WARNING: infile %s for %s not found"%(inFile,ef))
|
||||||
generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
|
generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
|
||||||
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
|
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
|
||||||
|
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE_IF),forIf=True)
|
||||||
generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
|
generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
|
||||||
version="dev"+datetime.now().strftime("%Y%m%d")
|
version="dev"+datetime.now().strftime("%Y%m%d")
|
||||||
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
|
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
|
||||||
|
|
104
lib/api/GwApi.h
104
lib/api/GwApi.h
|
@ -5,6 +5,7 @@
|
||||||
#include "NMEA0183Msg.h"
|
#include "NMEA0183Msg.h"
|
||||||
#include "GWConfig.h"
|
#include "GWConfig.h"
|
||||||
#include "GwBoatData.h"
|
#include "GwBoatData.h"
|
||||||
|
#include <map>
|
||||||
//API to be used for additional tasks
|
//API to be used for additional tasks
|
||||||
class GwApi{
|
class GwApi{
|
||||||
public:
|
public:
|
||||||
|
@ -33,29 +34,31 @@ class GwApi{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* a simple value container
|
* an interface for the data exchange between tasks
|
||||||
* to exchange data between tasks
|
* the core part will not handle this data at all but
|
||||||
|
* this interface ensures that there is a correct locking of the
|
||||||
|
* data to correctly handle multi threading
|
||||||
|
* The user code should not use this intterface directly
|
||||||
|
* but instead it should use the static functions
|
||||||
|
* apiGetXXX(GwApi *,...)
|
||||||
|
* apiSetXXX(GwApi *,...)
|
||||||
|
* that will be created by the macro DECLARE_TASK_INTERFACE
|
||||||
*/
|
*/
|
||||||
class Value{
|
class TaskInterfaces
|
||||||
long lvalue=0;
|
{
|
||||||
String svalue;
|
|
||||||
bool isString=false;
|
|
||||||
bool isValid=false;
|
|
||||||
public:
|
public:
|
||||||
Value(const String &v){isString=true;svalue=v;isValid=true;}
|
class Base
|
||||||
Value(long l){lvalue=l;isValid=true;}
|
{
|
||||||
Value(){}
|
public:
|
||||||
long getLValue() const{
|
virtual ~Base()
|
||||||
if(!isString) return lvalue;
|
{
|
||||||
return atol(svalue.c_str());
|
|
||||||
}
|
}
|
||||||
String getSValue() const{
|
|
||||||
if(isString) return svalue;
|
|
||||||
return String(lvalue);
|
|
||||||
}
|
|
||||||
bool valid() const{return isValid;}
|
|
||||||
};
|
};
|
||||||
|
using Ptr = std::shared_ptr<Base>;
|
||||||
|
public:
|
||||||
|
virtual bool iset(const String &file, const String &name, Ptr v) = 0;
|
||||||
|
virtual Ptr iget(const String &name, int &result) = 0;
|
||||||
|
};
|
||||||
class Status{
|
class Status{
|
||||||
public:
|
public:
|
||||||
typedef enum{
|
typedef enum{
|
||||||
|
@ -156,14 +159,7 @@ class GwApi{
|
||||||
virtual void increment(int idx,const String &name,bool failed=false){}
|
virtual void increment(int idx,const String &name,bool failed=false){}
|
||||||
virtual void reset(int idx){}
|
virtual void reset(int idx){}
|
||||||
virtual void remove(int idx){}
|
virtual void remove(int idx){}
|
||||||
|
virtual TaskInterfaces * taskInterfaces()=0;
|
||||||
/**
|
|
||||||
* exchange data between different user tasks
|
|
||||||
* each task can set arbitrary items
|
|
||||||
* that can be accessed by other tasks
|
|
||||||
*/
|
|
||||||
virtual void setTaskValue(const String &name,const Value &v){}
|
|
||||||
virtual Value getTaskValue(const String &taskName,const String &name){return Value();}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* not thread safe methods
|
* not thread safe methods
|
||||||
|
@ -188,4 +184,58 @@ class GwApi{
|
||||||
#ifndef DECLARE_STRING_CAPABILITY
|
#ifndef DECLARE_STRING_CAPABILITY
|
||||||
#define DECLARE_STRING_CAPABILITY(name,value)
|
#define DECLARE_STRING_CAPABILITY(name,value)
|
||||||
#endif
|
#endif
|
||||||
|
/**
|
||||||
|
* macro to declare an interface that a task provides to other tasks
|
||||||
|
* this macro must be used in an File I<taskname>.h or GwI<taskname>.h
|
||||||
|
* the first parameter must be the name of the task that should be able
|
||||||
|
* to write this data (the same name as being used in DECLARE_TASK).
|
||||||
|
* The second parameter must be a type (class) that inherits from
|
||||||
|
* GwApi::TaskInterfaces::Base.
|
||||||
|
* This class must provide copy constructors and empty constructors.
|
||||||
|
* The attributes should only be simple values or structs but no pointers.
|
||||||
|
* example:
|
||||||
|
* class TestTaskApi: public GwApi::TaskInterfaces::Base{
|
||||||
|
* public:
|
||||||
|
* int ival1=0;
|
||||||
|
* int ival2=99;
|
||||||
|
* String sval="unset";
|
||||||
|
* };
|
||||||
|
* DECLARE_TASKIF(testTask,TestTaskApi);
|
||||||
|
* The macro will generate 2 static funtions:
|
||||||
|
*
|
||||||
|
* bool apiSetTestTaskApi(GwApi *api, const TestTaskApi &v);
|
||||||
|
* TestTaskApi apiGetTestTaskApi(GwApi *api, int &result);
|
||||||
|
*
|
||||||
|
* The setter will return true on success.
|
||||||
|
* It is intended to be used by the task that did declare the api.
|
||||||
|
* The getter will set the result to -1 if no data is available, otherwise
|
||||||
|
* it will return the update count (so you can check if there was a change
|
||||||
|
* compared to the last call).
|
||||||
|
* It is intended to be used by any task.
|
||||||
|
* Be aware that all the apis share a common namespace - so be sure to somehow
|
||||||
|
* make your API names unique.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define DECLARE_TASKIF_IMPL(task,type) \
|
||||||
|
static bool apiSet##type(GwApi *a,const type &v){ \
|
||||||
|
if (! a || ! a->taskInterfaces()) return false; \
|
||||||
|
return a->taskInterfaces()->iset(__FILE__,#type,GwApi::TaskInterfaces::Ptr(new type(v)));\
|
||||||
|
}\
|
||||||
|
static const type apiGet##type(GwApi *a, int &result) {\
|
||||||
|
if (! a || ! a->taskInterfaces()) {\
|
||||||
|
result=-1; \
|
||||||
|
return type(); \
|
||||||
|
}\
|
||||||
|
auto ptr=a->taskInterfaces()->iget(#type,result); \
|
||||||
|
if (!ptr) {\
|
||||||
|
result=-1; \
|
||||||
|
return type(); \
|
||||||
|
}\
|
||||||
|
type *tp=(type*)ptr.get(); \
|
||||||
|
return type(*tp); \
|
||||||
|
}
|
||||||
|
#ifndef DECLARE_TASKIF
|
||||||
|
#define DECLARE_TASKIF(task,type) DECLARE_TASKIF_IMPL(task,type)
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef GWIEXAMPLETASK_H
|
||||||
|
#define GWIEXAMPLETASK_H
|
||||||
|
#include "GwApi.h"
|
||||||
|
/**
|
||||||
|
* an interface for the example task
|
||||||
|
*/
|
||||||
|
class ExampleIf : public GwApi::TaskInterfaces::Base{
|
||||||
|
public:
|
||||||
|
long count=0;
|
||||||
|
String someValue;
|
||||||
|
};
|
||||||
|
DECLARE_TASKIF(exampleTask,ExampleIf);
|
||||||
|
|
||||||
|
#endif
|
|
@ -17,7 +17,46 @@
|
||||||
std::vector<GwUserTask> userTasks;
|
std::vector<GwUserTask> userTasks;
|
||||||
std::vector<GwUserTask> initTasks;
|
std::vector<GwUserTask> initTasks;
|
||||||
GwUserCode::Capabilities userCapabilities;
|
GwUserCode::Capabilities userCapabilities;
|
||||||
|
class RegEntry{
|
||||||
|
public:
|
||||||
|
String file;
|
||||||
|
String task;
|
||||||
|
RegEntry(const String &t, const String &f):file(f),task(t){}
|
||||||
|
RegEntry(){}
|
||||||
|
};
|
||||||
|
using RegMap=std::map<String,RegEntry>;
|
||||||
|
static RegMap ®istrations(){
|
||||||
|
static RegMap *regMap=new RegMap();
|
||||||
|
return *regMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registerInterface(const String &task,const String &file, const String &name){
|
||||||
|
auto it=registrations().find(name);
|
||||||
|
if (it != registrations().end()){
|
||||||
|
if (it->second.file != file){
|
||||||
|
ESP_LOGE("Assert","type %s redefined in %s original in %s",name,file,it->second.file);
|
||||||
|
std::abort();
|
||||||
|
};
|
||||||
|
if (it->second.task != task){
|
||||||
|
ESP_LOGE("Assert","type %s registered for multiple tasks %s and %s",name,task,it->second.task);
|
||||||
|
std::abort();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
registrations()[name]=RegEntry(task,file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GwIreg{
|
||||||
|
public:
|
||||||
|
GwIreg(const String &task,const String &file, const String &name){
|
||||||
|
registerInterface(task,file,name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DECLARE_TASKIF(task,type) \
|
||||||
|
DECLARE_TASKIF_IMPL(task,type) \
|
||||||
|
GwIreg __register##type(#task,__FILE__,#type)
|
||||||
|
|
||||||
|
|
||||||
class GwUserTaskDef{
|
class GwUserTaskDef{
|
||||||
|
@ -46,44 +85,79 @@ class GwUserCapability{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#include "GwUserTasks.h"
|
#include "GwUserTasks.h"
|
||||||
class TaskData{
|
#include "GwUserTasksIf.h"
|
||||||
SemaphoreHandle_t lock;
|
|
||||||
std::map<String,GwApi::Value> data;
|
|
||||||
GwLog *logger;
|
|
||||||
String computeKey(const String &taskName,const String &name) const{
|
|
||||||
return taskName+":#:"+name;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
TaskData(GwLog *l){
|
|
||||||
lock=xSemaphoreCreateMutex();
|
|
||||||
logger=l;
|
|
||||||
}
|
|
||||||
~TaskData(){
|
|
||||||
vSemaphoreDelete(lock);
|
|
||||||
}
|
|
||||||
void setTaskValue(const String &taskName,const String &name,const GwApi::Value &v){
|
|
||||||
String key=computeKey(taskName,name);
|
|
||||||
LOG_DEBUG(GwLog::DEBUG,"set task data %s=%s",key.c_str(),v.getSValue().c_str());
|
|
||||||
GWSYNCHRONIZED(&lock);
|
|
||||||
data[key]=v;
|
|
||||||
}
|
|
||||||
GwApi::Value getTaskValue(const String &taskName,const String &name){
|
|
||||||
GwApi::Value rt;
|
|
||||||
String key=computeKey(taskName,name);
|
|
||||||
{
|
|
||||||
GWSYNCHRONIZED(&lock);
|
|
||||||
auto it=data.find(key);
|
|
||||||
if (it != data.end()) rt=it->second;
|
|
||||||
}
|
|
||||||
LOG_DEBUG(GwLog::DEBUG,"get task data %s:%s (valid=%d)",key.c_str(),rt.getSValue().c_str(),(int)rt.valid());
|
|
||||||
return rt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
class TaskDataEntry{
|
||||||
|
public:
|
||||||
|
GwApi::TaskInterfaces::Ptr ptr;
|
||||||
|
int updates=0;
|
||||||
|
TaskDataEntry(GwApi::TaskInterfaces::Ptr p):ptr(p){}
|
||||||
|
TaskDataEntry(){}
|
||||||
};
|
};
|
||||||
|
class TaskInterfacesStorage{
|
||||||
|
GwLog *logger;
|
||||||
|
SemaphoreHandle_t lock;
|
||||||
|
std::map<String,TaskDataEntry> values;
|
||||||
|
public:
|
||||||
|
TaskInterfacesStorage(GwLog* l):
|
||||||
|
logger(l){
|
||||||
|
lock=xSemaphoreCreateMutex();
|
||||||
|
}
|
||||||
|
bool set(const String &file, const String &name, const String &task,GwApi::TaskInterfaces::Ptr v){
|
||||||
|
GWSYNCHRONIZED(&lock);
|
||||||
|
auto it=registrations().find(name);
|
||||||
|
if (it == registrations().end()){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"invalid set %s not known",name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (it->second.file != file){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"invalid set %s wrong file, expected %s , got %s",name.c_str(),it->second.file.c_str(),file.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (it->second.task != task){
|
||||||
|
LOG_DEBUG(GwLog::ERROR,"invalid set %s wrong task, expected %s , got %s",name.c_str(),it->second.task.c_str(),task.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto vit=values.find(name);
|
||||||
|
if (vit != values.end()){
|
||||||
|
vit->second.updates++;
|
||||||
|
vit->second.ptr=v;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
values[name]=TaskDataEntry(v);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
GwApi::TaskInterfaces::Ptr get(const String &name, int &result){
|
||||||
|
GWSYNCHRONIZED(&lock);
|
||||||
|
auto it = values.find(name);
|
||||||
|
if (it == values.end())
|
||||||
|
{
|
||||||
|
result = -1;
|
||||||
|
return GwApi::TaskInterfaces::Ptr();
|
||||||
|
}
|
||||||
|
result = it->second.updates;
|
||||||
|
return it->second.ptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class TaskInterfacesImpl : public GwApi::TaskInterfaces{
|
||||||
|
String task;
|
||||||
|
TaskInterfacesStorage *storage;
|
||||||
|
public:
|
||||||
|
TaskInterfacesImpl(const String &n,TaskInterfacesStorage *s):
|
||||||
|
task(n),storage(s){}
|
||||||
|
virtual bool iset(const String &file, const String &name, Ptr v){
|
||||||
|
return storage->set(file,name,task,v);
|
||||||
|
}
|
||||||
|
virtual Ptr iget(const String &name, int &result){
|
||||||
|
return storage->get(name,result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class TaskApi : public GwApiInternal
|
class TaskApi : public GwApiInternal
|
||||||
{
|
{
|
||||||
GwApiInternal *api=nullptr;
|
GwApiInternal *api=nullptr;
|
||||||
TaskData *taskData=nullptr;
|
|
||||||
int sourceId;
|
int sourceId;
|
||||||
SemaphoreHandle_t *mainLock;
|
SemaphoreHandle_t *mainLock;
|
||||||
SemaphoreHandle_t localLock;
|
SemaphoreHandle_t localLock;
|
||||||
|
@ -91,20 +165,20 @@ class TaskApi : public GwApiInternal
|
||||||
String name;
|
String name;
|
||||||
bool counterUsed=false;
|
bool counterUsed=false;
|
||||||
int counterIdx=0;
|
int counterIdx=0;
|
||||||
|
TaskInterfacesImpl *interfaces;
|
||||||
public:
|
public:
|
||||||
TaskApi(GwApiInternal *api,
|
TaskApi(GwApiInternal *api,
|
||||||
int sourceId,
|
int sourceId,
|
||||||
SemaphoreHandle_t *mainLock,
|
SemaphoreHandle_t *mainLock,
|
||||||
const String &name,
|
const String &name,
|
||||||
TaskData *d)
|
TaskInterfacesStorage *s)
|
||||||
{
|
{
|
||||||
this->sourceId = sourceId;
|
this->sourceId = sourceId;
|
||||||
this->api = api;
|
this->api = api;
|
||||||
this->mainLock=mainLock;
|
this->mainLock=mainLock;
|
||||||
this->name=name;
|
this->name=name;
|
||||||
localLock=xSemaphoreCreateMutex();
|
localLock=xSemaphoreCreateMutex();
|
||||||
taskData=d;
|
interfaces=new TaskInterfacesImpl(name,s);
|
||||||
}
|
}
|
||||||
virtual GwRequestQueue *getQueue()
|
virtual GwRequestQueue *getQueue()
|
||||||
{
|
{
|
||||||
|
@ -153,6 +227,7 @@ public:
|
||||||
api->getStatus(status);
|
api->getStatus(status);
|
||||||
}
|
}
|
||||||
virtual ~TaskApi(){
|
virtual ~TaskApi(){
|
||||||
|
delete interfaces;
|
||||||
vSemaphoreDelete(localLock);
|
vSemaphoreDelete(localLock);
|
||||||
};
|
};
|
||||||
virtual void fillStatus(GwJsonDocument &status){
|
virtual void fillStatus(GwJsonDocument &status){
|
||||||
|
@ -202,11 +277,8 @@ public:
|
||||||
else it->second=GwCounter<String>("count"+name);
|
else it->second=GwCounter<String>("count"+name);
|
||||||
return counterIdx;
|
return counterIdx;
|
||||||
}
|
}
|
||||||
virtual void setTaskValue(const String &name,const Value &v){
|
virtual TaskInterfaces * taskInterfaces(){
|
||||||
taskData->setTaskValue(this->name,name,v);
|
return interfaces;
|
||||||
}
|
|
||||||
virtual Value getTaskValue(const String &taskName,const String &name){
|
|
||||||
return taskData->getTaskValue(taskName,name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -215,7 +287,10 @@ GwUserCode::GwUserCode(GwApiInternal *api,SemaphoreHandle_t *mainLock){
|
||||||
this->logger=api->getLogger();
|
this->logger=api->getLogger();
|
||||||
this->api=api;
|
this->api=api;
|
||||||
this->mainLock=mainLock;
|
this->mainLock=mainLock;
|
||||||
this->taskData=new TaskData(this->logger);
|
this->taskData=new TaskInterfacesStorage(this->logger);
|
||||||
|
}
|
||||||
|
GwUserCode::~GwUserCode(){
|
||||||
|
delete taskData;
|
||||||
}
|
}
|
||||||
void userTaskStart(void *p){
|
void userTaskStart(void *p){
|
||||||
GwUserTask *task=(GwUserTask*)p;
|
GwUserTask *task=(GwUserTask*)p;
|
||||||
|
|
|
@ -34,14 +34,15 @@ class GwUserTask{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TaskData;
|
class TaskInterfacesStorage;
|
||||||
class GwUserCode{
|
class GwUserCode{
|
||||||
GwLog *logger;
|
GwLog *logger;
|
||||||
GwApiInternal *api;
|
GwApiInternal *api;
|
||||||
SemaphoreHandle_t *mainLock;
|
SemaphoreHandle_t *mainLock;
|
||||||
TaskData *taskData;
|
TaskInterfacesStorage *taskData;
|
||||||
void startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name);
|
void startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name);
|
||||||
public:
|
public:
|
||||||
|
~GwUserCode();
|
||||||
typedef std::map<String,String> Capabilities;
|
typedef std::map<String,String> Capabilities;
|
||||||
GwUserCode(GwApiInternal *api, SemaphoreHandle_t *mainLock);
|
GwUserCode(GwApiInternal *api, SemaphoreHandle_t *mainLock);
|
||||||
void startUserTasks(int baseId);
|
void startUserTasks(int baseId);
|
||||||
|
|
|
@ -320,6 +320,7 @@ public:
|
||||||
return config.getString(config.talkerId,String("GP")).c_str();
|
return config.getString(config.talkerId,String("GP")).c_str();
|
||||||
}
|
}
|
||||||
virtual ~ApiImpl(){}
|
virtual ~ApiImpl(){}
|
||||||
|
virtual TaskInterfaces *taskInterfaces(){ return nullptr;}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool delayedRestart(){
|
bool delayedRestart(){
|
||||||
|
|
Loading…
Reference in New Issue