#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::clone() const { return clone_impl(); } std::shared_ptr 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(*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); } bool cbf_store::update(const cbf_store& newitem, bool& publish, 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 publish_spec as a parameter return update(newitem, publish_spec_t{1, 5, 10}, publish, comparecolumns); // we are forced to instantiate this class, so we provide a default implementation } bool cbf_store::update(const cbf_store& newitem, publish_spec_t publish_spec, bool& publish, const int *comparecolumns) { if(!is_valid()) { return false; } time_t newtime = newitem.last_timestamp; bool publish_expired = this->is_publish_expired(newtime, publish_spec.interval); bool validity_expired = this->is_validity_expired(newtime, publish_spec.timeout); bool isduplicate = this->compare(newitem, comparecolumns) == 0; time_t reset_timer = this->first_timestamp + publish_spec.interval - newtime; auto ntstime = ESPTime::from_epoch_local(newtime).strftime("%H:%M:%S"); int timediff = is_valid() ? (int)(newtime - this->last_timestamp) : -1; //ESP_LOGI(this->tag("store UPDATE Bef").c_str(), "%s Td:%2d To:%s Ex:%s Rt:%2d Nts: %s %s", isduplicate ? "DUP" : "NEW", timediff, validity_expired ? "Y" : "N", publish_expired ? "Y" : "N", static_cast(reset_timer), ntstime.c_str(), this->to_string().c_str()); if(validity_expired) { *this = newitem; if(!isduplicate) { this->count = 1; publish_expired = false; } } if(isduplicate || publish_expired) { this->count++; publish = (this->count == publish_spec.on_count); this->last_timestamp = newtime; if(reset_timer <= 0) { this->first_timestamp = newtime; this->count = 1; } } //ESP_LOGI(this->tag("store UPDATE Aft").c_str(), "%s Td:%2d To:%s Ex:%s Rt:%2d Pu:%s Nts: %s %s", isduplicate ? "DUP" : "NEW", timediff, validity_expired ? "Y" : "N", publish_expired ? "Y" : "N", static_cast(reset_timer), publish ? "Y" : "N", ntstime.c_str(), this->to_string().c_str()); return isduplicate; } 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) 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 id%d 0x%03X ", msg_id, id, can_id); else snprintf(tag, sizeof(tag), "%-18s %04d id%d 0x%03X ", prefix.c_str(), msg_id, id, can_id); 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