Merge branch 'wellenvogel:master' into feature/env2

This commit is contained in:
free-x 2021-11-24 21:31:28 +01:00 committed by GitHub
commit df126c9f34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 123 additions and 64 deletions

View File

@ -6,20 +6,46 @@ GwBoatData::GwBoatData(GwLog *logger){
GwBoatData::~GwBoatData(){ GwBoatData::~GwBoatData(){
GwBoatItemBase::GwBoatItemMap::iterator it; GwBoatItemBase::GwBoatItemMap::iterator it;
for (it=values.begin() ; it != values.end();it++){ for (it=values.begin() ; it != values.end();it++){
delete *it; delete it->second;
} }
} }
template<class T> GwBoatItem<T> *GwBoatData::getOrCreate(T dummy, String name, String format, template<class T> GwBoatItem<T> *GwBoatData::getOrCreate(T initial, GwBoatItemNameProvider *provider)
unsigned long invalidTime)
{ {
for (auto it=values.begin();it != values.end();it++){ String name=provider->getBoatItemName();
if ((*it)->getName() == name){ auto it=values.find(name);
return *it; if (it != values.end()) {
int expectedType=GwBoatItemTypes::getType(initial);
if (expectedType != it->second->getCurrentType()){
LOG_DEBUG(GwLog::DEBUG,"invalid type for boat item %s, expected %d, got %d",
name.c_str(),expectedType,it->second->getCurrentType());
return NULL;
} }
return (GwBoatItem<T>*)(it->second);
} }
return new GwBoatItem<T>(name,format,invalidTime,&values); GwBoatItem<T> *rt=new GwBoatItem<T>(GwBoatItemTypes::getType(initial), name,
provider->getBoatItemFormat(),
provider->getInvalidTime(),
&values);
rt->update(initial);
LOG_DEBUG(GwLog::LOG,"creating boatItem %s, type %d",
name.c_str(),rt->getCurrentType());
return rt;
} }
template<class T> bool GwBoatData::update(T value,int source,GwBoatItemNameProvider *provider){
GwBoatItem<T> *item=getOrCreate(value,provider);
if (! item) return false;
return item->update(value,source);
}
template bool GwBoatData::update<double>(double value,int source,GwBoatItemNameProvider *provider);
template<class T> T GwBoatData::getDataWithDefault(T defaultv, GwBoatItemNameProvider *provider){
auto it=values.find(provider->getBoatItemName());
if (it == values.end()) return defaultv;
int expectedType=GwBoatItemTypes::getType(defaultv);
if (expectedType != it->second->getCurrentType()) return defaultv;
return ((GwBoatItem<T> *)(it->second))->getDataWithDefault(defaultv);
}
template double GwBoatData::getDataWithDefault<double>(double defaultv, GwBoatItemNameProvider *provider);
String GwBoatData::toJson() const { String GwBoatData::toJson() const {
unsigned long minTime=millis(); unsigned long minTime=millis();
GwBoatItemBase::GwBoatItemMap::const_iterator it; GwBoatItemBase::GwBoatItemMap::const_iterator it;
@ -27,11 +53,13 @@ String GwBoatData::toJson() const {
size_t elementSizes=0; size_t elementSizes=0;
for (it=values.begin() ; it != values.end();it++){ for (it=values.begin() ; it != values.end();it++){
count++; count++;
elementSizes+=(*it)->getJsonSize(); elementSizes+=it->second->getJsonSize();
} }
DynamicJsonDocument json(JSON_OBJECT_SIZE(count)+elementSizes+10); int sz=JSON_OBJECT_SIZE(count)+elementSizes+10;
LOG_DEBUG(GwLog::DEBUG,"size for boatData: %d",sz);
DynamicJsonDocument json(sz);
for (it=values.begin() ; it != values.end();it++){ for (it=values.begin() ; it != values.end();it++){
(*it)->toJsonDoc(&json,minTime); it->second->toJsonDoc(&json,minTime);
} }
String buf; String buf;
serializeJson(json,buf); serializeJson(json,buf);

View File

@ -4,9 +4,21 @@
#include "GwLog.h" #include "GwLog.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <Arduino.h> #include <Arduino.h>
#include <vector> #include <map>
#define GW_BOAT_VALUE_LEN 32 #define GW_BOAT_VALUE_LEN 32
#define GWSC(name) static constexpr const __FlashStringHelper* name=F(#name) #define GWSC(name) static constexpr const __FlashStringHelper* name=F(#name)
#define GWTYPE_DOUBLE 1
#define GWTYPE_UINT32 2
#define GWTYPE_UINT16 3
#define GWTYPE_INT16 4
#define GWTYPE_USER 100
class GwBoatItemTypes{
public:
static int getType(const uint32_t &x){return GWTYPE_UINT32;}
static int getType(const uint16_t &x){return GWTYPE_UINT16;}
static int getType(const int16_t &x){return GWTYPE_INT16;}
static int getType(const double &x){return GWTYPE_DOUBLE;}
};
class GwBoatItemBase{ class GwBoatItemBase{
public: public:
static const unsigned long INVALID_TIME=60000; static const unsigned long INVALID_TIME=60000;
@ -23,8 +35,9 @@ class GwBoatItemBase{
GWSC(mtr2nm); GWSC(mtr2nm);
GWSC(formatDop); GWSC(formatDop);
GWSC(formatRot); GWSC(formatRot);
typedef std::vector<GwBoatItemBase*> GwBoatItemMap; typedef std::map<String,GwBoatItemBase*> GwBoatItemMap;
protected: protected:
int type;
unsigned long lastSet=0; unsigned long lastSet=0;
unsigned long invalidTime=INVALID_TIME; unsigned long invalidTime=INVALID_TIME;
String name; String name;
@ -34,6 +47,7 @@ class GwBoatItemBase{
else lastSet=millis(); else lastSet=millis();
} }
public: public:
int getCurrentType(){return type;}
unsigned long getLastSet() const {return lastSet;} unsigned long getLastSet() const {return lastSet;}
bool isValid(unsigned long now=0) const { bool isValid(unsigned long now=0) const {
if (lastSet == 0) return false; if (lastSet == 0) return false;
@ -46,13 +60,14 @@ class GwBoatItemBase{
this->invalidTime=invalidTime; this->invalidTime=invalidTime;
this->name=name; this->name=name;
this->format=format; this->format=format;
this->type=0;
} }
virtual ~GwBoatItemBase(){} virtual ~GwBoatItemBase(){}
void invalidate(){ void invalidate(){
lastSet=0; lastSet=0;
} }
virtual void toJsonDoc(JsonDocument *doc, unsigned long minTime)=0; virtual void toJsonDoc(JsonDocument *doc, unsigned long minTime)=0;
virtual size_t getJsonSize(){return JSON_OBJECT_SIZE(15);} virtual size_t getJsonSize(){return JSON_OBJECT_SIZE(10);}
virtual int getLastSource()=0; virtual int getLastSource()=0;
virtual void refresh(unsigned long ts=0){uls(ts);} virtual void refresh(unsigned long ts=0){uls(ts);}
String getName(){return name;} String getName(){return name;}
@ -63,10 +78,11 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
T data; T data;
int lastUpdateSource; int lastUpdateSource;
public: public:
GwBoatItem(String name,String formatInfo,unsigned long invalidTime=INVALID_TIME,GwBoatItemMap *map=NULL): GwBoatItem(int type,String name,String formatInfo,unsigned long invalidTime=INVALID_TIME,GwBoatItemMap *map=NULL):
GwBoatItemBase(name,formatInfo,invalidTime){ GwBoatItemBase(name,formatInfo,invalidTime){
this->type=type;
if (map){ if (map){
map->push_back(this); (*map)[name]=this;
} }
lastUpdateSource=-1; lastUpdateSource=-1;
} }
@ -77,7 +93,7 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
//priority handling //priority handling
//sources with lower ids will win //sources with lower ids will win
//and we will not overwrite their value //and we will not overwrite their value
if (lastUpdateSource < source){ if (lastUpdateSource < source && lastUpdateSource >= 0){
return false; return false;
} }
} }
@ -171,7 +187,8 @@ bool convertToJson(const GwSatInfoList &si,JsonVariant &variant);
class GwBoatDataSatList : public GwBoatItem<GwSatInfoList> class GwBoatDataSatList : public GwBoatItem<GwSatInfoList>
{ {
public: public:
GwBoatDataSatList(String name, String formatInfo, unsigned long invalidTime = INVALID_TIME, GwBoatItemMap *map = NULL) : GwBoatItem<GwSatInfoList>(name, formatInfo, invalidTime, map) {} GwBoatDataSatList(String name, String formatInfo, unsigned long invalidTime = INVALID_TIME, GwBoatItemMap *map = NULL) :
GwBoatItem<GwSatInfoList>(GWTYPE_USER+1, name, formatInfo, invalidTime, map) {}
bool update(GwSatInfo info, int source) bool update(GwSatInfo info, int source)
{ {
unsigned long now = millis(); unsigned long now = millis();
@ -205,8 +222,16 @@ public:
}; };
class GwBoatItemNameProvider
{
public:
virtual String getBoatItemName() = 0;
virtual String getBoatItemFormat() = 0;
virtual unsigned long getInvalidTime(){ return GwBoatItemBase::INVALID_TIME;}
virtual ~GwBoatItemNameProvider() {}
};
#define GWBOATDATA(type,name,time,fmt) \ #define GWBOATDATA(type,name,time,fmt) \
GwBoatItem<type> *name=new GwBoatItem<type>(F(#name),GwBoatItemBase::fmt,time,&values) ; GwBoatItem<type> *name=new GwBoatItem<type>(GwBoatItemTypes::getType((type)0),F(#name),GwBoatItemBase::fmt,time,&values) ;
#define GWSPECBOATDATA(clazz,name,time,fmt) \ #define GWSPECBOATDATA(clazz,name,time,fmt) \
clazz *name=new clazz(F(#name),GwBoatItemBase::fmt,time,&values) ; clazz *name=new clazz(F(#name),GwBoatItemBase::fmt,time,&values) ;
class GwBoatData{ class GwBoatData{
@ -254,43 +279,11 @@ class GwBoatData{
public: public:
GwBoatData(GwLog *logger); GwBoatData(GwLog *logger);
~GwBoatData(); ~GwBoatData();
template<class T> GwBoatItem<T> *getOrCreate(T dummy,String name,String format, template<class T> GwBoatItem<T> *getOrCreate(T initial,GwBoatItemNameProvider *provider);
unsigned long invalidTime=GwBoatItemBase::INVALID_TIME); template<class T> bool update(T value,int source,GwBoatItemNameProvider *provider);
template<class T> T getDataWithDefault(T defaultv, GwBoatItemNameProvider *provider);
String toJson() const; String toJson() const;
}; };
/**
* class for lazy creation of a boat item
* once we have someone that knows the name for it
* and providing fast access without searching all the time trough the map
* xdr mappings implement such a provider
*/
class GwBoatItemNameProvider
{
public:
virtual String getBoatItemName() = 0;
virtual String getBoatItemFormat() = 0;
virtual ~GwBoatItemNameProvider() {}
};
template<class T> class GwBoatItemHolder{
private:
GwBoatItem<T> *item=NULL;
GwBoatData *data;
unsigned long invalidTime=GwBoatItemBase::INVALID_TIME;
public:
GwBoatItemHolder(GwBoatData *data,unsigned long invalidTime=GwBoatItemBase::INVALID_TIME){
this->data=data;
this->invalidTime=invalidTime;
}
GwBoatItem<T> *getItem(GwBoatItemNameProvider *provider){
if (item) return item;
T dummy;
item=data->getOrCreate(dummy,
provider->getBoatItemName() ,
provider->getBoatItemFormat(),
invalidTime);
return item;
}
GwBoatItem<T> *getItem(){return item;}
};
#endif #endif

View File

@ -105,6 +105,10 @@ private:
return false; return false;
return item->update(value,sourceId); return item->update(value,sourceId);
} }
bool updateDouble(GwXDRFoundMapping * mapping, double &value){
if (mapping->empty) return false;
return boatData->update(value,sourceId,mapping);
}
unsigned long LastPosSend; unsigned long LastPosSend;
unsigned long NextRMCSend; unsigned long NextRMCSend;
@ -1234,7 +1238,7 @@ private:
return; return;
} }
GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,0,TemperatureInstance); GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRTEMP,(int)TemperatureSource,0,TemperatureInstance);
if (mapping.empty) return; if (! updateDouble(&mapping,Temperature)) return;
LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str()); LOG_DEBUG(GwLog::DEBUG+1,"found temperature mapping %s",mapping.definition->toString().c_str());
addToXdr(mapping.buildXdrEntry(Temperature)); addToXdr(mapping.buildXdrEntry(Temperature));
finalizeXdr(); finalizeXdr();
@ -1251,7 +1255,7 @@ private:
return; return;
} }
GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRHUMIDITY,(int)HumiditySource,0,HumidityInstance); GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRHUMIDITY,(int)HumiditySource,0,HumidityInstance);
if (mapping.empty) return; if (! updateDouble(&mapping,ActualHumidity)) return;
LOG_DEBUG(GwLog::DEBUG+1,"found humidity mapping %s",mapping.definition->toString().c_str()); LOG_DEBUG(GwLog::DEBUG+1,"found humidity mapping %s",mapping.definition->toString().c_str());
addToXdr(mapping.buildXdrEntry(ActualHumidity)); addToXdr(mapping.buildXdrEntry(ActualHumidity));
finalizeXdr(); finalizeXdr();
@ -1268,7 +1272,7 @@ private:
return; return;
} }
GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRPRESSURE,(int)PressureSource,0,PressureInstance); GwXDRFoundMapping mapping=xdrMappings->getMapping(XDRPRESSURE,(int)PressureSource,0,PressureInstance);
if (mapping.empty) return; if (! updateDouble(&mapping,ActualPressure)) return;
LOG_DEBUG(GwLog::DEBUG+1,"found pressure mapping %s",mapping.definition->toString().c_str()); LOG_DEBUG(GwLog::DEBUG+1,"found pressure mapping %s",mapping.definition->toString().c_str());
addToXdr(mapping.buildXdrEntry(ActualPressure)); addToXdr(mapping.buildXdrEntry(ActualPressure));
finalizeXdr(); finalizeXdr();

