236 lines
11 KiB
C++
236 lines
11 KiB
C++
#include "source/solar/cbf_store.h"
|
|
|
|
namespace solar
|
|
{
|
|
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id)
|
|
: cb_frame(msg_id, can_id)
|
|
{
|
|
this->id = id; // used for debugging, can be omitted
|
|
this->count = 0;
|
|
this->first_timestamp = 0;
|
|
this->last_timestamp = 0;
|
|
// ESP_LOGI(tag("store CTOR1").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
|
}
|
|
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id, time_t first_timestamp, time_t last_timestamp)
|
|
: cb_frame(msg_id, can_id)
|
|
{
|
|
this->id = id;
|
|
this->count = 0;
|
|
this->first_timestamp = first_timestamp;
|
|
this->last_timestamp = last_timestamp;
|
|
// ESP_LOGI(tag("store CTOR2").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
|
}
|
|
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id, const byte_vector& frame, bool rtr, time_t first_timestamp, time_t last_timestamp)
|
|
: cb_frame(msg_id, can_id, frame, rtr)
|
|
{
|
|
this->id = id;
|
|
this->count = 0;
|
|
this->first_timestamp = first_timestamp;
|
|
this->last_timestamp = last_timestamp;
|
|
// ESP_LOGI(tag("store CTOR3").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
|
}
|
|
cbf_store::cbf_store(const cbf_store& b, int id)
|
|
: cb_frame(b)
|
|
{
|
|
this->id = id;
|
|
this->count = b.count;
|
|
this->first_timestamp = b.first_timestamp;
|
|
this->last_timestamp = b.last_timestamp;
|
|
// ESP_LOGI(tag("store CCTOR2").c_str(), "%-20s %s", "Copied store", this->to_string().c_str());
|
|
}
|
|
cbf_store::cbf_store(const cbf_store&& b, int id)
|
|
: cb_frame(b)
|
|
{
|
|
this->id = id;
|
|
this->count = b.count;
|
|
this->first_timestamp = b.first_timestamp;
|
|
this->last_timestamp = b.last_timestamp;
|
|
// ESP_LOGI(tag("store MCCTOR2").c_str(), "%-20s %s", "Copied store", this->to_string().c_str());
|
|
}
|
|
std::shared_ptr<cbf_store> cbf_store::clone() const
|
|
{
|
|
return clone_impl();
|
|
}
|
|
std::shared_ptr<cbf_store> cbf_store::clone_impl() const
|
|
{
|
|
ESP_LOGW(tag("store CLONE").c_str(), "%-20s", "Cloning store"); // this should happen as all cloning should be done by derived classes
|
|
return std::make_shared<cbf_store>(*this);
|
|
}
|
|
bool cbf_store::is_publish_expired(time_t currenttime, int update_interval) const
|
|
{
|
|
return (this->last_timestamp == 0) || ((currenttime - this->last_timestamp) >= update_interval);
|
|
}
|
|
bool cbf_store::is_validity_expired(time_t currenttime, int timeout_interval) const
|
|
{
|
|
return (this->last_timestamp == 0) || ((currenttime - this->last_timestamp) >= timeout_interval);
|
|
}
|
|
cbf_store::cbf_updateresult cbf_store::update(const cbf_store& newitem, const int *comparecolumns)
|
|
{
|
|
ESP_LOGW(this->tag("store UPDATE").c_str(), "%-20s %s", "Default update call", this->to_string().c_str()); // this should happen as all updating should be called from derived classes, i.e. providing the publish_specs as parameters
|
|
return update(newitem, publish_spec_t{1, 5, 10}, publish_spec_t{1, 5, 10}, comparecolumns); // we are forced to instantiate this class, so we provide a default implementation
|
|
}
|
|
cbf_store::cbf_updateresult cbf_store::update(const cbf_store& newitem, publish_spec_t publish_spec, publish_spec_t rtr_publish_spec, const int *comparecolumns)
|
|
{
|
|
if(!is_valid()) {
|
|
return cbf_updateresult::stu_NONE; // cannot update an invalid store
|
|
}
|
|
auto pbspec = rtr ? rtr_publish_spec : publish_spec;
|
|
time_t newtime = newitem.last_timestamp;
|
|
bool publish_expired = this->is_publish_expired(newtime, pbspec.interval);
|
|
bool validity_expired = this->is_validity_expired(newtime, pbspec.timeout);
|
|
bool isduplicate = this->compare(newitem, comparecolumns) == 0;
|
|
time_t reset_timer = this->first_timestamp + pbspec.interval - newtime;
|
|
int timediff = is_valid() ? (int)(newtime - this->last_timestamp) : -1;
|
|
auto result = isduplicate ? cbf_updateresult::stu_DUPLICATE : cbf_updateresult::stu_NONE;
|
|
// auto ftstime = ESPTime::from_epoch_local(this->first_timestamp).strftime("%H:%M:%S");
|
|
// auto ltstime = ESPTime::from_epoch_local(this->last_timestamp).strftime("%H:%M:%S");
|
|
// auto ntstime = ESPTime::from_epoch_local(newtime).strftime("%H:%M:%S");
|
|
// if(rtr)
|
|
// ESP_LOGI(this->tag("store UPDATE").c_str(), "A: %s PBS %d,%d,%d PE:%s VE:%s DUP:%s FTS %s LTS %s NTS %s RES[D%s P%s]", rtr ? "RTR" : "NML", pbspec.on_count, pbspec// .interval, pbspec.timeout, publish_expired ? "Y" : "N", validity_expired ? "Y" : "N", isduplicate ? "Y" : "N", ftstime.c_str(), ltstime.c_str(), ntstime.c_str(),// result & cbf_updateresult::stu_DUPLICATE ? "Y" : "N", result & cbf_updateresult::stu_PUBLISH ? "Y" : "N");
|
|
if(validity_expired) {
|
|
*this = newitem;
|
|
if(!isduplicate) {
|
|
this->count = 1;
|
|
publish_expired = false;
|
|
}
|
|
}
|
|
if(isduplicate || publish_expired) {
|
|
this->count++;
|
|
if(this->count == pbspec.on_count)
|
|
this->setpublish(); // must be reset by caller after publish
|
|
if(this->getpublish())
|
|
result = static_cast<cbf_updateresult>(result | cbf_updateresult::stu_PUBLISH);
|
|
this->last_timestamp = newtime;
|
|
if(reset_timer <= 0) {
|
|
this->first_timestamp = newtime;
|
|
this->count = 1;
|
|
}
|
|
}
|
|
// ftstime = ESPTime::from_epoch_local(this->first_timestamp).strftime("%H:%M:%S");
|
|
// ltstime = ESPTime::from_epoch_local(this->last_timestamp).strftime("%H:%M:%S");
|
|
// if(rtr)
|
|
// ESP_LOGI(this->tag("store UPDATE").c_str(), "B: %s PBS %d,%d,%d PE:%s VE:%s DUP:%s FTS %s LTS %s NTS %s RES[D%s P%s]", rtr ? "RTR" : "NML", pbspec.on_count, pbspec.interval, pbspec.timeout, publish_expired ? "Y" : "N", validity_expired ? "Y" : "N", isduplicate ? "Y" : "N", ftstime.c_str(), ltstime.c_str(), ntstime.c_str(), result & cbf_updateresult::stu_DUPLICATE ? "Y" : "N", result & cbf_updateresult::stu_PUBLISH ? "Y" : "N");
|
|
|
|
return result;
|
|
}
|
|
int cbf_store::compare(const cbf_store& b, const int *comparecolumns) const
|
|
{
|
|
int result = 0;
|
|
bool stopcompare = false;
|
|
bool isdefaultcompare = comparecolumns == nullptr || *comparecolumns == 0;
|
|
int ncmpcols = 0;
|
|
while (comparecolumns[ncmpcols] != 0) ncmpcols++;
|
|
if (isdefaultcompare) {
|
|
for (int i = sisortNULL + 1; i < sisortEND && !stopcompare && result == 0; i++) {
|
|
bool validnextcol = i < sisortEND - 1;
|
|
result = compare(b, i, &stopcompare, validnextcol);
|
|
}
|
|
}
|
|
else {
|
|
for (int i = 0; i < sisortEND && !stopcompare && result == 0 && comparecolumns[i] != 0; i++) {
|
|
int cmpcol = comparecolumns[i];
|
|
bool validnextcol = cmpcol != 0 && comparecolumns[i + 1] != 0;
|
|
result = compare(b, cmpcol, &stopcompare, validnextcol);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
int cbf_store::compare(const cbf_store &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const
|
|
{
|
|
int result = 0;
|
|
int reverseorderflag = cmpflag & sisortreverse;
|
|
int casesensitiveflag = cmpflag & sisortcase;
|
|
int nulliswildcardflag = cmpflag & sisortwild;
|
|
int stopcompareflag = cmpflag & sistopcomparecol;
|
|
cmpflag = cmpflag & ~FLAGBITS;
|
|
if (cmpflag == 0)
|
|
return result;
|
|
bool casesensitive = casesensitiveflag != 0;
|
|
bool sortwild = nulliswildcardflag != 0;
|
|
bool stopcompare = stopcompareflag != 0;
|
|
*stopcomparesignal = false;
|
|
switch (cmpflag) {
|
|
case sisortNULL:
|
|
return 0;
|
|
case sisortcanid:
|
|
{
|
|
bool bothvalid = this->can_id != 0 && b.can_id != 0;
|
|
if (bothvalid) {
|
|
result = num_compare(this->can_id, b.can_id);
|
|
*stopcomparesignal = stopcompare; // set flag only if both items are valid
|
|
} else {
|
|
if (sortwild)
|
|
result = 0;
|
|
else {
|
|
bool bothinvalid = !(this->can_id || b.can_id);
|
|
if (validnextcol && bothinvalid)
|
|
result = 0; //if validnextcol then return 0 if both IDs are invalid
|
|
result = this->can_id ? 1 : -1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case sisortmsgid:
|
|
result = num_compare(this->msg_id, b.msg_id);
|
|
break;
|
|
case sisortcount:
|
|
{
|
|
result = num_compare(this->count, b.count);
|
|
break;
|
|
}
|
|
case sisortfirst_timestamp:
|
|
{
|
|
auto a_ts = this->first_timestamp;
|
|
auto b_ts = b.first_timestamp;
|
|
result = num_compare(a_ts, b_ts);
|
|
break;
|
|
}
|
|
case sisortlast_timestamp:
|
|
{
|
|
auto a_ts = this->last_timestamp;
|
|
auto b_ts = b.last_timestamp;
|
|
result = num_compare(a_ts, b_ts);
|
|
break;
|
|
}
|
|
case sisortrtr:
|
|
result = bool_compare(this->rtr, b.rtr);
|
|
break;
|
|
case sisortframe:
|
|
{
|
|
result = this->compare_frame(b.frame);
|
|
break;
|
|
}
|
|
default:
|
|
result = 0;
|
|
// ESP_LOGE("cbf_store::compare", "Unknown compare column %d", cmpflag);
|
|
*stopcomparesignal = true; // stop compare as we don't know how to handle
|
|
// should never reach here as all cases must be dealt with
|
|
break;
|
|
}
|
|
if (reverseorderflag != 0)
|
|
result = -result;
|
|
return result;
|
|
}
|
|
std::string cbf_store::tag(const std::string& prefix, int prefixlen) const
|
|
{
|
|
// Be extra careful with the placement of printf format specifiers and their corresponding arguments.
|
|
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
|
char tag[48];
|
|
if(prefix.length() == 0)
|
|
snprintf(tag, sizeof(tag), "#%04d s%d 0x%03X %s", msg_id, id, can_id, getpublish() ? "P" : " ");
|
|
else
|
|
snprintf(tag, sizeof(tag), "%-*s #%04d s%d 0x%03X %s", prefixlen, prefix.c_str(), msg_id, id, can_id, getpublish() ? "P" : " ");
|
|
return std::string(tag);
|
|
}
|
|
std::string cbf_store::to_string() const
|
|
{
|
|
// Be extra careful with the placement of printf format specifiers and their corresponding arguments.
|
|
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
|
char buffer[80];
|
|
auto ftstime = is_valid() ? ESPTime::from_epoch_local(first_timestamp).strftime("%H:%M:%S") : "N/A";
|
|
auto ltstime = is_valid() ? ESPTime::from_epoch_local(last_timestamp).strftime("%H:%M:%S") : "N/A";
|
|
snprintf(buffer, sizeof(buffer), " Fts: %s Lts: %s Count: %2d %s", ftstime.c_str(), ltstime.c_str(), count, cb_frame::to_string().c_str());
|
|
return std::string(buffer);
|
|
}
|
|
} // namespace solar
|