use new api boatDataString for the UI
This commit is contained in:
parent
3944735190
commit
aae16531ff
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
13
src/main.cpp
13
src/main.cpp
|
@ -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");
|
||||||
|
|
56
web/index.js
56
web/index.js
|
@ -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 () {
|
||||||
|
|
Loading…
Reference in New Issue