342 lines
14 KiB
C++
342 lines
14 KiB
C++
#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 = "<remote request> ";
|
|
text = " ";
|
|
n = 6; // to align text output
|
|
}
|
|
else {
|
|
if (this->frame.empty()) {
|
|
return "<empty>";
|
|
}
|
|
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<double>::quiet_NaN();
|
|
}
|
|
if (is_signed) {
|
|
value = static_cast<int16_t>((msb << 8) + lsb);
|
|
} else {
|
|
value = static_cast<uint16_t>((msb << 8) + lsb);
|
|
}
|
|
//ESP_LOGI("cb_frame::get_double", "0x%02X%02X unsigned %d, scale %d", msb, lsb, value, scale);
|
|
return static_cast<double>(value) / inv_scale;
|
|
}
|
|
// Helper function to convert four scaled values into a byte stream
|
|
// set scale to negative for signed values
|
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1)
|
|
{
|
|
std::vector<uint8_t> byte_stream(2, 0);
|
|
int value;
|
|
if(scale1 != 0) {
|
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
|
byte_stream[0] = value % 256;
|
|
byte_stream[1] = (value >> 8) % 256;
|
|
}
|
|
return byte_stream;
|
|
}
|
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2)
|
|
{
|
|
std::vector<uint8_t> byte_stream(4, 0);
|
|
int value;
|
|
if(scale1 != 0) {
|
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
|
byte_stream[0] = value % 256;
|
|
byte_stream[1] = (value >> 8) % 256;
|
|
}
|
|
if(scale2 != 0) {
|
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
|
byte_stream[2] = value % 256;
|
|
byte_stream[3] = (value >> 8) % 256;
|
|
}
|
|
return byte_stream;
|
|
}
|
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3)
|
|
{
|
|
std::vector<uint8_t> byte_stream(6, 0);
|
|
int value;
|
|
if(scale1 != 0) {
|
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
|
byte_stream[0] = value % 256;
|
|
byte_stream[1] = (value >> 8) % 256;
|
|
}
|
|
if(scale2 != 0) {
|
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
|
byte_stream[2] = value % 256;
|
|
byte_stream[3] = (value >> 8) % 256;
|
|
}
|
|
if(scale3 != 0) {
|
|
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
|
byte_stream[4] = value % 256;
|
|
byte_stream[5] = (value >> 8) % 256;
|
|
}
|
|
return byte_stream;
|
|
}
|
|
std::vector<uint8_t> 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<uint8_t> byte_stream(8, 0);
|
|
int value;
|
|
if(scale1 != 0) {
|
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
|
byte_stream[0] = value % 256;
|
|
byte_stream[1] = (value >> 8) % 256;
|
|
}
|
|
if(scale2 != 0) {
|
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
|
byte_stream[2] = value % 256;
|
|
byte_stream[3] = (value >> 8) % 256;
|
|
}
|
|
if(scale3 != 0) {
|
|
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
|
byte_stream[4] = value % 256;
|
|
byte_stream[5] = (value >> 8) % 256;
|
|
}
|
|
if(scale4 != 0) {
|
|
value = (scale4 < 0) ? static_cast<int16_t>(value4 * -scale4) : static_cast<uint16_t>(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<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4)
|
|
{
|
|
std::vector<uint8_t> byte_stream(8, 0);
|
|
int value;
|
|
if(scale1 != 0) {
|
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
|
byte_stream[0] = value % 256;
|
|
byte_stream[1] = (value >> 8) % 256;
|
|
}
|
|
if(scale2 != 0) {
|
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
|
byte_stream[2] = value % 256;
|
|
byte_stream[3] = (value >> 8) % 256;
|
|
}
|
|
if(scale3 != 0) {
|
|
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
|
byte_stream[4] = value % 256;
|
|
byte_stream[5] = (value >> 8) % 256;
|
|
}
|
|
value = (scale4 < 0) ? static_cast<int16_t>(round(value4 * -scale4)) : static_cast<uint16_t>(round(value4 * scale4));
|
|
byte_stream[6] = value % 256;
|
|
byte_stream[7] = (value >> 8) % 256;
|
|
return byte_stream;
|
|
}
|
|
|
|
} // namespace solar
|