use new api boatDataString for the UI

This commit is contained in:
wellenvogel 2021-11-25 16:43:13 +01:00
parent 3944735190
commit aae16531ff
4 changed files with 183 additions and 24 deletions

View File

@ -1,4 +1,5 @@
#include "GwBoatData.h" #include "GwBoatData.h"
#include <ArduinoJson/Json/TextFormatter.hpp>
#define GWTYPE_DOUBLE 1 #define GWTYPE_DOUBLE 1
#define GWTYPE_UINT32 2 #define GWTYPE_UINT32 2
#define GWTYPE_UINT16 3 #define GWTYPE_UINT16 3
@ -30,8 +31,55 @@ GwBoatItemBase::GwBoatItemBase(String name, String format, unsigned long invalid
this->name = name; this->name = name;
this->format = format; this->format = format;
this->type = 0; this->type = 0;
this->lastUpdateSource=-1;
}
#define STRING_SIZE 40
GwBoatItemBase::StringWriter::StringWriter(){
buffer=new uint8_t[STRING_SIZE];
wp=buffer;
bufferSize=STRING_SIZE;
buffer [0]=0;
};
const char *GwBoatItemBase::StringWriter::c_str() const{
return (const char *)buffer;
}
int GwBoatItemBase::StringWriter::getSize() const{
return wp-buffer;
}
void GwBoatItemBase::StringWriter::reset(){
wp=buffer;
*wp=0;
}
void GwBoatItemBase::StringWriter::ensure(size_t size){
size_t fill=wp-buffer;
size_t newSize=bufferSize;
while ((fill+size) >= (newSize-1) ){
newSize+=STRING_SIZE;
}
if (newSize != bufferSize){
uint8_t *newBuffer=new uint8_t[newSize];
memcpy(newBuffer,buffer,fill);
newBuffer[fill]=0;
delete buffer;
buffer=newBuffer;
wp=newBuffer+fill;
bufferSize=newSize;
}
}
size_t GwBoatItemBase::StringWriter::write(uint8_t c){
ensure(1);
*wp=c;
wp++;
*wp=0;
return 1;
}
size_t GwBoatItemBase::StringWriter::write(const uint8_t* s, size_t n){
ensure(n);
memcpy(wp,s,n);
wp+=n;
*wp=0;
return n;
} }
template<class T> GwBoatItem<T>::GwBoatItem(String name,String formatInfo,unsigned long invalidTime,GwBoatItemMap *map): template<class T> GwBoatItem<T>::GwBoatItem(String name,String formatInfo,unsigned long invalidTime,GwBoatItemMap *map):
GwBoatItemBase(name,formatInfo,invalidTime){ GwBoatItemBase(name,formatInfo,invalidTime){
T dummy; T dummy;
@ -39,7 +87,6 @@ template<class T> GwBoatItem<T>::GwBoatItem(String name,String formatInfo,unsign
if (map){ if (map){
(*map)[name]=this; (*map)[name]=this;
} }
lastUpdateSource=-1;
} }
template <class T> template <class T>
@ -88,6 +135,59 @@ void GwBoatItem<T>::toJsonDoc(JsonDocument *doc, unsigned long minTime)
o[F("valid")] = isValid(minTime); o[F("valid")] = isValid(minTime);
o[F("format")] = format; o[F("format")] = format;
} }
class WriterWrapper{
GwBoatItemBase::StringWriter *writer=NULL;
public:
WriterWrapper(GwBoatItemBase::StringWriter *w){
writer=w;
}
size_t write(uint8_t c){
if (! writer) return 0;
return writer->write(c);
}
size_t write(const uint8_t* s, size_t n){
if (! writer) return 0;
return writer->write(s,n);
}
};
typedef ARDUINOJSON_NAMESPACE::TextFormatter<WriterWrapper> GwTextWriter;
static void writeToString(GwTextWriter *writer,const double &value){
writer->writeFloat(value);
}
static void writeToString(GwTextWriter *writer,const uint16_t &value){
writer->writeInteger(value);
}
static void writeToString(GwTextWriter *writer,const uint32_t &value){
writer->writeInteger(value);
}
static void writeToString(GwTextWriter *writer,const int16_t &value){
writer->writeInteger(value);
}
static void writeToString(GwTextWriter *writer,GwSatInfoList &value){
writer->writeInteger(value.getNumSats());
}
template <class T>
void GwBoatItem<T>::fillString(){
writer.reset();
WriterWrapper wrapper(&writer);
GwTextWriter stringWriter(wrapper);
stringWriter.writeRaw(name.c_str());
stringWriter.writeChar(',');
stringWriter.writeInteger(isValid()?1:0);
stringWriter.writeChar(',');
stringWriter.writeInteger(lastSet);
stringWriter.writeChar(',');
stringWriter.writeInteger(lastUpdateSource);
stringWriter.writeChar(',');
stringWriter.writeRaw(format.c_str());
stringWriter.writeChar(',');
writeToString(&stringWriter,data);
}
template class GwBoatItem<double>; template class GwBoatItem<double>;
template class GwBoatItem<uint32_t>; template class GwBoatItem<uint32_t>;
template class GwBoatItem<uint16_t>; template class GwBoatItem<uint16_t>;
@ -214,7 +314,14 @@ String GwBoatData::toJson() const {
serializeJson(json,buf); serializeJson(json,buf);
return buf; return buf;
} }
String GwBoatData::toString(){
String rt;
for (auto it=values.begin() ; it != values.end();it++){
rt+=it->second->getDataString();
rt+="\n";
}
return rt;
}
double formatCourse(double cv) double formatCourse(double cv)
{ {
double rt = cv * 180.0 / M_PI; double rt = cv * 180.0 / M_PI;

View File

@ -10,6 +10,19 @@
class GwBoatItemBase{ class GwBoatItemBase{
public: public:
class StringWriter{
uint8_t *buffer =NULL;
uint8_t *wp=NULL;
size_t bufferSize=0;
void ensure(size_t size);
public:
StringWriter();
size_t write(uint8_t c);
size_t write(const uint8_t* s, size_t n);
const char * c_str() const;
int getSize() const;
void reset();
};
static const unsigned long INVALID_TIME=60000; static const unsigned long INVALID_TIME=60000;
//the formatter names that must be known in js //the formatter names that must be known in js
GWSC(formatCourse); GWSC(formatCourse);
@ -31,10 +44,12 @@ class GwBoatItemBase{
unsigned long invalidTime=INVALID_TIME; unsigned long invalidTime=INVALID_TIME;
String name; String name;
String format; String format;
StringWriter writer;
void uls(unsigned long ts=0){ void uls(unsigned long ts=0){
if (ts) lastSet=ts; if (ts) lastSet=ts;
else lastSet=millis(); else lastSet=millis();
} }
int lastUpdateSource;
public: public:
int getCurrentType(){return type;} int getCurrentType(){return type;}
unsigned long getLastSet() const {return lastSet;} unsigned long getLastSet() const {return lastSet;}
@ -44,9 +59,14 @@ class GwBoatItemBase{
void invalidate(){ void invalidate(){
lastSet=0; lastSet=0;
} }
const char *getDataString(){
fillString();
return writer.c_str();
}
virtual void fillString()=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(10);} virtual size_t getJsonSize(){return JSON_OBJECT_SIZE(10);}
virtual int getLastSource()=0; virtual int getLastSource(){return lastUpdateSource;}
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;}
}; };
@ -54,7 +74,6 @@ class GwBoatData;
template<class T> class GwBoatItem : public GwBoatItemBase{ template<class T> class GwBoatItem : public GwBoatItemBase{
protected: protected:
T data; T data;
int lastUpdateSource;
public: public:
GwBoatItem(String name,String formatInfo,unsigned long invalidTime=INVALID_TIME,GwBoatItemMap *map=NULL); GwBoatItem(String name,String formatInfo,unsigned long invalidTime=INVALID_TIME,GwBoatItemMap *map=NULL);
virtual ~GwBoatItem(){} virtual ~GwBoatItem(){}
@ -67,6 +86,7 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
if (! isValid(millis())) return defaultv; if (! isValid(millis())) return defaultv;
return data; return data;
} }
virtual void fillString();
virtual void toJsonDoc(JsonDocument *doc, unsigned long minTime); virtual void toJsonDoc(JsonDocument *doc, unsigned long minTime);
virtual int getLastSource(){return lastUpdateSource;} virtual int getLastSource(){return lastUpdateSource;}
}; };
@ -178,6 +198,7 @@ class GwBoatData{
template<class T> bool update(T value,int source,GwBoatItemNameProvider *provider); template<class T> bool update(T value,int source,GwBoatItemNameProvider *provider);
template<class T> T getDataWithDefault(T defaultv, GwBoatItemNameProvider *provider); template<class T> T getDataWithDefault(T defaultv, GwBoatItemNameProvider *provider);
String toJson() const; String toJson() const;
String toString();
}; };

