diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h index 26c95e2..c9f2cc5 100644 --- a/lib/api/GwApi.h +++ b/lib/api/GwApi.h @@ -5,9 +5,16 @@ #include "NMEA0183Msg.h" #include "GWConfig.h" #include "GwBoatData.h" +#include +#include //API to be used for additional tasks class GwApi{ public: + typedef std::map ValueMap; + typedef std::vector StringList; + /** + * thread safe methods - can directly be called from a user task + */ virtual GwRequestQueue *getQueue()=0; virtual void sendN2kMessage(const tN2kMsg &msg, bool convert=true)=0; /** @@ -16,10 +23,27 @@ class GwApi{ virtual void sendNMEA0183Message(const tNMEA0183Msg &msg, int sourceId,bool convert=true)=0; virtual void sendNMEA0183Message(const tNMEA0183Msg &msg, bool convert=true)=0; virtual int getSourceId()=0; + /** + * the returned config data must only be read in a user task + * writing to it is not thread safe and could lead to crashes + */ virtual GwConfigHandler *getConfig()=0; virtual GwLog *getLogger()=0; - virtual GwBoatData *getBoatData()=0; virtual const char* getTalkerId()=0; + /** + * get a set of boat data values by their names + * the returned map will have the keys being the strings in the names list + * the values are the boat data values converted to double + * invalid values are not returned in the map + * this method is thread safe and can directly be used from a user task + */ + virtual ValueMap getBoatDataValues(StringList)=0; + /** + * not thread safe methods + * accessing boat data must only be executed from within the main thread + * you need to use the request pattern as shown in GwExampleTask.cpp + */ + virtual GwBoatData *getBoatData()=0; virtual ~GwApi(){} }; #ifndef DECLARE_USERTASK diff --git a/lib/boatData/GwBoatData.cpp b/lib/boatData/GwBoatData.cpp index fca4d4b..5e5cb51 100644 --- a/lib/boatData/GwBoatData.cpp +++ b/lib/boatData/GwBoatData.cpp @@ -329,6 +329,17 @@ String GwBoatData::toString(){ } return rt; } +bool GwBoatData::isValid(String name){ + auto it=values.find(name); + if (it == values.end()) return false; + return it->second->isValid(); +} +double GwBoatData::getDoubleValue(String name,double defaultv){ + auto it=values.find(name); + if (it == values.end()) return defaultv; + if (! it->second->isValid()) return defaultv; + return it->second->getDoubleValue(); +} double formatCourse(double cv) { double rt = cv * 180.0 / M_PI; diff --git a/lib/boatData/GwBoatData.h b/lib/boatData/GwBoatData.h index 8d5f7f1..7329e8d 100644 --- a/lib/boatData/GwBoatData.h +++ b/lib/boatData/GwBoatData.h @@ -69,7 +69,9 @@ class GwBoatItemBase{ virtual size_t getJsonSize(); virtual int getLastSource(){return lastUpdateSource;} virtual void refresh(unsigned long ts=0){uls(ts);} + virtual double getDoubleValue()=0; String getName(){return name;} + String getFormat(){return format;} }; class GwBoatData; template class GwBoatItem : public GwBoatItemBase{ @@ -88,6 +90,7 @@ template class GwBoatItem : public GwBoatItemBase{ if (! isValid(millis())) return defaultv; return data; } + virtual double getDoubleValue(){return (double)data;} virtual void fillString(); virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime); virtual int getLastSource(){return lastUpdateSource;} @@ -120,6 +123,7 @@ class GwSatInfoList{ if (idx >= 0 && idx < sats.size()) return &sats.at(idx); return NULL; } + operator double(){ return getNumSats();} }; class GwBoatDataSatList : public GwBoatItem @@ -136,6 +140,9 @@ public: if (! isValid()) return 0; return data.getNumSats(); } + virtual double getDoubleValue(){ + return (double)(data.getNumSats()); + } }; @@ -199,6 +206,8 @@ class GwBoatData{ template GwBoatItem *getOrCreate(T initial,GwBoatItemNameProvider *provider); template bool update(T value,int source,GwBoatItemNameProvider *provider); template T getDataWithDefault(T defaultv, GwBoatItemNameProvider *provider); + bool isValid(String name); + double getDoubleValue(String name,double defaultv); String toJson() const; String toString(); }; diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp index ab1c12d..5f6a3a2 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 /** * an init function that ist being called before other initializations from the core @@ -48,7 +49,11 @@ void exampleTask(GwApi *api){ //initialization goes here //------ bool hasPosition=false; + bool hasPosition2=false; LOG_DEBUG(GwLog::DEBUG,"example switch ist %s",exampleSwitch?"true":"false"); + GwApi::StringList itemNames; + itemNames.push_back(F("Latitude")); + itemNames.push_back(F("Longitude")); while(true){ delay(1000); /* @@ -89,6 +94,42 @@ void exampleTask(GwApi *api){ } r->unref(); //delete the request + + /** second example with string based functions to access boatData + uses STL vector and map - but does not need any request handling + this uses a reasonable amount of mmeory on the stack: + up to 2x the size of the list of names and 2x the size of the returned map + the default stack size of 2000 will not fit. + So be sure to use DECLARA_USERTASK_PARAM(taskFuntion,stackSize) and provide + a reasonable stack size (e.g. 4000 in this example). + Finally it only makes sense to use one of the versions - either with the request + or with the ValueMap approach. + The request access to boatData gives you more options on how to access the data + and ueses less ressources and runtime but this one is maybe easier to understand and implement + **/ + //fetch the current values of the items that we have in itemNames + GwApi::ValueMap boatItems=api->getBoatDataValues(itemNames); + //get the values out of the map + //the returned item is a map iterator + auto longitude=boatItems.find(itemNames[1]); + auto latitude=boatItems.find(itemNames[0]); + //check if the iterators are valid (i.e. the values we requested have been found in boatData) + if (longitude != boatItems.end() && latitude != boatItems.end()){ + //both values are there - so we have a valid position + if (! hasPosition2){ + //access to the values via iterator->second (iterator->first would be the name) + if (exampleSwitch) LOG_DEBUG(GwLog::LOG,"(2)position availale lat=%f, lon=%f", + latitude->second,longitude->second); + hasPosition2=true; + } + } + else{ + if (hasPosition2){ + if (exampleSwitch) LOG_DEBUG(GwLog::LOG,"(2)position lost"); + hasPosition2=false; + } + } + } vTaskDelete(NULL); diff --git a/lib/exampletask/GwExampleTask.h b/lib/exampletask/GwExampleTask.h index ed84eb6..28704eb 100644 --- a/lib/exampletask/GwExampleTask.h +++ b/lib/exampletask/GwExampleTask.h @@ -27,10 +27,11 @@ void exampleTask(GwApi *param); void exampleInit(GwApi *param); //make the task known to the core //the task function should not return (unless you delete the task - see example code) -DECLARE_USERTASK(exampleTask) -//if your task needs more stack then the default 2000 bytes, replace the DECLARE_USERTASK -//with: DECLARE_USERTASK_PARAM(exampleTask,2500); -//this will give you 2500 bytes of stack +//we create our task with a stack size of 4000 bytes +DECLARE_USERTASK_PARAM(exampleTask,4000) +//if your task is happy with the default 2000 bytes of stack, replace the DECLARE_USERTASK_PARAM +//with: DECLARE_USERTASK(exampleTask); + //let the core call an init function before the //N2K Stuff and the communication is set up diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp index 7f6da3e..3c1e07f 100644 --- a/lib/usercode/GwUserCode.cpp +++ b/lib/usercode/GwUserCode.cpp @@ -95,6 +95,10 @@ public: virtual const char* getTalkerId(){ return api->getTalkerId(); } + virtual ValueMap getBoatDataValues(StringList names){ + GWSYNCHRONIZED(mainLock); + return api->getBoatDataValues(names); + } virtual ~TaskApi(){}; }; diff --git a/src/main.cpp b/src/main.cpp index d342ddc..7564c25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -339,6 +339,15 @@ public: virtual GwLog* getLogger(){ return &logger; } + virtual GwApi::ValueMap getBoatDataValues(GwApi::StringList names){ + std::map rt; + for (auto it=names.begin();it!= names.end();it++){ + if (boatData.isValid(*it)){ + rt[*it]=boatData.getDoubleValue(*it,0); + } + } + return rt; + } virtual GwBoatData *getBoatData(){ return &boatData; } @@ -357,8 +366,8 @@ bool delayedRestart(){ vTaskDelete(NULL); },"reset",1000,&logger,0,NULL) == pdPASS; } - -GwUserCode userCodeHandler(new ApiImpl(MIN_USER_TASK),&mainLock); +ApiImpl *apiImpl=new ApiImpl(MIN_USER_TASK); +GwUserCode userCodeHandler(apiImpl,&mainLock); #define JSON_OK "{\"status\":\"OK\"}"