add new function getBoatDataValues to api for easier access

This commit is contained in:
wellenvogel 2021-12-03 17:00:51 +01:00
parent b4b08fa12d
commit 04d90447e8
7 changed files with 106 additions and 7 deletions

View File

@ -5,9 +5,16 @@
#include "NMEA0183Msg.h"
#include "GWConfig.h"
#include "GwBoatData.h"
#include <map>
#include <vector>
//API to be used for additional tasks
class GwApi{
public:
typedef std::map<String,double> ValueMap;
typedef std::vector<String> 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

View File

@ -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;

View File

@ -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 T> class GwBoatItem : public GwBoatItemBase{
@ -88,6 +90,7 @@ template<class T> 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<GwSatInfoList>
@ -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<class T> GwBoatItem<T> *getOrCreate(T initial,GwBoatItemNameProvider *provider);
template<class T> bool update(T value,int source,GwBoatItemNameProvider *provider);
template<class T> T getDataWithDefault(T defaultv, GwBoatItemNameProvider *provider);
bool isValid(String name);
double getDoubleValue(String name,double defaultv);
String toJson() const;
String toString();
};

View File

@ -3,6 +3,7 @@
#ifdef BOARD_TEST
#include "GwExampleTask.h"
#include "GwApi.h"
#include <vector>
/**
* 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);

View File

@ -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

View File

@ -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(){};
};

View File

@ -339,6 +339,15 @@ public:
virtual GwLog* getLogger(){
return &logger;
}
virtual GwApi::ValueMap getBoatDataValues(GwApi::StringList names){
std::map<String,double> 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\"}"