#include "source/solar/cb_frame.h" // limit the amount of logging from this class as it can be called very frequently. Too much logging can lead to instability and no connectivity. namespace solar { const cb_frame emptyframe = cb_frame(); // to return a reference to when no valid frame is found cb_frame::cb_frame() { this->msg_id = 0; this->publish = false; this->rtr = false; this->can_id = 0; this->frame.clear(); // ESP_LOGI(tag("frame CTOR0").c_str(), "%-20s %s", "Created frame", this->to_string().c_str()); } cb_frame::cb_frame(int msg_id, uint32_t can_id) { this->msg_id = msg_id; this->publish = false; this->rtr = false; this->can_id = can_id; this->frame.clear(); // ESP_LOGI(tag("frame CTOR1").c_str(), "%-20s %s", "Created frame", this->to_string().c_str()); } cb_frame::cb_frame(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr) { this->msg_id = msg_id; this->publish = false; this->rtr = rtr; this->can_id = can_id; this->frame = frame; // ESP_LOGI(tag("frame CTOR2").c_str(), "%-20s %s", "Created frame", this->to_string().c_str()); } void cb_frame::clear() { this->msg_id = 0; this->publish = false; this->rtr = false; this->can_id = 0; this->frame.clear(); } void cb_frame::swap(cb_frame &s) { std::swap(this->msg_id, s.msg_id); std::swap(this->publish, s.publish); std::swap(this->rtr, s.rtr); std::swap(this->can_id, s.can_id); this->frame.swap(s.frame); } bool cb_frame::is_valid() const { return this->can_id != 0; } void cb_frame::setpublish(bool do_publish) const { this->publish = do_publish; } bool cb_frame::getpublish() const { return this->publish; } bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, bool extended_id, bool remote_transmission_request) const { if(canbus == nullptr) { ESP_LOGE(tag("cb_frame::send_data").c_str(), "CAN bus interface is null, cannot send data"); return false; } if(!this->is_valid()) { ESP_LOGE(tag("cb_frame::send_data").c_str(), "Frame is not valid, cannot send data: %s", this->to_string().c_str()); return false; } return cb_frame::send_frame(canbus, this->can_id, this->frame, extended_id, remote_transmission_request); } bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id, bool remote_transmission_request) { if(canbus == nullptr) { return false; } canbus->send_data(can_id, extended_id, remote_transmission_request, frame); return true; } int cb_frame::compare(const cb_frame& 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 < ncmpcols && !stopcompare && result == 0; i++) { result = compare(b, comparecolumns[i], &stopcompare, true); } } return result; } int cb_frame::compare(const cb_frame &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->is_valid() && b.is_valid(); 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->is_valid() || b.is_valid()); 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 sisortrtr: result = bool_compare(this->rtr, b.rtr); break; case sisortframe: { result = this->compare_frame(b.frame); break; } default: result = 0; ESP_LOGE("cb_frame::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; } int cb_frame::compare_frame(const byte_vector& _frame) const { int result = 0; auto j = _frame.begin(); for(auto i = this->frame.begin(); i != this->frame.end() && result == 0; ++i) { if(j == _frame.end()) { // ESP_LOGW("cb_frame::compare", "Frame size mismatch: this.frame.size()=%zu, _frame.size()=%zu", this->frame.size(), _frame.size()); result = 1; // this frame is longer than _frame break; } // ESP_LOGI("cb_frame::compare", "Comparing frame byte %zu: %02X vs %02X", i - this->frame.begin(), *i, *j); result = num_compare(*i, *j); // if (result != 0) { // ESP_LOGI("cb_frame::compare", "Frame byte %zu mismatch: %02X vs %02X", i - this->frame.begin(), *i, *j); // } j++; } return result; } std::string cb_frame::tag(const std::string& prefix, int prefixlen) const { char tag[48]; if(prefix.length() == 0) snprintf(tag, sizeof(tag), "%04d 0x%03X ", msg_id, can_id); else snprintf(tag, sizeof(tag), "%-*s %04d 0x%03X ", prefixlen, prefix.c_str(), msg_id, can_id); return std::string(tag); } std::string cb_frame::to_string() const { // Be extra careful with the placement of printf format specifiers and their corresponding arguments to avoid buffer overflows and ensure correct formatting. // Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes. std::string text = ""; char hex[10]; std::string line; int n = 0; if(rtr) { line = " "; text = " "; n = 6; // to align text output } else { if (this->frame.empty()) { return ""; } for (const auto& byte : frame) { n++; snprintf(hex, sizeof(hex), "%02X ", byte); line += hex; if(byte > 31 && byte < 127) { text += (char) byte; } else { text += "."; } } } for (int i = n; i < 8; i++) { line += " "; text += " "; } return line + " " + text; } // helper function to extract a scaled double value from two bytes double cb_frame::get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed) { int value = 0; if ((lsb == 0xFF && msb == 0xFF) || inv_scale == 0) { return std::numeric_limits::quiet_NaN(); } if (is_signed) { value = static_cast((msb << 8) + lsb); } else { value = static_cast((msb << 8) + lsb); } //ESP_LOGI("cb_frame::get_double", "0x%02X%02X unsigned %d, scale %d", msb, lsb, value, scale); return static_cast(value) / inv_scale; } // Helper function to convert four scaled values into a byte stream // set scale to negative for signed values std::vector cb_frame::get_byte_stream(double value1, int scale1) { std::vector byte_stream(2, 0); int value; if(scale1 != 0) { value = (scale1 < 0) ? static_cast(value1 * -scale1) : static_cast(value1 * scale1); byte_stream[0] = value % 256; byte_stream[1] = (value >> 8) % 256; } return byte_stream; } std::vector cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2) { std::vector byte_stream(4, 0); int value; if(scale1 != 0) { value = (scale1 < 0) ? static_cast(value1 * -scale1) : static_cast(value1 * scale1); byte_stream[0] = value % 256; byte_stream[1] = (value >> 8) % 256; } if(scale2 != 0) { value = (scale2 < 0) ? static_cast(value2 * -scale2) : static_cast(value2 * scale2); byte_stream[2] = value % 256; byte_stream[3] = (value >> 8) % 256; } return byte_stream; } std::vector cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3) { std::vector byte_stream(6, 0); int value; if(scale1 != 0) { value = (scale1 < 0) ? static_cast(value1 * -scale1) : static_cast(value1 * scale1); byte_stream[0] = value % 256; byte_stream[1] = (value >> 8) % 256; } if(scale2 != 0) { value = (scale2 < 0) ? static_cast(value2 * -scale2) : static_cast(value2 * scale2); byte_stream[2] = value % 256; byte_stream[3] = (value >> 8) % 256; } if(scale3 != 0) { value = (scale3 < 0) ? static_cast(value3 * -scale3) : static_cast(value3 * scale3); byte_stream[4] = value % 256; byte_stream[5] = (value >> 8) % 256; } return byte_stream; } std::vector cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, int scale4) { //ESP_LOGI("cb_frame::get_byte_stream", "1: %.3f 2: %.3f 3: %.3f 4: %.3f", value1, value2, value3, value4); std::vector byte_stream(8, 0); int value; if(scale1 != 0) { value = (scale1 < 0) ? static_cast(value1 * -scale1) : static_cast(value1 * scale1); byte_stream[0] = value % 256; byte_stream[1] = (value >> 8) % 256; } if(scale2 != 0) { value = (scale2 < 0) ? static_cast(value2 * -scale2) : static_cast(value2 * scale2); byte_stream[2] = value % 256; byte_stream[3] = (value >> 8) % 256; } if(scale3 != 0) { value = (scale3 < 0) ? static_cast(value3 * -scale3) : static_cast(value3 * scale3); byte_stream[4] = value % 256; byte_stream[5] = (value >> 8) % 256; } if(scale4 != 0) { value = (scale4 < 0) ? static_cast(value4 * -scale4) : static_cast(value4 * scale4); byte_stream[6] = value % 256; byte_stream[7] = (value >> 8) % 256; } return byte_stream; } // Helper function to convert four scaled values into a byte stream // set scale to negative for signed values // Overloaded function to handle double scale for the fourth value std::vector cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4) { std::vector byte_stream(8, 0); int value; if(scale1 != 0) { value = (scale1 < 0) ? static_cast(value1 * -scale1) : static_cast(value1 * scale1); byte_stream[0] = value % 256; byte_stream[1] = (value >> 8) % 256; } if(scale2 != 0) { value = (scale2 < 0) ? static_cast(value2 * -scale2) : static_cast(value2 * scale2); byte_stream[2] = value % 256; byte_stream[3] = (value >> 8) % 256; } if(scale3 != 0) { value = (scale3 < 0) ? static_cast(value3 * -scale3) : static_cast(value3 * scale3); byte_stream[4] = value % 256; byte_stream[5] = (value >> 8) % 256; } value = (scale4 < 0) ? static_cast(round(value4 * -scale4)) : static_cast(round(value4 * scale4)); byte_stream[6] = value % 256; byte_stream[7] = (value >> 8) % 256; return byte_stream; } } // namespace solar