OVMS3/OVMS.V3/components/retools/src/retools.cpp

853 lines
25 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 14th March 2017
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2017 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "ovms_log.h"
static const char *TAG = "re";
#include <string.h>
#include "retools.h"
#include "dbc_app.h"
#include "ovms.h"
#include "ovms_peripherals.h"
#include "ovms_events.h"
#include "ovms_utils.h"
#include "ovms_notify.h"
void re_stream_list(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
re *MyRE = NULL;
const char* re_green[2][2] = { { "\x1b" "[32m", "\x1b" "[39m" }, { "<b>", "</b>" } };
const char* re_red[2][2] = { { "\x1b" "[31m", "\x1b" "[39m" }, { "<u>", "</u>" } };
void HighlightDump(char* bufferp, const char* data, size_t rlength, uint8_t red, uint8_t green, int mode = 0, char** ascii = NULL)
{
const char *s = data;
const char *os = data;
size_t colsize = 8;
for (int k=0;k<colsize;k++)
{
uint8_t mask = (k==0)?1:(1<<k);
if (k<rlength)
{
if (green & mask)
bufferp += sprintf(bufferp, "%s%2.2x%s ", re_green[mode][0], *s, re_green[mode][1]);
else if (red & mask)
bufferp += sprintf(bufferp, "%s%2.2x%s ", re_red[mode][0], *s, re_red[mode][1]);
else
bufferp += sprintf(bufferp, "%2.2x ", *s);
s++;
}
else
{
bufferp += sprintf(bufferp," ");
}
}
if (ascii)
{
*bufferp++ = 0;
*ascii = bufferp;
}
else
{
bufferp += sprintf(bufferp,"| ");
}
s = os;
for (int k=0;k<colsize;k++)
{
uint8_t mask = (k==0)?1:(1<<k);
if (k<rlength)
{
if (green & mask)
bufferp += sprintf(bufferp, "%s%c%s", re_green[mode][0], isprint((int)*s) ? *s : '.', re_green[mode][1]);
else if (red & mask)
bufferp += sprintf(bufferp, "%s%c%s", re_red[mode][0], isprint((int)*s) ? *s : '.', re_red[mode][1]);
else
*bufferp++ = isprint((int)*s) ? *s : '.';
s++;
}
else
{
*bufferp++ = ' ';
}
}
*bufferp = 0;
}
static void RE_task(void *pvParameters)
{
re *me = (re*)pvParameters;
me->Task();
}
void re::Task()
{
CAN_log_message_t message;
while(1)
{
if (xQueueReceive(m_rxqueue, &message.frame, (portTickType)portMAX_DELAY)==pdTRUE)
{
if (MyRE != NULL) // Protect against MyRE not set (during init)
{
switch (m_mode)
{
case Analyse:
case Discover:
if ((m_filter)&&(!m_filter->IsFiltered(&message.frame)))
{
// Frame is filtered, just drop it...
}
else
{
DoAnalyse(&message.frame);
}
break;
}
m_finished = monotonictime;
}
}
}
}
void re::DoAnalyse(CAN_frame_t* frame)
{
char vbuf[256];
OvmsRecMutexLock lock(&m_mutex);
std::string key = GetKey(frame);
auto k = m_rmap.find(key);
re_record_t* r;
if (m_rmap.size() == 0) m_started = monotonictime;
if (k == m_rmap.end())
{
r = k->second;
r = new re_record_t;
memset(r,0,sizeof(re_record_t));
r->attr.b.Changed = 1; // Mark the whole ID as changed
r->attr.dc = 0xff;
switch (MyRE->m_mode)
{
case Analyse:
break;
case Discover:
r->attr.b.Discovered = 1;
r->attr.dd = 0xff;
HighlightDump(vbuf, (const char*)frame->data.u8, frame->FIR.B.DLC, r->attr.dc, r->attr.dd);
ESP_LOGV(TAG, "Discovered new %s%s%s %s",
re_green[0][0], key.c_str(), re_green[0][1], vbuf);
break;
}
m_rmap[key] = r;
}
else
{
r = k->second;
switch (MyRE->m_mode)
{
case Analyse:
for (int k=0;k<r->last.FIR.B.DLC;k++)
{
if (r->last.data.u8[k] != frame->data.u8[k])
r->attr.dc |= (1<<k); // Mark the byte as changed
}
break;
case Discover:
{
bool found = false;
for (int j=0;j<r->last.FIR.B.DLC;j++)
{
uint8_t mask = (j==0)?1:(1<<j);
if (((r->attr.dc & mask)==0) &&
(r->last.data.u8[j] != frame->data.u8[j]))
{
r->attr.dc |= mask; // Mark the byte as changed
r->attr.dd |= mask; // Mark the byte as discovered
found = true;
}
}
if (found)
{
HighlightDump(vbuf, (const char*)frame->data.u8, frame->FIR.B.DLC, r->attr.dc, r->attr.dd);
ESP_LOGV(TAG, "Discovered change %s %s", k->first.c_str(), vbuf);
}
break;
}
}
}
memcpy(&r->last,frame,sizeof(CAN_frame_t));
r->rxcount++;
}
std::string re::GetKey(CAN_frame_t* frame)
{
std::string key;
if (frame->origin != NULL)
key = std::string(frame->origin->GetName());
else
key = std::string("can?");
key.append("/");
char id[9];
if (frame->FIR.B.FF == CAN_frame_std)
sprintf(id,"%03x",frame->MsgID);
else
sprintf(id,"%08x",frame->MsgID);
key.append(id);
if (((m_obdii_std_min>0) &&
(frame->FIR.B.FF == CAN_frame_std) &&
(frame->MsgID >= m_obdii_std_min) &&
(frame->MsgID <= m_obdii_std_max))
||
((m_obdii_ext_min>0) &&
(frame->FIR.B.FF == CAN_frame_ext) &&
(frame->MsgID >= m_obdii_ext_min) &&
(frame->MsgID <= m_obdii_ext_max)))
{
// It is an OBDII request
if (frame->data.u8[0] > 8)
{
// Probably just a continuation frame. Ignore it.
return key;
}
uint8_t mode = frame->data.u8[1];
char req[16];
if (mode > 0x4a)
{
sprintf(req,":O2Pm%d:%d",mode-0x40,((int)frame->data.u8[2]<<8)+frame->data.u8[3]);
}
else if (mode > 0x40)
{
sprintf(req,":O2Pm%d:%d",mode-0x40,(int)frame->data.u8[2]);
}
else if (mode > 0x0a)
{
sprintf(req,":O2Qm%d:%d",mode,((int)frame->data.u8[2]<<8)+frame->data.u8[3]);
}
else
{
sprintf(req,":O2Qm%d:%d",mode,(int)frame->data.u8[2]);
}
key.append(req);
return key;
}
// Check for, and process, multiplexed signal
if (frame->origin != NULL)
{
dbcfile* dbc = frame->origin->GetDBC();
if (dbc != NULL)
{
dbcMessage* m = dbc->m_messages.FindMessage(frame->FIR.B.FF, frame->MsgID);
if ((m != NULL)&&(m->IsMultiplexor()))
{
// We have a multiplexed signal
dbcSignal* s = m->GetMultiplexorSignal();
dbcNumber muxn = s->Decode(frame);
uint32_t mux = muxn.GetUnsignedInteger();
char b[8];
sprintf(b,":%04x",mux);
key.append(b);
}
}
}
return key;
}
re::re(const char* name, canfilter* filter)
: pcp(name)
{
m_filter = filter;
m_obdii_std_min = 0;
m_obdii_std_max = 0;
m_obdii_ext_min = 0;
m_obdii_ext_max = 0;
m_started = monotonictime;
m_finished = monotonictime;
m_mode = Analyse;
m_rxqueue = xQueueCreate(20,sizeof(CAN_frame_t));
xTaskCreatePinnedToCore(RE_task, "OVMS RE", 4096, (void*)this, 5, &m_task, CORE(1));
MyCan.RegisterListener(m_rxqueue, true);
}
re::~re()
{
OvmsRecMutexLock lock(&m_mutex);
MyCan.DeregisterListener(m_rxqueue);
Clear();
vQueueDelete(m_rxqueue);
vTaskDelete(m_task);
if (m_filter)
{
delete m_filter;
m_filter = NULL;
}
}
void re::SetPowerMode(PowerMode powermode)
{
m_powermode = powermode;
switch (powermode)
{
case On:
break;
case Sleep:
break;
case DeepSleep:
break;
case Off:
break;
default:
break;
}
}
void re::Clear()
{
for (re_record_map_t::iterator it=m_rmap.begin(); it!=m_rmap.end(); ++it)
{
delete it->second;
}
m_rmap.clear();
m_started = monotonictime;
m_finished = monotonictime;
}
void re_start(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (MyRE)
writer->puts("Error: RE tools already running");
else
{
canfilter* filter = NULL;
if (argc>0)
{
filter = new canfilter();
for (int k=0;k<argc;k++)
{
filter->AddFilter(argv[k]);
}
}
MyRE = new re("re", filter);
MyEvents.SignalEvent("retools.started", NULL);
}
}
void re_stop(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
writer->puts("Error: RE tools not running");
else
{
delete MyRE;
MyRE = NULL;
MyEvents.SignalEvent("retools.stopped", NULL);
}
}
void re_clear(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
OvmsRecMutexLock lock(&MyRE->m_mutex);
MyRE->Clear();
if (MyNotify.HasReader("stream", "retools.list"))
{
StringWriter buf;
re_stream_list(COMMAND_RESULT_VERBOSE, &buf, NULL, 0, NULL);
MyNotify.NotifyString("stream", "retools.list.set", buf.c_str());
}
writer->puts("Cleared all records");
MyEvents.SignalEvent("retools.cleared.all", NULL);
}
void re_list(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
uint32_t tdiff = (MyRE->m_finished - MyRE->m_started)*1000;
if (tdiff == 0) tdiff = 1000;
OvmsRecMutexLock lock(&MyRE->m_mutex);
writer->printf("%-20.20s %10s %6s %s\n","key","records","ms","last");
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
if ((argc==0)||(strstr(it->first.c_str(),argv[0])))
{
char vbuf[48];
char *s = vbuf;
FormatHexDump(&s, (const char*)it->second->last.data.u8, it->second->last.FIR.B.DLC, 8);
writer->printf("%-20s %10d %6d %s\n",
it->first.c_str(),it->second->rxcount,(tdiff/it->second->rxcount),vbuf);
}
}
}
void re_stream_list(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
char vbuf[256];
if (!MyRE)
{
writer->puts("[]");
return;
}
uint32_t tdiff = (MyRE->m_finished - MyRE->m_started)*1000;
if (tdiff == 0) tdiff = 1000;
OvmsRecMutexLock lock(&MyRE->m_mutex);
writer->printf("[");
int cnt = 0;
char *ascii = NULL;
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
if (argc == 0 || strstr(it->first.c_str(),argv[0]) != NULL)
{
HighlightDump(vbuf, (const char*)it->second->last.data.u8,
it->second->last.FIR.B.DLC, it->second->attr.dc, it->second->attr.dd, 1, &ascii);
writer->printf("%s[\"%s\",%d,%d,\"%s\",\"%s\"]\n",
cnt ? "," : "",
json_encode(it->first).c_str(), it->second->rxcount, (tdiff/it->second->rxcount),
json_encode(std::string(vbuf)).c_str(),
json_encode(std::string(ascii)).c_str());
cnt++;
}
}
writer->puts("]");
}
void re_dbc_list(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
uint32_t tdiff = (MyRE->m_finished - MyRE->m_started)*1000;
if (tdiff == 0) tdiff = 1000;
OvmsRecMutexLock lock(&MyRE->m_mutex);
writer->printf("%-20.20s %10s %6s %s\n","key","records","ms","last");
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
if ((argc==0)||(strstr(it->first.c_str(),argv[0])))
{
char vbuf[48];
char *s = vbuf;
FormatHexDump(&s, (const char*)it->second->last.data.u8, it->second->last.FIR.B.DLC, 8);
writer->printf("%-20s %10d %6d %s\n",
it->first.c_str(),it->second->rxcount,(tdiff/it->second->rxcount),vbuf);
if (it->second->last.origin)
{
dbcfile* dbc = it->second->last.origin->GetDBC();
if (dbc)
{
// We have a DBC attached.
dbcMessage* msg = dbc->m_messages.FindMessage(it->second->last.FIR.B.FF, it->second->last.MsgID);
if (msg)
{
// Let's look for signals...
dbcSignal* mux = msg->GetMultiplexorSignal();
uint32_t muxval;
if (mux)
{
dbcNumber r = mux->Decode(&it->second->last);
muxval = r.GetSignedInteger();
std::ostringstream ss;
ss << " dbc/mux/";
ss << mux->GetName();
ss << ": ";
ss << r;
ss << " ";
ss << mux->GetUnit();
writer->puts(ss.str().c_str());
}
for (dbcSignal* sig : msg->m_signals)
{
if ((mux==NULL)||(sig->GetMultiplexSwitchvalue() == muxval))
{
dbcNumber r = sig->Decode(&it->second->last);
std::ostringstream ss;
ss << " dbc/";
ss << sig->GetName();
ss << ": ";
ss << r;
ss << " ";
ss << sig->GetUnit();
writer->puts(ss.str().c_str());
}
}
}
}
}
}
}
}
void re_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("RE: tools not running");
return;
}
switch (MyRE->m_mode)
{
case Analyse:
writer->puts("Mode: Analysing");
break;
case Discover:
writer->puts("Mode: Discovering");
break;
}
if (MyRE->m_filter)
{
writer->printf("Filter: %s\n", MyRE->m_filter->Info().c_str());
}
OvmsRecMutexLock lock(&MyRE->m_mutex);
writer->printf("Key Map: %d entries\n",MyRE->m_rmap.size());
if (MyRE->m_rmap.size() > 0)
{
int nignored = 0;
int nchanged = 0;
int bchanged = 0;
int ndiscovered = 0;
int bdiscovered = 0;
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
re_record_t *r = it->second;
if (r->attr.b.Ignore) nignored++;
if (r->attr.b.Changed) nchanged++;
if (r->attr.b.Discovered) ndiscovered++;
for (int j=0;j<8;j++)
{
uint8_t mask = (1<<j);
if (r->attr.dc & mask) bchanged++;
if (r->attr.dd & mask) bdiscovered++;
}
}
writer->printf(" %d keys are ignored\n",nignored);
writer->printf(" %d keys are new and changed\n",nchanged);
writer->printf(" %d bytes are changed\n",bchanged);
writer->printf(" %d keys are new and discovered\n",ndiscovered);
writer->printf(" %d bytes are discovered\n",bdiscovered);
}
}
void re_obdii_std(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
MyRE->m_obdii_std_min = (uint32_t)strtol(argv[0],NULL,16);
MyRE->m_obdii_std_max = (uint32_t)strtol(argv[1],NULL,16);
writer->printf("Set OBDII standard ID range %03x-%03x\n",MyRE->m_obdii_std_min,MyRE->m_obdii_std_max);
}
void re_obdii_ext(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
MyRE->m_obdii_ext_min = (uint32_t)strtol(argv[0],NULL,16);
MyRE->m_obdii_ext_max = (uint32_t)strtol(argv[1],NULL,16);
writer->printf("Set OBDII extended ID range %08x-%08x\n",MyRE->m_obdii_ext_min,MyRE->m_obdii_ext_max);
}
void re_mode_analyse(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
MyRE->m_mode = Analyse;
writer->puts("Now running in analyse mode");
MyEvents.SignalEvent("retools.mode.analyse", NULL);
}
void re_mode_discover(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
OvmsRecMutexLock lock(&MyRE->m_mutex);
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
it->second->attr.b.Discovered = 0;
it->second->attr.dd = 0;
}
MyRE->m_mode = Discover;
writer->puts("Now running in discover mode");
MyEvents.SignalEvent("retools.mode.discover", NULL);
}
void re_clear_changed(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
OvmsRecMutexLock lock(&MyRE->m_mutex);
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
it->second->attr.b.Changed = 0;
it->second->attr.dc = 0;
}
if (MyNotify.HasReader("stream", "retools.list"))
{
StringWriter buf;
re_stream_list(COMMAND_RESULT_VERBOSE, &buf, NULL, 0, NULL);
MyNotify.NotifyString("stream", "retools.list.set", buf.c_str());
}
writer->puts("Cleared all change flags");
MyEvents.SignalEvent("retools.cleared.changed", NULL);
}
void re_clear_discovered(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
OvmsRecMutexLock lock(&MyRE->m_mutex);
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
it->second->attr.b.Discovered = 0;
it->second->attr.dd = 0;
}
if (MyNotify.HasReader("stream", "retools.list"))
{
StringWriter buf;
re_stream_list(COMMAND_RESULT_VERBOSE, &buf, NULL, 0, NULL);
MyNotify.NotifyString("stream", "retools.list.set", buf.c_str());
}
writer->puts("Cleared all discover flags");
MyEvents.SignalEvent("retools.cleared.discovered", NULL);
}
void re_list_changed(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
char vbuf[256];
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
uint32_t tdiff = (MyRE->m_finished - MyRE->m_started)*1000;
if (tdiff == 0) tdiff = 1000;
OvmsRecMutexLock lock(&MyRE->m_mutex);
writer->printf("%-20.20s %10s %6s %s\n","key","records","ms","last");
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
if ((it->second->attr.b.Changed)||(it->second->attr.dc))
{
HighlightDump(vbuf, (const char*)it->second->last.data.u8,
it->second->last.FIR.B.DLC, it->second->attr.dc, it->second->attr.dd);
if ((argc==0)||(strstr(it->first.c_str(),argv[0])))
{
writer->printf("%-20s %10d %6d %s\n",
it->first.c_str(),it->second->rxcount,(tdiff/it->second->rxcount),vbuf);
}
}
}
}
void re_stream_changed(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
char vbuf[256];
if (!MyRE)
{
writer->puts("[]");
return;
}
uint32_t tdiff = (MyRE->m_finished - MyRE->m_started)*1000;
if (tdiff == 0) tdiff = 1000;
OvmsRecMutexLock lock(&MyRE->m_mutex);
writer->printf("[");
int cnt = 0;
char *ascii = NULL;
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
if ((it->second->attr.b.Changed || it->second->attr.dc) &&
(argc == 0 || strstr(it->first.c_str(),argv[0]) != NULL))
{
HighlightDump(vbuf, (const char*)it->second->last.data.u8,
it->second->last.FIR.B.DLC, it->second->attr.dc, it->second->attr.dd, 1, &ascii);
writer->printf("%s[\"%s\",%d,%d,\"%s\",\"%s\"]\n",
cnt ? "," : "",
json_encode(it->first).c_str(), it->second->rxcount, (tdiff/it->second->rxcount),
json_encode(std::string(vbuf)).c_str(),
json_encode(std::string(ascii)).c_str());
cnt++;
}
}
writer->puts("]");
}
void re_list_discovered(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
char vbuf[256];
if (!MyRE)
{
writer->puts("Error: RE tools not running");
return;
}
uint32_t tdiff = (MyRE->m_finished - MyRE->m_started)*1000;
if (tdiff == 0) tdiff = 1000;
OvmsRecMutexLock lock(&MyRE->m_mutex);
writer->printf("%-20.20s %10s %6s %s\n","key","records","ms","last");
for (re_record_map_t::iterator it=MyRE->m_rmap.begin(); it!=MyRE->m_rmap.end(); ++it)
{
if ((it->second->attr.b.Discovered)||(it->second->attr.dd))
{
HighlightDump(vbuf, (const char*)it->second->last.data.u8,
it->second->last.FIR.B.DLC, it->second->attr.dc, it->second->attr.dd);
if ((argc==0)||(strstr(it->first.c_str(),argv[0])))
{
writer->printf("%-20s %10d %6d %s\n",
it->first.c_str(),it->second->rxcount,(tdiff/it->second->rxcount),vbuf);
}
}
}
}
class REInit
{
public:
REInit();
void Ticker1(std::string event, void* data);
} REInit __attribute__ ((init_priority (8800)));
void REInit::Ticker1(std::string event, void* data)
{
if (MyRE && MyNotify.HasReader("stream", "retools.status"))
{
StringWriter buf;
re_status(COMMAND_RESULT_VERBOSE, &buf, NULL, 0, NULL);
MyNotify.NotifyString("stream", "retools.status", buf.c_str());
}
if (MyRE && MyNotify.HasReader("stream", "retools.list"))
{
StringWriter buf;
re_stream_changed(COMMAND_RESULT_VERBOSE, &buf, NULL, 0, NULL);
MyNotify.NotifyString("stream", "retools.list.update", buf.c_str());
}
}
REInit::REInit()
{
ESP_LOGI(TAG, "Initialising RE Tools (8800)");
OvmsCommand* cmd_re = MyCommandApp.RegisterCommand("re","RE framework", re_status, "", 0, 0, false);
cmd_re->RegisterCommand("start","Start RE tools",
re_start,
"[filter1] ... [filterN]\n"
"Filter: <bus> | <id>[-<id>] | <bus>:<id>[-<id>]\n"
"Example: 2:2a0-37f",
0, 9);
cmd_re->RegisterCommand("stop","Stop RE tools",re_stop);
cmd_re->RegisterCommand("clear","Clear RE records",re_clear);
cmd_re->RegisterCommand("list","List RE records",re_list, "", 0, 1);
cmd_re->RegisterCommand("status","Show RE status",re_status);
OvmsCommand* cmd_dbc = cmd_re->RegisterCommand("dbc","RE DBC framework");
cmd_dbc->RegisterCommand("list","List RE DBC records",re_dbc_list, "", 0, 1);
OvmsCommand* cmd_reobdii = cmd_re->RegisterCommand("obdii","RE OBDII framework");
cmd_reobdii->RegisterCommand("standard","Set OBDII standard ID range",re_obdii_std, "<min> <max>", 2, 2);
cmd_reobdii->RegisterCommand("extended","Set OBDII extended ID range",re_obdii_ext, "<min> <max>", 2, 2);
OvmsCommand* cmd_mode = cmd_re->RegisterCommand("mode","RE mode framework");
cmd_mode->RegisterCommand("analyse","Set mode to analyse",re_mode_analyse);
cmd_mode->RegisterCommand("discover","Set mode to discover",re_mode_discover);
OvmsCommand* cmd_discover = cmd_re->RegisterCommand("discover","RE discover framework");
OvmsCommand* cmd_discover_list = cmd_discover->RegisterCommand("list","RE discover list framework");
cmd_discover_list->RegisterCommand("changed","List changed records",re_list_changed, "", 0, 1);
cmd_discover_list->RegisterCommand("discovered","List discovered records",re_list_discovered, "", 0, 1);
OvmsCommand* cmd_discover_clear = cmd_discover->RegisterCommand("clear","RE discover clear framework");
cmd_discover_clear->RegisterCommand("changed","Clear changed flags",re_clear_changed);
cmd_discover_clear->RegisterCommand("discovered","Clear discovered flags",re_clear_discovered);
OvmsCommand* cmd_stream = cmd_re->RegisterCommand("stream","RE JSON streaming");
cmd_stream->RegisterCommand("list","Output array of all RE records",re_stream_list, "[<filter>]", 0, 1);
cmd_stream->RegisterCommand("changed","Output array of changed RE records",re_stream_changed, "[<filter>]", 0, 1);
using std::placeholders::_1;
using std::placeholders::_2;
MyEvents.RegisterEvent(TAG, "ticker.1", std::bind(&REInit::Ticker1, this, _1, _2));
}