View File

@ -420,6 +420,17 @@ protected:
result = boatData.toJson(); result = boatData.toJson();
} }
}; };
class BoatDataStringRequest : public GwRequestMessage
{
public:
BoatDataStringRequest() : GwRequestMessage(F("text/plain"),F("boatDataString")){};
protected:
virtual void processRequest()
{
result = boatData.toString();
}
};
class XdrExampleRequest : public GwRequestMessage class XdrExampleRequest : public GwRequestMessage
{ {
@ -562,6 +573,8 @@ void setup() {
{ return new ResetConfigRequest(); }); { return new ResetConfigRequest(); });
webserver.registerMainHandler("/api/boatData", [](AsyncWebServerRequest *request)->GwRequestMessage * webserver.registerMainHandler("/api/boatData", [](AsyncWebServerRequest *request)->GwRequestMessage *
{ return new BoatDataRequest(); }); { return new BoatDataRequest(); });
webserver.registerMainHandler("/api/boatDataString", [](AsyncWebServerRequest *request)->GwRequestMessage *
{ return new BoatDataStringRequest(); });
webserver.registerMainHandler("/api/xdrExample", [](AsyncWebServerRequest *request)->GwRequestMessage * webserver.registerMainHandler("/api/xdrExample", [](AsyncWebServerRequest *request)->GwRequestMessage *
{ {
String mapping=request->arg("mapping"); String mapping=request->arg("mapping");

View File

@ -1035,6 +1035,7 @@ function resizeFont(el,reset,maxIt){
} }
} }
function createDashboardItem(name, def, parent) { function createDashboardItem(name, def, parent) {
if (! def.name) return;
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);
let value = addEl('span', 'dashValue', frame); let value = addEl('span', 'dashValue', frame);
@ -1044,22 +1045,35 @@ 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);
let u=fmt?fmt.u:''; let u=fmt?fmt.u:' ';
if (! fmt && def.format.match(/formatXdr/)){ if (! fmt && def.format && def.format.match(/formatXdr/)){
u=def.format.replace(/formatXdr/,''); u=def.format.replace(/formatXdr/,'');
} }
addEl('span','unit',footer,u); addEl('span','unit',footer,u);
return value; return value;
} }
function parseBoatDataLine(line){
let rt={};
let parts=line.split(',');
rt.name=parts[0];
rt.valid=parts[1] === '1';
rt.update=parseInt(parts[2]);
rt.source=parseInt(parts[3]);
rt.format=parts[4];
rt.value=parts[5];
return rt;
}
function createDashboard() { function createDashboard() {
let frame = document.getElementById('dashboardPage'); let frame = document.getElementById('dashboardPage');
if (!frame) return; if (!frame) return;
getJson("api/boatData").then(function (json) { getText("api/boatDataString").then(function (txt) {
frame.innerHTML = ''; frame.innerHTML = '';
for (let n in json) { let values=txt.split('\n');
createDashboardItem(n, json[n], frame); for (let n in values) {
let def=parseBoatDataLine(values[n]);
createDashboardItem(def.name, def, frame);
} }
updateDashboard(json); updateDashboard(values);
}); });
} }
function sourceName(v){ function sourceName(v){
@ -1071,30 +1085,34 @@ function sourceName(v){
} }
function updateDashboard(data) { function updateDashboard(data) {
let frame = document.getElementById('dashboardPage'); let frame = document.getElementById('dashboardPage');
let names={};
for (let n in data) { for (let n in data) {
let de = document.getElementById('data_' + n); let current=parseBoatDataLine(data[n]);
if (! current.name) return;
names[current.name]=true;
let de = document.getElementById('data_' + current.name);
if (! de && frame){ if (! de && frame){
de=createDashboardItem(n,data[n],frame); de=createDashboardItem(current.name,current,frame);
} }
if (de) { if (de) {
let newContent='----'; let newContent='----';
if (data[n].valid) { if (current.valid) {
let formatter; let formatter;
if (data[n].format && data[n].format != "NULL") { if (current.format && current.format != "NULL") {
let key = data[n].format.replace(/^\&/, ''); let key = current.format.replace(/^\&/, '');
formatter = valueFormatters[key]; formatter = valueFormatters[key];
} }
if (formatter) { if (formatter) {
newContent = formatter.f(data[n].value); newContent = formatter.f(current.value);
} }
else { else {
let v = parseFloat(data[n].value); let v = parseFloat(current.value);
if (!isNaN(v)) { if (!isNaN(v)) {
v = v.toFixed(3) v = v.toFixed(3)
newContent = v; newContent = v;
} }
else { else {
newContent = data[n].value; newContent = current.value;
} }
} }
} }
@ -1104,15 +1122,15 @@ function updateDashboard(data) {
resizeFont(de,true); resizeFont(de,true);
} }
} }
let src=document.getElementById('source_'+n); let src=document.getElementById('source_'+current.name);
if (src){ if (src){
src.textContent=sourceName(data[n].source); src.textContent=sourceName(current.source);
} }
} }
forEl('.dashValue',function(el){ forEl('.dashValue',function(el){
let id=el.getAttribute('id'); let id=el.getAttribute('id');
if (id){ if (id){
if (! data[id.replace(/^data_/,'')]){ if (! names[id.replace(/^data_/,'')]){
el.parentElement.remove(); el.parentElement.remove();
} }
} }
@ -1123,8 +1141,8 @@ window.setInterval(update, 1000);
window.setInterval(function () { window.setInterval(function () {
let dp = document.getElementById('dashboardPage'); let dp = document.getElementById('dashboardPage');
if (dp.classList.contains('hidden')) return; if (dp.classList.contains('hidden')) return;
getJson('api/boatData').then(function (data) { getText('api/boatDataString').then(function (data) {
updateDashboard(data); updateDashboard(data.split('\n'));
}); });
}, 1000); }, 1000);
window.addEventListener('load', function () { window.addEventListener('load', function () {