View File

@ -188,6 +188,10 @@ GwXDRMappingDef *GwXDRMappingDef::fromString(String s)
return NULL; return NULL;
} }
} }
if (rt->direction == GwXDRMappingDef::M_DISABLED || rt->xdrName == ""){
delete rt;
return NULL;
}
return rt; return rt;
} }
String GwXDRMappingDef::getTransducerName(int instance) String GwXDRMappingDef::getTransducerName(int instance)

View File

@ -166,7 +166,7 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{
String buildXdrEntry(double value); String buildXdrEntry(double value);
//boat Data info //boat Data info
virtual String getBoatItemName(){ virtual String getBoatItemName(){
return getTransducerName(); return String("xdr")+getTransducerName();
}; };
virtual String getBoatItemFormat(){ virtual String getBoatItemFormat(){
return "formatXdr"+type->xdrunit; //TODO: use the type def for the correct format return "formatXdr"+type->xdrunit; //TODO: use the type def for the correct format

View File

@ -242,7 +242,7 @@ body{
} }
.dash { .dash {
width: 6em; width: 6.5em;
height: 4em; height: 4em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1025,6 +1025,15 @@ let valueFormatters = {
u:'m' u:'m'
} }
} }
function resizeFont(el,reset,maxIt){
if (maxIt === undefined) maxIt=10;
if (! el) return;
if (reset) el.style.fontSize='';
while (el.scrollWidth > el.clientWidth && maxIt){
let next=parseFloat(window.getComputedStyle(el).fontSize)*0.9;
el.style.fontSize=next+"px";
}
}
function createDashboardItem(name, def, parent) { function createDashboardItem(name, def, parent) {
let frame = addEl('div', 'dash', parent); let frame = addEl('div', 'dash', parent);
let title = addEl('span', 'dashTitle', frame, name); let title = addEl('span', 'dashTitle', frame, name);
@ -1035,7 +1044,11 @@ function createDashboardItem(name, def, parent) {
let footer = addEl('div','footer',frame); let footer = addEl('div','footer',frame);
let src= addEl('span','source',footer); let src= addEl('span','source',footer);
src.setAttribute('id','source_'+name); src.setAttribute('id','source_'+name);
addEl('span','unit',footer,fmt?fmt.u:''); let u=fmt?fmt.u:'';
if (! fmt && def.format.match(/formatXdr/)){
u=def.format.replace(/formatXdr/,'');
}
addEl('span','unit',footer,u);
return value; return value;
} }
function createDashboard() { function createDashboard() {
@ -1057,9 +1070,14 @@ function sourceName(v){
return "---"; return "---";
} }
function updateDashboard(data) { function updateDashboard(data) {
let frame = document.getElementById('dashboardPage');
for (let n in data) { for (let n in data) {
let de = document.getElementById('data_' + n); let de = document.getElementById('data_' + n);
if (! de && frame){
de=createDashboardItem(n,data[n],frame);
}
if (de) { if (de) {
let newContent='----';
if (data[n].valid) { if (data[n].valid) {
let formatter; let formatter;
if (data[n].format && data[n].format != "NULL") { if (data[n].format && data[n].format != "NULL") {
@ -1067,26 +1085,38 @@ function updateDashboard(data) {
formatter = valueFormatters[key]; formatter = valueFormatters[key];
} }
if (formatter) { if (formatter) {
de.textContent = formatter.f(data[n].value); newContent = formatter.f(data[n].value);
} }
else { else {
let v = parseFloat(data[n].value); let v = parseFloat(data[n].value);
if (!isNaN(v)) { if (!isNaN(v)) {
v = v.toFixed(3) v = v.toFixed(3)
de.textContent = v; newContent = v;
} }
else { else {
de.textContent = data[n].value; newContent = data[n].value;
} }
} }
} }
else de.textContent = "---"; else newContent = "---";
if (newContent !== de.textContent){
de.textContent=newContent;
resizeFont(de,true);
}
} }
let src=document.getElementById('source_'+n); let src=document.getElementById('source_'+n);
if (src){ if (src){
src.textContent=sourceName(data[n].source); src.textContent=sourceName(data[n].source);
} }
} }
forEl('.dashValue',function(el){
let id=el.getAttribute('id');
if (id){
if (! data[id.replace(/^data_/,'')]){
el.parentElement.remove();
}
}
});
} }
window.setInterval(update, 1000); window.setInterval(update, 1000);