merge prep

This commit is contained in:
Chris Stuurman 2026-03-20 17:19:14 +02:00
parent 4fb865c0fa
commit 605523ede5
18 changed files with 1802 additions and 1802 deletions

View File

@ -1,6 +1,6 @@
# ESPHome config/source/solar Folder # ESPHome config/source/solar Folder
This folder contains Contains source code files and related resources for solar projects. This folder contains Contains source code files and related resources for solar projects.
For more information, visit the [ESPHome documentation](https://esphome.io/). For more information, visit the [ESPHome documentation](https://esphome.io/).

View File

@ -1,342 +1,342 @@
#include "source/solar/cb_frame.h" #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. // 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 namespace solar
{ {
const cb_frame emptyframe = cb_frame(); // to return a reference to when no valid frame is found const cb_frame emptyframe = cb_frame(); // to return a reference to when no valid frame is found
cb_frame::cb_frame() cb_frame::cb_frame()
{ {
this->msg_id = 0; this->msg_id = 0;
this->publish = false; this->publish = false;
this->rtr = false; this->rtr = false;
this->can_id = 0; this->can_id = 0;
this->frame.clear(); this->frame.clear();
// ESP_LOGI(tag("frame CTOR0").c_str(), "%-20s %s", "Created frame", this->to_string().c_str()); // 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) cb_frame::cb_frame(int msg_id, uint32_t can_id)
{ {
this->msg_id = msg_id; this->msg_id = msg_id;
this->publish = false; this->publish = false;
this->rtr = false; this->rtr = false;
this->can_id = can_id; this->can_id = can_id;
this->frame.clear(); this->frame.clear();
// ESP_LOGI(tag("frame CTOR1").c_str(), "%-20s %s", "Created frame", this->to_string().c_str()); // 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) cb_frame::cb_frame(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr)
{ {
this->msg_id = msg_id; this->msg_id = msg_id;
this->publish = false; this->publish = false;
this->rtr = rtr; this->rtr = rtr;
this->can_id = can_id; this->can_id = can_id;
this->frame = frame; this->frame = frame;
// ESP_LOGI(tag("frame CTOR2").c_str(), "%-20s %s", "Created frame", this->to_string().c_str()); // ESP_LOGI(tag("frame CTOR2").c_str(), "%-20s %s", "Created frame", this->to_string().c_str());
} }
void cb_frame::clear() void cb_frame::clear()
{ {
this->msg_id = 0; this->msg_id = 0;
this->publish = false; this->publish = false;
this->rtr = false; this->rtr = false;
this->can_id = 0; this->can_id = 0;
this->frame.clear(); this->frame.clear();
} }
void cb_frame::swap(cb_frame &s) void cb_frame::swap(cb_frame &s)
{ {
std::swap(this->msg_id, s.msg_id); std::swap(this->msg_id, s.msg_id);
std::swap(this->publish, s.publish); std::swap(this->publish, s.publish);
std::swap(this->rtr, s.rtr); std::swap(this->rtr, s.rtr);
std::swap(this->can_id, s.can_id); std::swap(this->can_id, s.can_id);
this->frame.swap(s.frame); this->frame.swap(s.frame);
} }
bool cb_frame::is_valid() const bool cb_frame::is_valid() const
{ {
return this->can_id != 0; return this->can_id != 0;
} }
void cb_frame::setpublish(bool do_publish) const void cb_frame::setpublish(bool do_publish) const
{ {
this->publish = do_publish; this->publish = do_publish;
} }
bool cb_frame::getpublish() const bool cb_frame::getpublish() const
{ {
return this->publish; return this->publish;
} }
bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, bool extended_id, bool remote_transmission_request) const bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, bool extended_id, bool remote_transmission_request) const
{ {
if(canbus == nullptr) { if(canbus == nullptr) {
ESP_LOGE(tag("cb_frame::send_data").c_str(), "CAN bus interface is null, cannot send data"); ESP_LOGE(tag("cb_frame::send_data").c_str(), "CAN bus interface is null, cannot send data");
return false; return false;
} }
if(!this->is_valid()) { 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()); 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 false;
} }
return cb_frame::send_frame(canbus, this->can_id, this->frame, extended_id, remote_transmission_request); 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) 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) { if(canbus == nullptr) {
return false; return false;
} }
canbus->send_data(can_id, extended_id, remote_transmission_request, frame); canbus->send_data(can_id, extended_id, remote_transmission_request, frame);
return true; return true;
} }
int cb_frame::compare(const cb_frame& b, const int *comparecolumns) const int cb_frame::compare(const cb_frame& b, const int *comparecolumns) const
{ {
int result = 0; int result = 0;
bool stopcompare = false; bool stopcompare = false;
bool isdefaultcompare = comparecolumns == nullptr || *comparecolumns == 0; bool isdefaultcompare = comparecolumns == nullptr || *comparecolumns == 0;
int ncmpcols = 0; int ncmpcols = 0;
while (comparecolumns[ncmpcols] != 0) ncmpcols++; while (comparecolumns[ncmpcols] != 0) ncmpcols++;
if (isdefaultcompare) { if (isdefaultcompare) {
for (int i = sisortNULL + 1; i < sisortEND && !stopcompare && result == 0; i++) { for (int i = sisortNULL + 1; i < sisortEND && !stopcompare && result == 0; i++) {
bool validnextcol = i < sisortEND - 1; bool validnextcol = i < sisortEND - 1;
result = compare(b, i, &stopcompare, validnextcol); result = compare(b, i, &stopcompare, validnextcol);
} }
} else { } else {
for (int i = 0; i < ncmpcols && !stopcompare && result == 0; i++) { for (int i = 0; i < ncmpcols && !stopcompare && result == 0; i++) {
result = compare(b, comparecolumns[i], &stopcompare, true); result = compare(b, comparecolumns[i], &stopcompare, true);
} }
} }
return result; return result;
} }
int cb_frame::compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const int cb_frame::compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const
{ {
int result = 0; int result = 0;
int reverseorderflag = cmpflag & sisortreverse; int reverseorderflag = cmpflag & sisortreverse;
int casesensitiveflag = cmpflag & sisortcase; int casesensitiveflag = cmpflag & sisortcase;
int nulliswildcardflag = cmpflag & sisortwild; int nulliswildcardflag = cmpflag & sisortwild;
int stopcompareflag = cmpflag & sistopcomparecol; int stopcompareflag = cmpflag & sistopcomparecol;
cmpflag = cmpflag & ~FLAGBITS; cmpflag = cmpflag & ~FLAGBITS;
if (cmpflag == 0) if (cmpflag == 0)
return result; return result;
bool casesensitive = casesensitiveflag != 0; bool casesensitive = casesensitiveflag != 0;
bool sortwild = nulliswildcardflag != 0; bool sortwild = nulliswildcardflag != 0;
bool stopcompare = stopcompareflag != 0; bool stopcompare = stopcompareflag != 0;
*stopcomparesignal = false; *stopcomparesignal = false;
switch (cmpflag) { switch (cmpflag) {
case sisortNULL: case sisortNULL:
return 0; return 0;
case sisortcanid: case sisortcanid:
{ {
bool bothvalid = this->is_valid() && b.is_valid(); bool bothvalid = this->is_valid() && b.is_valid();
if (bothvalid) { if (bothvalid) {
result = num_compare(this->can_id, b.can_id); result = num_compare(this->can_id, b.can_id);
*stopcomparesignal = stopcompare; // set flag only if both items are valid *stopcomparesignal = stopcompare; // set flag only if both items are valid
} else { } else {
if (sortwild) if (sortwild)
result = 0; result = 0;
else { else {
bool bothinvalid = !(this->is_valid() || b.is_valid()); bool bothinvalid = !(this->is_valid() || b.is_valid());
if (validnextcol && bothinvalid) if (validnextcol && bothinvalid)
result = 0; //if validnextcol then return 0 if both IDs are invalid result = 0; //if validnextcol then return 0 if both IDs are invalid
result = this->can_id ? 1 : -1; result = this->can_id ? 1 : -1;
} }
} }
break; break;
} }
case sisortmsgid: case sisortmsgid:
result = num_compare(this->msg_id, b.msg_id); result = num_compare(this->msg_id, b.msg_id);
break; break;
case sisortrtr: case sisortrtr:
result = bool_compare(this->rtr, b.rtr); result = bool_compare(this->rtr, b.rtr);
break; break;
case sisortframe: case sisortframe:
{ {
result = this->compare_frame(b.frame); result = this->compare_frame(b.frame);
break; break;
} }
default: default:
result = 0; result = 0;
ESP_LOGE("cb_frame::compare", "Unknown compare column %d", cmpflag); ESP_LOGE("cb_frame::compare", "Unknown compare column %d", cmpflag);
*stopcomparesignal = true; // stop compare as we don't know how to handle *stopcomparesignal = true; // stop compare as we don't know how to handle
// should never reach here as all cases must be dealt with // should never reach here as all cases must be dealt with
break; break;
} }
if (reverseorderflag != 0) if (reverseorderflag != 0)
result = -result; result = -result;
return result; return result;
} }
int cb_frame::compare_frame(const byte_vector& _frame) const int cb_frame::compare_frame(const byte_vector& _frame) const
{ {
int result = 0; int result = 0;
auto j = _frame.begin(); auto j = _frame.begin();
for(auto i = this->frame.begin(); i != this->frame.end() && result == 0; ++i) { for(auto i = this->frame.begin(); i != this->frame.end() && result == 0; ++i) {
if(j == _frame.end()) { if(j == _frame.end()) {
// ESP_LOGW("cb_frame::compare", "Frame size mismatch: this.frame.size()=%zu, _frame.size()=%zu", this->frame.size(), _frame.size()); // 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 result = 1; // this frame is longer than _frame
break; break;
} }
// ESP_LOGI("cb_frame::compare", "Comparing frame byte %zu: %02X vs %02X", i - this->frame.begin(), *i, *j); // ESP_LOGI("cb_frame::compare", "Comparing frame byte %zu: %02X vs %02X", i - this->frame.begin(), *i, *j);
result = num_compare(*i, *j); result = num_compare(*i, *j);
// if (result != 0) { // if (result != 0) {
// ESP_LOGI("cb_frame::compare", "Frame byte %zu mismatch: %02X vs %02X", i - this->frame.begin(), *i, *j); // ESP_LOGI("cb_frame::compare", "Frame byte %zu mismatch: %02X vs %02X", i - this->frame.begin(), *i, *j);
// } // }
j++; j++;
} }
return result; return result;
} }
std::string cb_frame::tag(const std::string& prefix, int prefixlen) const std::string cb_frame::tag(const std::string& prefix, int prefixlen) const
{ {
char tag[48]; char tag[48];
if(prefix.length() == 0) if(prefix.length() == 0)
snprintf(tag, sizeof(tag), "%04d 0x%03X ", msg_id, can_id); snprintf(tag, sizeof(tag), "%04d 0x%03X ", msg_id, can_id);
else else
snprintf(tag, sizeof(tag), "%-*s %04d 0x%03X ", prefixlen, prefix.c_str(), msg_id, can_id); snprintf(tag, sizeof(tag), "%-*s %04d 0x%03X ", prefixlen, prefix.c_str(), msg_id, can_id);
return std::string(tag); return std::string(tag);
} }
std::string cb_frame::to_string() const 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. // 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. // Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
std::string text = ""; std::string text = "";
char hex[10]; char hex[10];
std::string line; std::string line;
int n = 0; int n = 0;
if(rtr) { if(rtr) {
line = "<remote request> "; line = "<remote request> ";
text = " "; text = " ";
n = 6; // to align text output n = 6; // to align text output
} }
else { else {
if (this->frame.empty()) { if (this->frame.empty()) {
return "<empty>"; return "<empty>";
} }
for (const auto& byte : frame) { for (const auto& byte : frame) {
n++; n++;
snprintf(hex, sizeof(hex), "%02X ", byte); snprintf(hex, sizeof(hex), "%02X ", byte);
line += hex; line += hex;
if(byte > 31 && byte < 127) { if(byte > 31 && byte < 127) {
text += (char) byte; text += (char) byte;
} }
else { else {
text += "."; text += ".";
} }
} }
} }
for (int i = n; i < 8; i++) { for (int i = n; i < 8; i++) {
line += " "; line += " ";
text += " "; text += " ";
} }
return line + " " + text; return line + " " + text;
} }
// helper function to extract a scaled double value from two bytes // 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) double cb_frame::get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed)
{ {
int value = 0; int value = 0;
if ((lsb == 0xFF && msb == 0xFF) || inv_scale == 0) { if ((lsb == 0xFF && msb == 0xFF) || inv_scale == 0) {
return std::numeric_limits<double>::quiet_NaN(); return std::numeric_limits<double>::quiet_NaN();
} }
if (is_signed) { if (is_signed) {
value = static_cast<int16_t>((msb << 8) + lsb); value = static_cast<int16_t>((msb << 8) + lsb);
} else { } else {
value = static_cast<uint16_t>((msb << 8) + lsb); 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); //ESP_LOGI("cb_frame::get_double", "0x%02X%02X unsigned %d, scale %d", msb, lsb, value, scale);
return static_cast<double>(value) / inv_scale; return static_cast<double>(value) / inv_scale;
} }
// Helper function to convert four scaled values into a byte stream // Helper function to convert four scaled values into a byte stream
// set scale to negative for signed values // set scale to negative for signed values
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1) std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1)
{ {
std::vector<uint8_t> byte_stream(2, 0); std::vector<uint8_t> byte_stream(2, 0);
int value; int value;
if(scale1 != 0) { if(scale1 != 0) {
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1); value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
byte_stream[0] = value % 256; byte_stream[0] = value % 256;
byte_stream[1] = (value >> 8) % 256; byte_stream[1] = (value >> 8) % 256;
} }
return byte_stream; return byte_stream;
} }
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2) 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); std::vector<uint8_t> byte_stream(4, 0);
int value; int value;
if(scale1 != 0) { if(scale1 != 0) {
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1); value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
byte_stream[0] = value % 256; byte_stream[0] = value % 256;
byte_stream[1] = (value >> 8) % 256; byte_stream[1] = (value >> 8) % 256;
} }
if(scale2 != 0) { if(scale2 != 0) {
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2); value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
byte_stream[2] = value % 256; byte_stream[2] = value % 256;
byte_stream[3] = (value >> 8) % 256; byte_stream[3] = (value >> 8) % 256;
} }
return byte_stream; 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> 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); std::vector<uint8_t> byte_stream(6, 0);
int value; int value;
if(scale1 != 0) { if(scale1 != 0) {
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1); value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
byte_stream[0] = value % 256; byte_stream[0] = value % 256;
byte_stream[1] = (value >> 8) % 256; byte_stream[1] = (value >> 8) % 256;
} }
if(scale2 != 0) { if(scale2 != 0) {
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2); value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
byte_stream[2] = value % 256; byte_stream[2] = value % 256;
byte_stream[3] = (value >> 8) % 256; byte_stream[3] = (value >> 8) % 256;
} }
if(scale3 != 0) { if(scale3 != 0) {
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3); value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
byte_stream[4] = value % 256; byte_stream[4] = value % 256;
byte_stream[5] = (value >> 8) % 256; byte_stream[5] = (value >> 8) % 256;
} }
return byte_stream; 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) 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); //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); std::vector<uint8_t> byte_stream(8, 0);
int value; int value;
if(scale1 != 0) { if(scale1 != 0) {
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1); value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
byte_stream[0] = value % 256; byte_stream[0] = value % 256;
byte_stream[1] = (value >> 8) % 256; byte_stream[1] = (value >> 8) % 256;
} }
if(scale2 != 0) { if(scale2 != 0) {
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2); value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
byte_stream[2] = value % 256; byte_stream[2] = value % 256;
byte_stream[3] = (value >> 8) % 256; byte_stream[3] = (value >> 8) % 256;
} }
if(scale3 != 0) { if(scale3 != 0) {
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3); value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
byte_stream[4] = value % 256; byte_stream[4] = value % 256;
byte_stream[5] = (value >> 8) % 256; byte_stream[5] = (value >> 8) % 256;
} }
if(scale4 != 0) { if(scale4 != 0) {
value = (scale4 < 0) ? static_cast<int16_t>(value4 * -scale4) : static_cast<uint16_t>(value4 * scale4); value = (scale4 < 0) ? static_cast<int16_t>(value4 * -scale4) : static_cast<uint16_t>(value4 * scale4);
byte_stream[6] = value % 256; byte_stream[6] = value % 256;
byte_stream[7] = (value >> 8) % 256; byte_stream[7] = (value >> 8) % 256;
} }
return byte_stream; return byte_stream;
} }
// Helper function to convert four scaled values into a byte stream // Helper function to convert four scaled values into a byte stream
// set scale to negative for signed values // set scale to negative for signed values
// Overloaded function to handle double scale for the fourth value // 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> 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); std::vector<uint8_t> byte_stream(8, 0);
int value; int value;
if(scale1 != 0) { if(scale1 != 0) {
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1); value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
byte_stream[0] = value % 256; byte_stream[0] = value % 256;
byte_stream[1] = (value >> 8) % 256; byte_stream[1] = (value >> 8) % 256;
} }
if(scale2 != 0) { if(scale2 != 0) {
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2); value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
byte_stream[2] = value % 256; byte_stream[2] = value % 256;
byte_stream[3] = (value >> 8) % 256; byte_stream[3] = (value >> 8) % 256;
} }
if(scale3 != 0) { if(scale3 != 0) {
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3); value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
byte_stream[4] = value % 256; byte_stream[4] = value % 256;
byte_stream[5] = (value >> 8) % 256; byte_stream[5] = (value >> 8) % 256;
} }
value = (scale4 < 0) ? static_cast<int16_t>(round(value4 * -scale4)) : static_cast<uint16_t>(round(value4 * scale4)); value = (scale4 < 0) ? static_cast<int16_t>(round(value4 * -scale4)) : static_cast<uint16_t>(round(value4 * scale4));
byte_stream[6] = value % 256; byte_stream[6] = value % 256;
byte_stream[7] = (value >> 8) % 256; byte_stream[7] = (value >> 8) % 256;
return byte_stream; return byte_stream;
} }
} // namespace solar } // namespace solar

View File

@ -1,66 +1,66 @@
#ifndef __SOLAR_CB_FRAME // include GUARD #ifndef __SOLAR_CB_FRAME // include GUARD
#define __SOLAR_CB_FRAME #define __SOLAR_CB_FRAME
#include "esphome.h" #include "esphome.h"
#include <utility> #include <utility>
#include "common.h" #include "common.h"
using namespace esphome; using namespace esphome;
namespace solar namespace solar
{ {
class cbf_store; class cbf_store;
class cb_frame { class cb_frame {
private: private:
mutable bool publish; // whether this frame should be published mutable bool publish; // whether this frame should be published
public: public:
enum cbf_sortcolumns : int enum cbf_sortcolumns : int
{ {
sisortNULL, sisortNULL,
sisortcanid, sisortcanid,
sisortrtr, sisortrtr,
sisortframe, sisortframe,
sisortmsgid, sisortmsgid,
sisortEND, sisortEND,
sisortcase = SORTCASE, // case sensitive sisortcase = SORTCASE, // case sensitive
sisortreverse = SORTREVERSE, // reverse order sisortreverse = SORTREVERSE, // reverse order
sisortwild = WILDNULL, // an empty, or null entry equates to wildcard sisortwild = WILDNULL, // an empty, or null entry equates to wildcard
sistopcomparecol = STOPCOMPARE // stop compare after this column if both items to be compared are valid and a compare was possible sistopcomparecol = STOPCOMPARE // stop compare after this column if both items to be compared are valid and a compare was possible
}; };
int msg_id; int msg_id;
bool rtr; bool rtr;
uint32_t can_id; uint32_t can_id;
byte_vector frame; byte_vector frame;
cb_frame(); cb_frame();
cb_frame(int msg_id, uint32_t can_id); cb_frame(int msg_id, uint32_t can_id);
cb_frame(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr); cb_frame(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr);
void clear(); void clear();
void swap(cb_frame &s); void swap(cb_frame &s);
virtual bool is_valid() const; virtual bool is_valid() const;
virtual int compare(const cb_frame& b, const int *comparecolumns) const; virtual int compare(const cb_frame& b, const int *comparecolumns) const;
virtual int compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const; virtual int compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const;
int compare_frame(const byte_vector& _frame) const; int compare_frame(const byte_vector& _frame) const;
void setpublish(bool do_publish = true) const; // we promise only to modify the publish flag void setpublish(bool do_publish = true) const; // we promise only to modify the publish flag
bool getpublish() const; bool getpublish() const;
virtual std::string tag(const std::string& prefix = "", int prefixlen = 18) const; virtual std::string tag(const std::string& prefix = "", int prefixlen = 18) const;
virtual std::string to_string() const; virtual std::string to_string() const;
virtual ~cb_frame() = default; virtual ~cb_frame() = default;
// using default (compiler auto generated) copy and move constructors and assignment operators // using default (compiler auto generated) copy and move constructors and assignment operators
/// Helper functions to convert four scaled values into a byte stream /// Helper functions to convert four scaled values into a byte stream
/// set scale to negative for signed values /// set scale to negative for signed values
static std::vector<uint8_t> get_byte_stream(double value1, int scale1); static std::vector<uint8_t> get_byte_stream(double value1, int scale1);
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2); static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2);
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3); static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3);
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, int scale4); static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, int scale4);
/// Overloaded function to handle double scale for the fourth value /// Overloaded function to handle double scale for the fourth value
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4); static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4);
/// helper function to extract a scaled double value from two bytes /// helper function to extract a scaled double value from two bytes
static double get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed = false); //inv_scale is the reciprocal of scale factor static double get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed = false); //inv_scale is the reciprocal of scale factor
bool send_frame(esphome::canbus::Canbus *canbus, bool extended_id = false, bool remote_transmission_request = false) const; bool send_frame(esphome::canbus::Canbus *canbus, bool extended_id = false, bool remote_transmission_request = false) const;
static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame
}; };
extern const cb_frame emptyframe; // to return a reference to when no valid frame is found extern const cb_frame emptyframe; // to return a reference to when no valid frame is found
} // namespace solar } // namespace solar
#endif // __SOLAR_CB_FRAME #endif // __SOLAR_CB_FRAME

View File

@ -1,93 +1,93 @@
#include "source/solar/cbf_cache.h" #include "source/solar/cbf_cache.h"
namespace solar namespace solar
{ {
static const int comparecolumns[] = {cbf_store::sisortframe, cbf_store::sisortrtr, 0}; static const int comparecolumns[] = {cbf_store::sisortframe, cbf_store::sisortrtr, 0};
void cbf_cache::clear() void cbf_cache::clear()
{ {
cache_map.clear(); cache_map.clear();
} }
int cbf_cache::size() const int cbf_cache::size() const
{ {
return cache_map.size(); return cache_map.size();
} }
bool cbf_cache::hasitem(uint32_t can_id, bool remote_transmission_request) const bool cbf_cache::hasitem(uint32_t can_id, bool remote_transmission_request) const
{ {
return hasitem(canid_rtr_t(can_id, remote_transmission_request)); return hasitem(canid_rtr_t(can_id, remote_transmission_request));
} }
bool cbf_cache::hasitem(canid_rtr_t key) const bool cbf_cache::hasitem(canid_rtr_t key) const
{ {
return cache_map.find(key) != cache_map.end(); return cache_map.find(key) != cache_map.end();
} }
cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request) cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request)
{ {
return cache_map.at(canid_rtr_t(can_id, remote_transmission_request)); return cache_map.at(canid_rtr_t(can_id, remote_transmission_request));
} }
const cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request) const const cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request) const
{ {
return cache_map.at(canid_rtr_t(can_id, remote_transmission_request)); return cache_map.at(canid_rtr_t(can_id, remote_transmission_request));
} }
bool cbf_cache::additem(const cbf_store& storeitem) bool cbf_cache::additem(const cbf_store& storeitem)
{ {
const auto& ret = cache_map.emplace(canid_rtr_t(storeitem.can_id, storeitem.rtr), cbf_cache_item(storeitem)); const auto& ret = cache_map.emplace(canid_rtr_t(storeitem.can_id, storeitem.rtr), cbf_cache_item(storeitem));
if(ret.second) { if(ret.second) {
return false; // new item inserted, no publish return false; // new item inserted, no publish
} }
auto& kvp = *ret.first; auto& kvp = *ret.first;
auto& item = kvp.second; auto& item = kvp.second;
auto updateresult = item.update(storeitem, comparecolumns, 1); auto updateresult = item.update(storeitem, comparecolumns, 1);
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) { if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
// ESP_LOGI(item.store0.tag("== ST1 DUP == ").c_str(), item.store0.to_string().c_str()); // ESP_LOGI(item.store0.tag("== ST1 DUP == ").c_str(), item.store0.to_string().c_str());
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH; return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
} }
// try next store to see if it has a duplicate of new item // try next store to see if it has a duplicate of new item
updateresult = item.update(storeitem, comparecolumns, 2); updateresult = item.update(storeitem, comparecolumns, 2);
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) { if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
// ESP_LOGI(item.store1.tag("== ST2 DUP == ").c_str(), item.store1.to_string().c_str()); // ESP_LOGI(item.store1.tag("== ST2 DUP == ").c_str(), item.store1.to_string().c_str());
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH; return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
} }
item.update(storeitem); item.update(storeitem);
return false; return false;
} }
const cb_frame& cbf_cache::get_frame(uint32_t can_id, bool remote_transmission_request) const const cb_frame& cbf_cache::get_frame(uint32_t can_id, bool remote_transmission_request) const
{ {
return get_frame(canid_rtr_t(can_id, remote_transmission_request)); return get_frame(canid_rtr_t(can_id, remote_transmission_request));
} }
const cb_frame& cbf_cache::get_frame(canid_rtr_t key) const const cb_frame& cbf_cache::get_frame(canid_rtr_t key) const
{ {
auto it = cache_map.find(key); auto it = cache_map.find(key);
if(it == cache_map.end()) if(it == cache_map.end())
return emptyframe; // return reference to static empty frame if not found return emptyframe; // return reference to static empty frame if not found
const auto& item = it->second; const auto& item = it->second;
return item.get_frame(); return item.get_frame();
} }
bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id, bool remote_transmission_request) bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id, bool remote_transmission_request)
{ {
auto key = canid_rtr_t(can_id, remote_transmission_request); auto key = canid_rtr_t(can_id, remote_transmission_request);
if(!this->hasitem(key)) if(!this->hasitem(key))
return false; return false;
const auto& cbf = get_frame(key); const auto& cbf = get_frame(key);
if(cbf.getpublish()) { if(cbf.getpublish()) {
return cbf.send_frame(canbus, extended_id, remote_transmission_request); return cbf.send_frame(canbus, extended_id, remote_transmission_request);
} }
return false; return false;
for(const auto& kvp : cache_map) { for(const auto& kvp : cache_map) {
const auto& item = kvp.second; const auto& item = kvp.second;
const auto& cbf = item.get_frame(); const auto& cbf = item.get_frame();
if(cbf.getpublish()) { if(cbf.getpublish()) {
cbf.send_frame(canbus, extended_id, remote_transmission_request); cbf.send_frame(canbus, extended_id, remote_transmission_request);
} }
} }
} }
// static version to send arbitrary frame // static version to send arbitrary frame
bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id, bool remote_transmission_request) bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id, bool remote_transmission_request)
{ {
return cb_frame::send_frame(canbus, can_id, frame, extended_id, remote_transmission_request); return cb_frame::send_frame(canbus, can_id, frame, extended_id, remote_transmission_request);
} }
bool cbf_cache::send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id) bool cbf_cache::send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id)
{ {
return cb_frame::send_frame(canbus, can_id, {}, extended_id, true); return cb_frame::send_frame(canbus, can_id, {}, extended_id, true);
} }
} // namespace solar } // namespace solar

View File

@ -1,39 +1,39 @@
// NB! A lot of comments in .h and .cpp files were auto generated by CoPilot. Applicable comments have been retained, others removed. // NB! A lot of comments in .h and .cpp files were auto generated by CoPilot. Applicable comments have been retained, others removed.
#ifndef __SOLAR_CBF_CACHE #ifndef __SOLAR_CBF_CACHE
#define __SOLAR_CBF_CACHE #define __SOLAR_CBF_CACHE
#include <utility> #include <utility>
#include <map> #include <map>
#include "esphome.h" #include "esphome.h"
#include "cbf_store_pylon.h" #include "cbf_store_pylon.h"
#include "cbf_cache_item.h" #include "cbf_cache_item.h"
using namespace esphome; using namespace esphome;
namespace solar namespace solar
{ {
class cbf_cache { class cbf_cache {
public: public:
typedef std::pair<uint32_t, bool> canid_rtr_t; // pair of CAN ID and RTR flag typedef std::pair<uint32_t, bool> canid_rtr_t; // pair of CAN ID and RTR flag
std::map<canid_rtr_t, cbf_cache_item> cache_map; // map of rtr/CAN IDs to cache items std::map<canid_rtr_t, cbf_cache_item> cache_map; // map of rtr/CAN IDs to cache items
cbf_cache() = default; cbf_cache() = default;
void clear(); void clear();
int size() const; int size() const;
bool hasitem(canid_rtr_t key) const; bool hasitem(canid_rtr_t key) const;
bool hasitem(uint32_t can_id, bool remote_transmission_request) const; bool hasitem(uint32_t can_id, bool remote_transmission_request) const;
cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request); cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request);
const cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request) const; const cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request) const;
// Add a new item to the cache or update an existing one // Add a new item to the cache or update an existing one
bool additem(const cbf_store& item); bool additem(const cbf_store& item);
const cb_frame& get_frame(canid_rtr_t key) const; const cb_frame& get_frame(canid_rtr_t key) const;
const cb_frame& get_frame(uint32_t can_id, bool remote_transmission_request) const; const cb_frame& get_frame(uint32_t can_id, bool remote_transmission_request) const;
virtual ~cbf_cache() = default; // virtual destructor for base class virtual ~cbf_cache() = default; // virtual destructor for base class
bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false, bool remote_transmission_request = false); bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false, bool remote_transmission_request = false);
static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame
static bool send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false); // static version to send remote transmission request frame static bool send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false); // static version to send remote transmission request frame
// using default (compiler auto generated) copy and move constructors and assignment operators // using default (compiler auto generated) copy and move constructors and assignment operators
}; };
} // namespace solar } // namespace solar
#endif // __SOLAR_CBF_CACHE #endif // __SOLAR_CBF_CACHE

View File

@ -1,117 +1,117 @@
// if filename is changes, remove old .o file from \\TRUENAS\esphome\config\.esphome\build\sthome-ut8\.pioenvs\sthome-ut8\src\source\solar // if filename is changes, remove old .o file from \\TRUENAS\esphome\config\.esphome\build\sthome-ut8\.pioenvs\sthome-ut8\src\source\solar
#include "source/solar/cbf_cache_item.h" #include "source/solar/cbf_cache_item.h"
namespace solar namespace solar
{ {
cbf_cache_item::cbf_cache_item() cbf_cache_item::cbf_cache_item()
{ {
this->store0 = std::make_shared<cbf_store>(0, 0, 1, byte_vector(), false, 0, 0); // make generic empty store this->store0 = std::make_shared<cbf_store>(0, 0, 1, byte_vector(), false, 0, 0); // make generic empty store
this->store1 = std::make_shared<cbf_store>(0, 0, 2, byte_vector(), false, 0, 0); // make generic empty store this->store1 = std::make_shared<cbf_store>(0, 0, 2, byte_vector(), false, 0, 0); // make generic empty store
ESP_LOGI( this->store0->tag("cache_item CTOR0A ").c_str(), "%-20s %s", "Created cache item", this->store0->to_string().c_str()); ESP_LOGI( this->store0->tag("cache_item CTOR0A ").c_str(), "%-20s %s", "Created cache item", this->store0->to_string().c_str());
ESP_LOGI( this->store1->tag("cache_item CTOR0B ").c_str(), "%-20s %s", "Created cache item", this->store1->to_string().c_str()); ESP_LOGI( this->store1->tag("cache_item CTOR0B ").c_str(), "%-20s %s", "Created cache item", this->store1->to_string().c_str());
} }
cbf_cache_item::cbf_cache_item(const cbf_store& store0) cbf_cache_item::cbf_cache_item(const cbf_store& store0)
{ {
this->store0 = store0.clone(); this->store0 = store0.clone();
this->store1 = store0.clone(); this->store1 = store0.clone();
this->store0->id = 1; this->store0->id = 1;
this->store1->id = 2; this->store1->id = 2;
} }
void cbf_cache_item::clear() void cbf_cache_item::clear()
{ {
store0 = nullptr; store0 = nullptr;
store1 = nullptr; store1 = nullptr;
} }
void cbf_cache_item::swap(cbf_cache_item &s) void cbf_cache_item::swap(cbf_cache_item &s)
{ {
std::swap(this->store0, s.store0); std::swap(this->store0, s.store0);
std::swap(this->store1, s.store1); std::swap(this->store1, s.store1);
} }
bool cbf_cache_item::update(const cbf_store& newitem) bool cbf_cache_item::update(const cbf_store& newitem)
{ {
if(!this->store0->is_valid()) { if(!this->store0->is_valid()) {
store0 = newitem.clone(); store0 = newitem.clone();
store0->id = 1; store0->id = 1;
return true; return true;
} }
if(!this->store1->is_valid()) { if(!this->store1->is_valid()) {
store1 = newitem.clone(); store1 = newitem.clone();
store1->id = 2; store1->id = 2;
return true; return true;
} }
bool result = store1->last_timestamp > store0->last_timestamp; bool result = store1->last_timestamp > store0->last_timestamp;
if(result) { if(result) {
store0 = newitem.clone(); store0 = newitem.clone();
this->store0->id = 1; this->store0->id = 1;
} }
else { else {
store1 = newitem.clone(); store1 = newitem.clone();
this->store1->id = 2; this->store1->id = 2;
} }
return result; return result;
} }
cbf_store::cbf_updateresult cbf_cache_item::update(const cbf_store& newitem, const int *comparecolumns, int store_selector) cbf_store::cbf_updateresult cbf_cache_item::update(const cbf_store& newitem, const int *comparecolumns, int store_selector)
{ {
cbf_store::cbf_updateresult result = cbf_store::cbf_updateresult::stu_NONE; cbf_store::cbf_updateresult result = cbf_store::cbf_updateresult::stu_NONE;
switch(store_selector) { switch(store_selector) {
case 2: case 2:
result = this->store1->update(newitem, comparecolumns); result = this->store1->update(newitem, comparecolumns);
break; break;
default: default:
result = this->store0->update(newitem, comparecolumns); result = this->store0->update(newitem, comparecolumns);
break; break;
} }
return result; return result;
} }
const cb_frame& cbf_cache_item::get_frame() const const cb_frame& cbf_cache_item::get_frame() const
{ {
return get_store(); // return most recent valid store's frame return get_store(); // return most recent valid store's frame
} }
const cbf_store& cbf_cache_item::get_store() const const cbf_store& cbf_cache_item::get_store() const
{ {
if(this->store0->is_valid() && !this->store1->is_valid()) { if(this->store0->is_valid() && !this->store1->is_valid()) {
return *store0; return *store0;
} }
if(!this->store0->is_valid() && this->store1->is_valid()) { if(!this->store0->is_valid() && this->store1->is_valid()) {
return *store1; return *store1;
} }
if(this->store0->is_valid() && this->store1->is_valid()) { if(this->store0->is_valid() && this->store1->is_valid()) {
if(this->store0->last_timestamp >= this->store1->last_timestamp) { if(this->store0->last_timestamp >= this->store1->last_timestamp) {
return *this->store0; return *this->store0;
} else { } else {
return *this->store1; return *this->store1;
} }
} }
return *this->store0; // no valid stores, but return store0 which is always present return *this->store0; // no valid stores, but return store0 which is always present
} }
std::string cbf_cache_item::tag(const std::string& prefix) const std::string cbf_cache_item::tag(const std::string& prefix) const
{ {
return get_store().tag(prefix, prefix.length()); return get_store().tag(prefix, prefix.length());
} }
std::string cbf_cache_item::to_string() const std::string cbf_cache_item::to_string() const
{ {
return get_store().to_string(); return get_store().to_string();
} }
std::string cbf_cache_item::tag0(const std::string& prefix) const std::string cbf_cache_item::tag0(const std::string& prefix) const
{ {
return store0->tag(prefix); return store0->tag(prefix);
} }
std::string cbf_cache_item::tag1(const std::string& prefix) const std::string cbf_cache_item::tag1(const std::string& prefix) const
{ {
return store1->tag(prefix); return store1->tag(prefix);
} }
std::string cbf_cache_item::st0_tostring() const std::string cbf_cache_item::st0_tostring() const
{ {
char buffer[150]; char buffer[150];
snprintf(buffer, sizeof(buffer), "ST1: [%s] %s", trim(tag0()).c_str(), store0->to_string().c_str()); snprintf(buffer, sizeof(buffer), "ST1: [%s] %s", trim(tag0()).c_str(), store0->to_string().c_str());
return std::string(buffer); return std::string(buffer);
} }
std::string cbf_cache_item::st1_tostring() const std::string cbf_cache_item::st1_tostring() const
{ {
char buffer[150]; char buffer[150];
snprintf(buffer, sizeof(buffer), "ST2: [%s] %s", trim(tag1()).c_str(), store1->to_string().c_str()); snprintf(buffer, sizeof(buffer), "ST2: [%s] %s", trim(tag1()).c_str(), store1->to_string().c_str());
return std::string(buffer); return std::string(buffer);
} }
} // namespace solar } // namespace solar

View File

@ -1,42 +1,42 @@
#ifndef __SOLAR_CBF_CACHE_ITEM #ifndef __SOLAR_CBF_CACHE_ITEM
#define __SOLAR_CBF_CACHE_ITEM #define __SOLAR_CBF_CACHE_ITEM
#include <utility> #include <utility>
#include <map> #include <map>
#include "esphome.h" #include "esphome.h"
#include "cbf_store_pylon.h" #include "cbf_store_pylon.h"
using namespace esphome; using namespace esphome;
namespace solar namespace solar
{ {
class cbf_cache_item { class cbf_cache_item {
private: private:
const int storecount = 2; const int storecount = 2;
public: public:
std::shared_ptr<cbf_store> store0; std::shared_ptr<cbf_store> store0;
std::shared_ptr<cbf_store> store1; std::shared_ptr<cbf_store> store1;
cbf_cache_item(); // to allow object to be used as a value element in map operator[] method cbf_cache_item(); // to allow object to be used as a value element in map operator[] method
cbf_cache_item(const cbf_store& store0); cbf_cache_item(const cbf_store& store0);
void clear(); void clear();
void swap(cbf_cache_item &s); void swap(cbf_cache_item &s);
bool update(const cbf_store& newitem); bool update(const cbf_store& newitem);
cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns, int store_selector); cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns, int store_selector);
const cb_frame& get_frame() const; // returns the most recent valid frame const cb_frame& get_frame() const; // returns the most recent valid frame
const cbf_store& get_store() const; // returns the most recent valid store const cbf_store& get_store() const; // returns the most recent valid store
virtual std::string tag0(const std::string& prefix = "") const; virtual std::string tag0(const std::string& prefix = "") const;
virtual std::string tag1(const std::string& prefix = "") const; virtual std::string tag1(const std::string& prefix = "") const;
virtual std::string tag(const std::string& prefix = "") const; virtual std::string tag(const std::string& prefix = "") const;
virtual std::string to_string() const; virtual std::string to_string() const;
virtual std::string st0_tostring() const; // string of store 0 virtual std::string st0_tostring() const; // string of store 0
virtual std::string st1_tostring() const; // string of store 1 virtual std::string st1_tostring() const; // string of store 1
// the default copy and move constructors and assignment operators are fine // the default copy and move constructors and assignment operators are fine
// because shared_ptr takes care of the underlying memory management // because shared_ptr takes care of the underlying memory management
cbf_cache_item(const cbf_cache_item& b) = default; cbf_cache_item(const cbf_cache_item& b) = default;
cbf_cache_item& operator=(const cbf_cache_item& b) = default; cbf_cache_item& operator=(const cbf_cache_item& b) = default;
cbf_cache_item(cbf_cache_item&& src) = default; cbf_cache_item(cbf_cache_item&& src) = default;
cbf_cache_item& operator=(cbf_cache_item&& src) = default; cbf_cache_item& operator=(cbf_cache_item&& src) = default;
virtual ~cbf_cache_item() = default; // virtual destructor for base class virtual ~cbf_cache_item() = default; // virtual destructor for base class
}; };
} // namespace solar } // namespace solar
#endif // __SOLAR_CBF_CACHE #endif // __SOLAR_CBF_CACHE

View File

@ -1,158 +1,158 @@
#include "cbf_pylon.h" #include "cbf_pylon.h"
namespace solar namespace solar
{ {
// see common.h for definition of publish_spec_t // see common.h for definition of publish_spec_t
// publish_spec_t(int on_count, int interval, int timeout) // publish_spec_t(int on_count, int interval, int timeout)
const publish_spec_t cbf_pylon::publish_spec = publish_spec_t(3, 15, 30); // default publish spec for Pylontech battery messages const publish_spec_t cbf_pylon::publish_spec = publish_spec_t(3, 15, 30); // default publish spec for Pylontech battery messages
const publish_spec_t cbf_pylon::rtr_publish_spec = publish_spec_t(2, 10, 15); // for remote transmission requests const publish_spec_t cbf_pylon::rtr_publish_spec = publish_spec_t(2, 10, 15); // for remote transmission requests
cbf_pylon& cbf_pylon::operator=(cbf_pylon&& src) cbf_pylon& cbf_pylon::operator=(cbf_pylon&& src)
{ {
if (this != &src) { if (this != &src) {
cb_frame::operator=(std::move(src)); cb_frame::operator=(std::move(src));
src.clear(); src.clear();
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str()); //ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
} }
return *this; return *this;
} }
void cbf_pylon::clear() void cbf_pylon::clear()
{ {
cb_frame::clear(); cb_frame::clear();
} }
// float cbf_pylon::_get_battery_charge_voltage_limit() const // float cbf_pylon::_get_battery_charge_voltage_limit() const
// { // {
// const auto& x = this->frame; // const auto& x = this->frame;
// return 0.1 * (float)((x[1] << 8) + x[0]); // return 0.1 * (float)((x[1] << 8) + x[0]);
// } // }
// Function to build a message from the pylon canbus frame // Function to build a message from the pylon canbus frame
std::string cbf_pylon::to_string() const std::string cbf_pylon::to_string() const
{ {
char buffer[80]; char buffer[80];
switch (can_id) switch (can_id)
{ {
case CB_BATTERY_LIMITS: case CB_BATTERY_LIMITS:
{ {
if(rtr) { if(rtr) {
return "Request for BATTERY LIMITS info"; return "Request for BATTERY LIMITS info";
} }
if (this->frame.size() < 6) { if (this->frame.size() < 6) {
return "Invalid frame size for CB_BATTERY_LIMITS"; return "Invalid frame size for CB_BATTERY_LIMITS";
} }
const auto& x = this->frame; const auto& x = this->frame;
float battery_charge_voltage_limit = 0.1 * ((x[1] << 8) + x[0]); // unit = 0.1V float battery_charge_voltage_limit = 0.1 * ((x[1] << 8) + x[0]); // unit = 0.1V
float charge_current_limit = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A float charge_current_limit = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A
float discharge_current_limit = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1A float discharge_current_limit = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1A
snprintf(buffer, sizeof(buffer), "BATTERY MAX CHARGE: VMax= %.1fV IMaxChg= %.1fA IMaxDis= %.1fA", battery_charge_voltage_limit, charge_current_limit, discharge_current_limit); snprintf(buffer, sizeof(buffer), "BATTERY MAX CHARGE: VMax= %.1fV IMaxChg= %.1fA IMaxDis= %.1fA", battery_charge_voltage_limit, charge_current_limit, discharge_current_limit);
return buffer; return buffer;
} }
break; break;
case CB_BATTERY_STATE: case CB_BATTERY_STATE:
{ {
if(rtr) { if(rtr) {
return "Request for BATTERY STATE info"; return "Request for BATTERY STATE info";
} }
if (this->frame.size() < 4) { if (this->frame.size() < 4) {
return "Invalid frame size for CB_BATTERY_STATE"; return "Invalid frame size for CB_BATTERY_STATE";
} }
const auto& x = this->frame; const auto& x = this->frame;
uint soc = static_cast<uint16_t>((x[1] << 8) + x[0]); uint soc = static_cast<uint16_t>((x[1] << 8) + x[0]);
uint soh = static_cast<uint16_t>((x[3] << 8) + x[2]); uint soh = static_cast<uint16_t>((x[3] << 8) + x[2]);
snprintf(buffer, sizeof(buffer), "BATTERY STATE: SOC= %d%% SOH= %d%%", soc, soh); snprintf(buffer, sizeof(buffer), "BATTERY STATE: SOC= %d%% SOH= %d%%", soc, soh);
return buffer; return buffer;
} }
break; break;
case CB_BATTERY_STATUS: case CB_BATTERY_STATUS:
{ {
if(rtr) { if(rtr) {
return "Request for BATTERY STATUS info"; return "Request for BATTERY STATUS info";
} }
if (this->frame.size() < 6) { if (this->frame.size() < 6) {
return "Invalid frame size for CB_BATTERY_STATUS"; return "Invalid frame size for CB_BATTERY_STATUS";
} }
const auto& x = this->frame; const auto& x = this->frame;
float system_voltage = 0.01 * static_cast<int16_t>((x[1] << 8) + x[0]); // unit = 0.01V Voltage of single module or average module voltage of system float system_voltage = 0.01 * static_cast<int16_t>((x[1] << 8) + x[0]); // unit = 0.01V Voltage of single module or average module voltage of system
float system_current = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A Module or system total current float system_current = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A Module or system total current
float average_cell_temperature = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1°C float average_cell_temperature = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1°C
snprintf(buffer, sizeof(buffer), "BATTERY STATUS: VSYS= %.2fV ISYS= %.1fA TSYS= %.1f°C", system_voltage, system_current, average_cell_temperature); snprintf(buffer, sizeof(buffer), "BATTERY STATUS: VSYS= %.2fV ISYS= %.1fA TSYS= %.1f°C", system_voltage, system_current, average_cell_temperature);
return buffer; return buffer;
} }
break; break;
case CB_BATTERY_FAULT: case CB_BATTERY_FAULT:
{ {
if(rtr) { if(rtr) {
return "Request for BATTERY FAULT info"; return "Request for BATTERY FAULT info";
} }
if (this->frame.size() < 8) { if (this->frame.size() < 8) {
return "Invalid frame size for CB_BATTERY_FAULT"; return "Invalid frame size for CB_BATTERY_FAULT";
} }
const auto& x = this->frame; const auto& x = this->frame;
uint8_t protection1 = x[0]; uint8_t protection1 = x[0];
uint8_t protection2 = x[1]; uint8_t protection2 = x[1];
uint8_t alarm1 = x[2]; uint8_t alarm1 = x[2];
uint8_t alarm2 = x[3]; uint8_t alarm2 = x[3];
uint8_t module_numbers = x[4]; uint8_t module_numbers = x[4];
char ch5 = x[5]; char ch5 = x[5];
char ch6 = x[6]; char ch6 = x[6];
bool discharge_over_current = protection1 & 0x80; bool discharge_over_current = protection1 & 0x80;
bool cell_under_temperature = protection1 & 0x10; bool cell_under_temperature = protection1 & 0x10;
bool cell_over_temperature = protection1 & 0x08; bool cell_over_temperature = protection1 & 0x08;
bool cell_or_module_under_voltage = protection1 & 0x04; bool cell_or_module_under_voltage = protection1 & 0x04;
bool cell_or_module_over_voltage = protection1 & 0x02; bool cell_or_module_over_voltage = protection1 & 0x02;
bool system_error = protection2 & 0x8; bool system_error = protection2 & 0x8;
bool charge_over_current = protection2 & 0x01; bool charge_over_current = protection2 & 0x01;
bool discharge_high_current = alarm1 & 0x80; bool discharge_high_current = alarm1 & 0x80;
bool cell_low_temperature = alarm1 & 0x10; bool cell_low_temperature = alarm1 & 0x10;
bool cell_high_temperature = alarm1 & 0x08; bool cell_high_temperature = alarm1 & 0x08;
bool cell_or_module_low_voltage = alarm1 & 0x04; bool cell_or_module_low_voltage = alarm1 & 0x04;
bool cell_or_module_high_voltage = alarm1 & 0x02; bool cell_or_module_high_voltage = alarm1 & 0x02;
bool internal_communication_fail = alarm2 & 0x8; bool internal_communication_fail = alarm2 & 0x8;
bool charge_high_current = alarm2 & 0x01; bool charge_high_current = alarm2 & 0x01;
snprintf(buffer, sizeof(buffer), "BATTERY PROTECT: %s%s%s%s%s%s%s ALARM= %s%s%s%s%s%s%s MN=%d %c%c", discharge_over_current ? "DOC " : "", cell_under_temperature ? "CUT " : "", cell_over_temperature ? "COT " : "", cell_or_module_under_voltage ? "CMUV " : "", cell_or_module_over_voltage ? "CMOV" : "", system_error ? "SERR " : "", charge_over_current ? "COC ": "", discharge_high_current ? "DHC " : "", cell_low_temperature ? "CLT " : "", cell_high_temperature ? "CHT " : "", cell_or_module_low_voltage ? "CMLV " : "", cell_or_module_high_voltage ? "CMHV" : "", internal_communication_fail ? "ICF " : "", charge_high_current ? "CHC ": "", module_numbers, ch5, ch6); snprintf(buffer, sizeof(buffer), "BATTERY PROTECT: %s%s%s%s%s%s%s ALARM= %s%s%s%s%s%s%s MN=%d %c%c", discharge_over_current ? "DOC " : "", cell_under_temperature ? "CUT " : "", cell_over_temperature ? "COT " : "", cell_or_module_under_voltage ? "CMUV " : "", cell_or_module_over_voltage ? "CMOV" : "", system_error ? "SERR " : "", charge_over_current ? "COC ": "", discharge_high_current ? "DHC " : "", cell_low_temperature ? "CLT " : "", cell_high_temperature ? "CHT " : "", cell_or_module_low_voltage ? "CMLV " : "", cell_or_module_high_voltage ? "CMHV" : "", internal_communication_fail ? "ICF " : "", charge_high_current ? "CHC ": "", module_numbers, ch5, ch6);
return buffer; return buffer;
} }
break; break;
case CB_BATTERY_REQUEST_FLAGS: case CB_BATTERY_REQUEST_FLAGS:
{ {
if(rtr) { if(rtr) {
return "Request for BATTERY REQUEST FLAGS info"; return "Request for BATTERY REQUEST FLAGS info";
} }
if (this->frame.size() < 1) { if (this->frame.size() < 1) {
return "Invalid frame size for CB_BATTERY_REQUEST_FLAGS"; return "Invalid frame size for CB_BATTERY_REQUEST_FLAGS";
} }
const auto& x = this->frame; const auto& x = this->frame;
uint8_t request_flag = x[0]; uint8_t request_flag = x[0];
bool charge_enable = request_flag & 0x80; bool charge_enable = request_flag & 0x80;
bool discharge_enable = request_flag & 0x40; bool discharge_enable = request_flag & 0x40;
bool request_force_charge1 = request_flag & 0x20; // use bit 5, the SOC range is: 15~19%. Bit 4 is NULL. Bit 5 is designed for inverter allows battery to shut down, and able to wake battery up to charge it. bool request_force_charge1 = request_flag & 0x20; // use bit 5, the SOC range is: 15~19%. Bit 4 is NULL. Bit 5 is designed for inverter allows battery to shut down, and able to wake battery up to charge it.
bool request_force_charge2 = request_flag & 0x10; // Bit 5 the SOC range is 5~10%, Bit 4 the SOC range is 9~13%. Bit 4 is designed for inverter doesn`t want battery to shut down, able to charge battery before shut down to avoid low energy. We suggest inverter to use this bit, In this case, inverter itself should set a threshold of SOC: after force charge, only when battery SOC is higher than this threshold then inverter will allow discharge, to avoid force charge and discharge status change frequently. bool request_force_charge2 = request_flag & 0x10; // Bit 5 the SOC range is 5~10%, Bit 4 the SOC range is 9~13%. Bit 4 is designed for inverter doesn`t want battery to shut down, able to charge battery before shut down to avoid low energy. We suggest inverter to use this bit, In this case, inverter itself should set a threshold of SOC: after force charge, only when battery SOC is higher than this threshold then inverter will allow discharge, to avoid force charge and discharge status change frequently.
bool request_full_charge = request_flag & 0x08; // Reason: when battery is not full charged for long time, the accumulative error of SOC calculation will be too high and may not able to be charged or discharged as expected capacity. Logic: if SOC never higher than 97% in 30 days, will set this flag to 1. And when the SOC is 97%, the flag will be 0. How to: we suggest inverter to charge the battery by grid when this flag is 1. bool request_full_charge = request_flag & 0x08; // Reason: when battery is not full charged for long time, the accumulative error of SOC calculation will be too high and may not able to be charged or discharged as expected capacity. Logic: if SOC never higher than 97% in 30 days, will set this flag to 1. And when the SOC is 97%, the flag will be 0. How to: we suggest inverter to charge the battery by grid when this flag is 1.
snprintf(buffer, sizeof(buffer), "BATTERY REQUEST: %s%s%s%s%s", charge_enable ? "CE " : "", discharge_enable ? "DE " : "", request_force_charge1 ? "RFORCECH1 " : "", request_force_charge2 ? "RFORCECH2 " : "", request_full_charge ? "RFULLCH" : ""); snprintf(buffer, sizeof(buffer), "BATTERY REQUEST: %s%s%s%s%s", charge_enable ? "CE " : "", discharge_enable ? "DE " : "", request_force_charge1 ? "RFORCECH1 " : "", request_force_charge2 ? "RFORCECH2 " : "", request_full_charge ? "RFULLCH" : "");
return buffer; return buffer;
} }
break; break;
case CB_BATTERY_MANUFACTURER: case CB_BATTERY_MANUFACTURER:
{ {
if(rtr) { if(rtr) {
return "Request for BATTERY MANUFACTURER info"; return "Request for BATTERY MANUFACTURER info";
} }
if (this->frame.size() < 8) { if (this->frame.size() < 8) {
return "Invalid frame size for CB_BATTERY_MANUFACTURER"; return "Invalid frame size for CB_BATTERY_MANUFACTURER";
} }
const auto& x = this->frame; const auto& x = this->frame;
// Manufacturer name is in the first 8 bytes, padded with spaces // Manufacturer name is in the first 8 bytes, padded with spaces
// Convert to string and trim trailing spaces // Convert to string and trim trailing spaces
std::string manufacturer(reinterpret_cast<const char*>(x.data()), 8); std::string manufacturer(reinterpret_cast<const char*>(x.data()), 8);
manufacturer.erase(std::find_if(manufacturer.rbegin(), manufacturer.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), manufacturer.end()); manufacturer.erase(std::find_if(manufacturer.rbegin(), manufacturer.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), manufacturer.end());
return "BATTERY OEM: " + manufacturer; return "BATTERY OEM: " + manufacturer;
} }
break; break;
} }
if(rtr) { if(rtr) {
return "Request for unknown pylon CAN ID"; return "Request for unknown pylon CAN ID";
} }
return "Unknown CAN ID"; return "Unknown CAN ID";
} }
} // namespace solar } // namespace solar

View File

@ -1,40 +1,40 @@
#ifndef __SOLAR_CBF_PYLON // include GUARD #ifndef __SOLAR_CBF_PYLON // include GUARD
#define __SOLAR_CBF_PYLON #define __SOLAR_CBF_PYLON
#include "esphome.h" #include "esphome.h"
#include <utility> #include <utility>
#include "common.h" #include "common.h"
#include "cb_frame.h" #include "cb_frame.h"
using namespace esphome; using namespace esphome;
namespace solar namespace solar
{ {
class cbf_pylon : virtual public cb_frame { class cbf_pylon : virtual public cb_frame {
public: public:
// Pylontech publish spec // Pylontech publish spec
// This is used to determine when to publish the battery data from the Pylontech battery // This is used to determine when to publish the battery data from the Pylontech battery
static const publish_spec_t publish_spec; // default publish spec for Pylontech battery messages static const publish_spec_t publish_spec; // default publish spec for Pylontech battery messages
static const publish_spec_t rtr_publish_spec; // for remote transmission requests static const publish_spec_t rtr_publish_spec; // for remote transmission requests
// Pylontech battery CAN IDs // Pylontech battery CAN IDs
// https://domosimple.eu/en/documentation/manuels/pylontech-can-bus-protocole // https://domosimple.eu/en/documentation/manuels/pylontech-can-bus-protocole
// VERIFIED IDs have been confirmed by capturing actual CAN bus traffic from a Pylontech battery // VERIFIED IDs have been confirmed by capturing actual CAN bus traffic from a Pylontech battery
static const int CB_BATTERY_LIMITS = 0x351; // VERIFIED: Pylontech battery max charging/discharging values message static const int CB_BATTERY_LIMITS = 0x351; // VERIFIED: Pylontech battery max charging/discharging values message
static const int CB_BATTERY_STATE = 0x355; // VERIFIED: Pylontech battery state message static const int CB_BATTERY_STATE = 0x355; // VERIFIED: Pylontech battery state message
static const int CB_BATTERY_STATUS = 0x356; // VERIFIED: Pylontech battery status message static const int CB_BATTERY_STATUS = 0x356; // VERIFIED: Pylontech battery status message
static const int CB_BATTERY_FAULT = 0x359; // VERIFIED: Pylontech battery fault message static const int CB_BATTERY_FAULT = 0x359; // VERIFIED: Pylontech battery fault message
static const int CB_BATTERY_REQUEST_FLAGS = 0x35C; // VERIFIED: Pylontech battery request flag message static const int CB_BATTERY_REQUEST_FLAGS = 0x35C; // VERIFIED: Pylontech battery request flag message
static const int CB_BATTERY_MANUFACTURER = 0x35E; // VERIFIED: Pylontech battery manufacturer message static const int CB_BATTERY_MANUFACTURER = 0x35E; // VERIFIED: Pylontech battery manufacturer message
//Property<int, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_set_battery_charge_voltage_limit, &cbf_pylon::_get_battery_charge_voltage_limit }; //Property<int, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_set_battery_charge_voltage_limit, &cbf_pylon::_get_battery_charge_voltage_limit };
//Property<float, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_get_battery_charge_voltage_limit}; //Property<float, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_get_battery_charge_voltage_limit};
cbf_pylon() = default; cbf_pylon() = default;
cbf_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr); cbf_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
void clear(); void clear();
virtual std::string to_string() const override; virtual std::string to_string() const override;
cbf_pylon& operator=(cbf_pylon&& src); // required due to double inheritance in cbf_store_pylon cbf_pylon& operator=(cbf_pylon&& src); // required due to double inheritance in cbf_store_pylon
// using default (compiler auto generated) copy and move constructors and assignment operators // using default (compiler auto generated) copy and move constructors and assignment operators
}; };
} // namespace solar } // namespace solar
#endif // __SOLAR_CBF_PYLON #endif // __SOLAR_CBF_PYLON

View File

@ -1,342 +1,342 @@
#include "cbf_sthome.h" #include "cbf_sthome.h"
namespace solar namespace solar
{ {
// see common.h for definition of publish_spec_t // see common.h for definition of publish_spec_t
// publish_spec_t(int on_count, int interval, int timeout) // publish_spec_t(int on_count, int interval, int timeout)
const publish_spec_t cbf_sthome::publish_spec = publish_spec_t(3, 15, 30); // default publish spec const publish_spec_t cbf_sthome::publish_spec = publish_spec_t(3, 15, 30); // default publish spec
const publish_spec_t cbf_sthome::rtr_publish_spec = publish_spec_t(2, 10, 40); // for remote transmission requests const publish_spec_t cbf_sthome::rtr_publish_spec = publish_spec_t(2, 10, 40); // for remote transmission requests
const std::string cbf_sthome::heartbeat = "HELLO\0\0\0"; // must be exactly 8 bytes including null terminators const std::string cbf_sthome::heartbeat = "HELLO\0\0\0"; // must be exactly 8 bytes including null terminators
//cbf_sthome::cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr) //cbf_sthome::cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr)
// : cb_frame(msg_id, can_id, frame, rtr) // : cb_frame(msg_id, can_id, frame, rtr)
// { // {
// } // }
cbf_sthome& cbf_sthome::operator=(cbf_sthome&& src) cbf_sthome& cbf_sthome::operator=(cbf_sthome&& src)
{ {
if (this != &src) { if (this != &src) {
cb_frame::operator=(std::move(src)); cb_frame::operator=(std::move(src));
src.clear(); src.clear();
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str()); //ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
} }
return *this; return *this;
} }
void cbf_sthome::clear() void cbf_sthome::clear()
{ {
cb_frame::clear(); cb_frame::clear();
} }
bool cbf_sthome::verify_heartbeat() const bool cbf_sthome::verify_heartbeat() const
{ {
const auto& x = this->frame; const auto& x = this->frame;
std::string hb(reinterpret_cast<const char*>(x.data()), x.size()); std::string hb(reinterpret_cast<const char*>(x.data()), x.size());
hb.erase(std::find_if(hb.rbegin(), hb.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), hb.end()); hb.erase(std::find_if(hb.rbegin(), hb.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), hb.end());
return hb == heartbeat; return hb == heartbeat;
} }
// float cbf_sthome::_get_battery_charge_voltage_limit() const // float cbf_sthome::_get_battery_charge_voltage_limit() const
// { // {
// const auto& x = this->frame; // const auto& x = this->frame;
// return 0.1 * (float)((x[1] << 8) + x[0]); // return 0.1 * (float)((x[1] << 8) + x[0]);
// } // }
// Function to build a message from the pylon canbus frame // Function to build a message from the pylon canbus frame
std::string cbf_sthome::to_string() const std::string cbf_sthome::to_string() const
{ {
char buffer[128]; char buffer[128];
const auto& x = this->frame; const auto& x = this->frame;
switch (can_id) switch (can_id)
{ {
case CB_CANBUS_ID01: case CB_CANBUS_ID01:
return rtr ? "Request for sthome-ut1 heartbeat" : verify_heartbeat() ? "sthome-ut1 alive" : ""; return rtr ? "Request for sthome-ut1 heartbeat" : verify_heartbeat() ? "sthome-ut1 alive" : "";
case CB_CANBUS_ID02: case CB_CANBUS_ID02:
return rtr ? "Request for sthome-ut2 heartbeat" : verify_heartbeat() ? "sthome-ut2 alive" : ""; return rtr ? "Request for sthome-ut2 heartbeat" : verify_heartbeat() ? "sthome-ut2 alive" : "";
case CB_CANBUS_ID03: case CB_CANBUS_ID03:
return rtr ? "Request for sthome-u3 heartbeat" : verify_heartbeat() ? "sthome-ut3 alive " : ""; return rtr ? "Request for sthome-u3 heartbeat" : verify_heartbeat() ? "sthome-ut3 alive " : "";
case CB_CANBUS_ID04: case CB_CANBUS_ID04:
return rtr ? "Request for sthome-ut4 heartbeat" : verify_heartbeat() ? "sthome-ut4 alive" : ""; return rtr ? "Request for sthome-ut4 heartbeat" : verify_heartbeat() ? "sthome-ut4 alive" : "";
case CB_CANBUS_ID05: case CB_CANBUS_ID05:
return rtr ? "Request for sthome-ut5 heartbeat" : verify_heartbeat() ? "sthome-ut5 alive" : ""; return rtr ? "Request for sthome-ut5 heartbeat" : verify_heartbeat() ? "sthome-ut5 alive" : "";
case CB_CANBUS_ID06: case CB_CANBUS_ID06:
return rtr ? "Request for sthome-ut6 heartbeat" : verify_heartbeat() ? "sthome-ut6 alive" : ""; return rtr ? "Request for sthome-ut6 heartbeat" : verify_heartbeat() ? "sthome-ut6 alive" : "";
case CB_CANBUS_ID07: case CB_CANBUS_ID07:
return rtr ? "Request for sthome-ut7 heartbeat" : verify_heartbeat() ? "sthome-ut7 alive" : ""; return rtr ? "Request for sthome-ut7 heartbeat" : verify_heartbeat() ? "sthome-ut7 alive" : "";
case CB_CANBUS_ID08: case CB_CANBUS_ID08:
return rtr ? "Request for sthome-ut8 heartbeat" : verify_heartbeat() ? "sthome-ut8 alive" : ""; return rtr ? "Request for sthome-ut8 heartbeat" : verify_heartbeat() ? "sthome-ut8 alive" : "";
case CB_CANBUS_ID09: case CB_CANBUS_ID09:
return rtr ? "Request for sthome-ut9 heartbeat" : verify_heartbeat() ? "sthome-ut9 alive" : ""; return rtr ? "Request for sthome-ut9 heartbeat" : verify_heartbeat() ? "sthome-ut9 alive" : "";
case CB_CANBUS_ID10: case CB_CANBUS_ID10:
return rtr ? "Request for sthome-ut10 heartbeat" : verify_heartbeat() ? "sthome-ut10 alive" : ""; return rtr ? "Request for sthome-ut10 heartbeat" : verify_heartbeat() ? "sthome-ut10 alive" : "";
case CB_GEYSER_TEMPERATURE_TOP: case CB_GEYSER_TEMPERATURE_TOP:
{ {
if(rtr) { if(rtr) {
return "Request for geyser top temp"; return "Request for geyser top temp";
} }
double temp_top = get_double(x[0], x[1], 256, true); double temp_top = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER TOP: %.2f°C", temp_top); snprintf (buffer, sizeof(buffer), "GEYSER TOP: %.2f°C", temp_top);
return buffer; return buffer;
} }
case CB_GEYSER_TEMPERATURE_BOTTOM: case CB_GEYSER_TEMPERATURE_BOTTOM:
{ {
if(rtr) { if(rtr) {
return "Request for geyser bottom temp"; return "Request for geyser bottom temp";
} }
double temp_bot = get_double(x[0], x[1], 256, true); double temp_bot = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER BOT: %.2f°C", temp_bot); snprintf (buffer, sizeof(buffer), "GEYSER BOT: %.2f°C", temp_bot);
return buffer; return buffer;
} }
case CB_GEYSER_TEMPERATURE_AMBIENT: case CB_GEYSER_TEMPERATURE_AMBIENT:
{ {
if(rtr) { if(rtr) {
return "Request for geyser ambient temp"; return "Request for geyser ambient temp";
} }
double temp_amb = get_double(x[0], x[1], 256, true); double temp_amb = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER AMB: %.2f°C", temp_amb); snprintf (buffer, sizeof(buffer), "GEYSER AMB: %.2f°C", temp_amb);
return buffer; return buffer;
} }
case CB_GEYSER_HEATING: case CB_GEYSER_HEATING:
{ {
if(rtr) { if(rtr) {
return "Request for geyser heating"; return "Request for geyser heating";
} }
double heating_loss = get_double(x[0], x[1], 64, true); // -512W to 512W double heating_loss = get_double(x[0], x[1], 64, true); // -512W to 512W
double heat_gained = get_double(x[2], x[3], 128); // 0 to 512W double heat_gained = get_double(x[2], x[3], 128); // 0 to 512W
double calculated_heat_loss = get_double(x[4], x[5], 128); // 0 to 512W double calculated_heat_loss = get_double(x[4], x[5], 128); // 0 to 512W
unsigned int est_heating_time = (unsigned int) static_cast<uint16_t>(256 * x[7] + x[6]); // 0 to 65536 seconds unsigned int est_heating_time = (unsigned int) static_cast<uint16_t>(256 * x[7] + x[6]); // 0 to 65536 seconds
snprintf (buffer, sizeof(buffer), "Heat: loss %.3fW, gain %.3fW, calc loss %.3fW, est. heat time %ds", heating_loss, heat_gained, calculated_heat_loss, est_heating_time); snprintf (buffer, sizeof(buffer), "Heat: loss %.3fW, gain %.3fW, calc loss %.3fW, est. heat time %ds", heating_loss, heat_gained, calculated_heat_loss, est_heating_time);
return buffer; return buffer;
} }
case CB_GEYSER_ACTIVE_SCHEDULE: case CB_GEYSER_ACTIVE_SCHEDULE:
{ {
if(rtr) { if(rtr) {
return "Request for geyser schedule"; return "Request for geyser schedule";
} }
double active_schedule_temp = get_double(x[0], x[1], 256, true); // -128 to 128°C double active_schedule_temp = get_double(x[0], x[1], 256, true); // -128 to 128°C
int active_heating_time = static_cast<int16_t>(256 * x[3] + x[2]); // -32768 to 32768s int active_heating_time = static_cast<int16_t>(256 * x[3] + x[2]); // -32768 to 32768s
double heating_overshoot_time = get_double(x[4], x[5], -64); // -512 to 512s double heating_overshoot_time = get_double(x[4], x[5], -64); // -512 to 512s
int active_schedule_day = static_cast<uint8_t>(x[6]); int active_schedule_day = static_cast<uint8_t>(x[6]);
snprintf (buffer, sizeof(buffer), "Geyser Schedule: target %.2f°C, heating time %ds, overshoot %.1fs, day %d", active_schedule_temp, active_heating_time, heating_overshoot_time, active_schedule_day); snprintf (buffer, sizeof(buffer), "Geyser Schedule: target %.2f°C, heating time %ds, overshoot %.1fs, day %d", active_schedule_temp, active_heating_time, heating_overshoot_time, active_schedule_day);
return buffer; return buffer;
} }
case CB_POWER_MAINS: case CB_POWER_MAINS:
{ {
if(rtr) { if(rtr) {
return "Request for power mains"; return "Request for power mains";
} }
double power = get_double(x[0], x[1], 2048); // 0 to 32kW double power = get_double(x[0], x[1], 2048); // 0 to 32kW
double voltage = get_double(x[2], x[3], 128); // 0 to 512V double voltage = get_double(x[2], x[3], 128); // 0 to 512V
double current = get_double(x[4], x[5], 512); // 0 to 128A double current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Mains : %.3fV, %.3fA, %.3fkW", voltage, current, power); snprintf (buffer, sizeof(buffer), "Mains : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer; return buffer;
} }
case CB_POWER_INVERTER: case CB_POWER_INVERTER:
{ {
if(rtr) { if(rtr) {
return "Request for power inverter"; return "Request for power inverter";
} }
double power = get_double(x[0], x[1], 2048); // 0 to 32kW double power = get_double(x[0], x[1], 2048); // 0 to 32kW
double voltage = get_double(x[2], x[3], 128); // 0 to 512V double voltage = get_double(x[2], x[3], 128); // 0 to 512V
double current = get_double(x[4], x[5], 512); // 0 to 128A double current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "InvOut: %.3fV, %.3fA, %.3fkW", voltage, current, power); snprintf (buffer, sizeof(buffer), "InvOut: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer; return buffer;
} }
case CB_POWER_PLUGS: case CB_POWER_PLUGS:
{ {
if(rtr) { if(rtr) {
return "Request for power plugs"; return "Request for power plugs";
} }
double power = get_double(x[0], x[1], 2048); // 0 to 32kW double power = get_double(x[0], x[1], 2048); // 0 to 32kW
double voltage = get_double(x[2], x[3], 128); // 0 to 512V double voltage = get_double(x[2], x[3], 128); // 0 to 512V
double current = get_double(x[4], x[5], 512); // 0 to 128A double current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Plugs : %.3fV, %.3fA, %.3fkW", voltage, current, power); snprintf (buffer, sizeof(buffer), "Plugs : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer; return buffer;
} }
case CB_POWER_LIGHTS: case CB_POWER_LIGHTS:
{ {
if(rtr) { if(rtr) {
return "Request for power lights"; return "Request for power lights";
} }
double power = get_double(x[0], x[1], 2048); // 0 to 32kW double power = get_double(x[0], x[1], 2048); // 0 to 32kW
double voltage = get_double(x[2], x[3], 128); // 0 to 512V double voltage = get_double(x[2], x[3], 128); // 0 to 512V
double current = get_double(x[4], x[5], 512); // 0 to 128A double current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Lights: %.3fV, %.3fA, %.3fkW", voltage, current, power); snprintf (buffer, sizeof(buffer), "Lights: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer; return buffer;
} }
case CB_POWER_GEYSER: case CB_POWER_GEYSER:
{ {
if(rtr) { if(rtr) {
return "Request for power geyser"; return "Request for power geyser";
} }
double power = get_double(x[0], x[1], 2048); // 0 to 32kW double power = get_double(x[0], x[1], 2048); // 0 to 32kW
double voltage = get_double(x[2], x[3], 128); // 0 to 512V double voltage = get_double(x[2], x[3], 128); // 0 to 512V
double current = get_double(x[4], x[5], 512); // 0 to 128A double current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Geyser: %.3fV, %.3fA, %.3fkW", voltage, current, power); snprintf (buffer, sizeof(buffer), "Geyser: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer; return buffer;
} }
case CB_POWER_POOL: case CB_POWER_POOL:
{ {
if(rtr) { if(rtr) {
return "Request for power pool"; return "Request for power pool";
} }
double power = get_double(x[0], x[1], 2048); // 0 to 32kW double power = get_double(x[0], x[1], 2048); // 0 to 32kW
double voltage = get_double(x[2], x[3], 128); // 0 to 512V double voltage = get_double(x[2], x[3], 128); // 0 to 512V
double current = get_double(x[4], x[5], 512); // 0 to 128A double current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Pool : %.3fV, %.3fA, %.3fkW", voltage, current, power); snprintf (buffer, sizeof(buffer), "Pool : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer; return buffer;
} }
case CB_POWER_GENERATED: case CB_POWER_GENERATED:
{ {
if(rtr) { if(rtr) {
return "Request for power generated"; return "Request for power generated";
} }
double power_generated = get_double(x[0], x[1], 2048); // 0 to 32kW double power_generated = get_double(x[0], x[1], 2048); // 0 to 32kW
double power_loss = get_double(x[2], x[3], 2048); // 0 to 32kW double power_loss = get_double(x[2], x[3], 2048); // 0 to 32kW
snprintf (buffer, sizeof(buffer), "Power : Generated %.3fkW, Loss %.3fkW", power_generated, power_loss); snprintf (buffer, sizeof(buffer), "Power : Generated %.3fkW, Loss %.3fkW", power_generated, power_loss);
return buffer; return buffer;
} }
case CB_ENERGY_MAINS: case CB_ENERGY_MAINS:
{ {
if(rtr) { if(rtr) {
return "Request for energy mains"; return "Request for energy mains";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "Mains Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "Mains Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_ENERGY_GEYSER: case CB_ENERGY_GEYSER:
{ {
if(rtr) { if(rtr) {
return "Request for energy geyser"; return "Request for energy geyser";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "Geyser Energy day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "Geyser Energy day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_ENERGY_POOL: case CB_ENERGY_POOL:
{ {
if(rtr) { if(rtr) {
return "Request for energy pool"; return "Request for energy pool";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "Pool Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "Pool Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_ENERGY_PLUGS: case CB_ENERGY_PLUGS:
{ {
if(rtr) { if(rtr) {
return "Request for energy plugs"; return "Request for energy plugs";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "Plugs Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "Plugs Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_ENERGY_LIGHTS: case CB_ENERGY_LIGHTS:
{ {
if(rtr) { if(rtr) {
return "Request for energy lights"; return "Request for energy lights";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "Lights Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "Lights Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_ENERGY_HOUSE: case CB_ENERGY_HOUSE:
{ {
if(rtr) { if(rtr) {
return "Request for energy house"; return "Request for energy house";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "House Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "House Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_ENERGY_GENERATED: case CB_ENERGY_GENERATED:
{ {
if(rtr) { if(rtr) {
return "Request for energy generated"; return "Request for energy generated";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "Generated Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "Generated Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_ENERGY_LOSS: case CB_ENERGY_LOSS:
{ {
if(rtr) { if(rtr) {
return "Request for energy loss"; return "Request for energy loss";
} }
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
snprintf (buffer, sizeof(buffer), "Energy Loss: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime); snprintf (buffer, sizeof(buffer), "Energy Loss: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
return buffer; return buffer;
} }
case CB_CONTROLLER_STATES: case CB_CONTROLLER_STATES:
{ {
if(rtr) { if(rtr) {
return "Request for controller states"; return "Request for controller states";
} }
uint8_t alarms = x[0]; uint8_t alarms = x[0];
uint8_t states = x[1]; uint8_t states = x[1];
uint8_t modes = x[2]; uint8_t modes = x[2];
bool inverter_1_battery_low = alarms & 0x80; bool inverter_1_battery_low = alarms & 0x80;
bool inverter_2_battery_low = alarms & 0x10; bool inverter_2_battery_low = alarms & 0x10;
bool inverter_overload = alarms & 0x08; bool inverter_overload = alarms & 0x08;
bool geyser_heating = states & 0x80; bool geyser_heating = states & 0x80;
bool geyser_energised = states & 0x40; bool geyser_energised = states & 0x40;
bool mains_supply_present = states & 0x20; bool mains_supply_present = states & 0x20;
bool battery_charging = states & 0x08; bool battery_charging = states & 0x08;
bool vacation_mode = modes & 0x80; // nobody at home bool vacation_mode = modes & 0x80; // nobody at home
std::string mode = ""; std::string mode = "";
int geysermode = (modes & 0x70) >> 4; int geysermode = (modes & 0x70) >> 4;
switch (geysermode) { switch (geysermode) {
case GM_SUNDAY: case GM_SUNDAY:
mode = "SUN"; mode = "SUN";
break; break;
case GM_WORKDAY: case GM_WORKDAY:
mode = "WRK"; mode = "WRK";
break; break;
case GM_SATURDAY: case GM_SATURDAY:
mode = "SAT"; mode = "SAT";
break; break;
case GM_PUBLIC_HOLIDAY: case GM_PUBLIC_HOLIDAY:
mode = "PUB"; mode = "PUB";
break; break;
case GM_SCHOOL_HOLIDAY: case GM_SCHOOL_HOLIDAY:
mode = "SCH"; mode = "SCH";
break; break;
default: default:
mode = "UNK"; mode = "UNK";
break; break;
} }
snprintf(buffer, sizeof(buffer), "STATES: %s%s%s%s MODE: %s%s INV: %s%s%s", geyser_heating ? "HEAT " : "", geyser_energised ? "GEYSER " : "", mains_supply_present ? "MAINS " : "", battery_charging ? "BATCHG " : "", vacation_mode ? "VACA ": "", mode.c_str(), inverter_1_battery_low ? "INV LOBAT " : "", inverter_2_battery_low ? "INV2 LOBAT " : "", inverter_overload ? "INV OVL " : ""); snprintf(buffer, sizeof(buffer), "STATES: %s%s%s%s MODE: %s%s INV: %s%s%s", geyser_heating ? "HEAT " : "", geyser_energised ? "GEYSER " : "", mains_supply_present ? "MAINS " : "", battery_charging ? "BATCHG " : "", vacation_mode ? "VACA ": "", mode.c_str(), inverter_1_battery_low ? "INV LOBAT " : "", inverter_2_battery_low ? "INV2 LOBAT " : "", inverter_overload ? "INV OVL " : "");
return buffer; return buffer;
} }
} }
if(rtr) { if(rtr) {
return "Request for unknown sthome CAN ID"; return "Request for unknown sthome CAN ID";
} }
return "Unknown CAN ID"; return "Unknown CAN ID";
} }
} // namespace solar } // namespace solar

View File

@ -1,78 +1,78 @@
#ifndef __SOLAR_CBF_STHOME_H // include GUARD #ifndef __SOLAR_CBF_STHOME_H // include GUARD
#define __SOLAR_CBF_STHOME_H #define __SOLAR_CBF_STHOME_H
#include "esphome.h" #include "esphome.h"
#include <utility> #include <utility>
#include "common.h" #include "common.h"
#include "cb_frame.h" #include "cb_frame.h"
using namespace esphome; using namespace esphome;
namespace solar namespace solar
{ {
class cbf_sthome : virtual public cb_frame { class cbf_sthome : virtual public cb_frame {
private: private:
// float _get_battery_charge_voltage_limit() const; // float _get_battery_charge_voltage_limit() const;
public: public:
// STHOME publish spec // STHOME publish spec
// This is used to determine when to publish the sthome data // This is used to determine when to publish the sthome data
static const publish_spec_t publish_spec; // default publish spec for sthome messages static const publish_spec_t publish_spec; // default publish spec for sthome messages
static const publish_spec_t rtr_publish_spec; // for remote transmission requests static const publish_spec_t rtr_publish_spec; // for remote transmission requests
enum geyser_modes : int { enum geyser_modes : int {
GM_SUNDAY, GM_SUNDAY,
GM_WORKDAY, GM_WORKDAY,
GM_SATURDAY, GM_SATURDAY,
GM_PUBLIC_HOLIDAY, GM_PUBLIC_HOLIDAY,
GM_SCHOOL_HOLIDAY, GM_SCHOOL_HOLIDAY,
}; };
enum canbus_ids : int { enum canbus_ids : int {
CB_CANBUS_ID01 = 0x501, CB_CANBUS_ID01 = 0x501,
CB_CANBUS_ID02, CB_CANBUS_ID02,
CB_CANBUS_ID03, CB_CANBUS_ID03,
CB_CANBUS_ID04, CB_CANBUS_ID04,
CB_CANBUS_ID05, CB_CANBUS_ID05,
CB_CANBUS_ID06, CB_CANBUS_ID06,
CB_CANBUS_ID07, CB_CANBUS_ID07,
CB_CANBUS_ID08, CB_CANBUS_ID08,
CB_CANBUS_ID09, CB_CANBUS_ID09,
CB_CANBUS_ID10, CB_CANBUS_ID10,
CB_POWER_MAINS = 0x401, CB_POWER_MAINS = 0x401,
CB_POWER_INVERTER, CB_POWER_INVERTER,
CB_POWER_PLUGS, CB_POWER_PLUGS,
CB_POWER_LIGHTS, CB_POWER_LIGHTS,
CB_POWER_GEYSER, CB_POWER_GEYSER,
CB_POWER_POOL, CB_POWER_POOL,
CB_POWER_GENERATED, CB_POWER_GENERATED,
CB_ENERGY_MAINS, CB_ENERGY_MAINS,
CB_ENERGY_GEYSER, CB_ENERGY_GEYSER,
CB_ENERGY_POOL, CB_ENERGY_POOL,
CB_ENERGY_PLUGS, CB_ENERGY_PLUGS,
CB_ENERGY_LIGHTS, CB_ENERGY_LIGHTS,
CB_ENERGY_HOUSE, CB_ENERGY_HOUSE,
CB_ENERGY_GENERATED, CB_ENERGY_GENERATED,
CB_ENERGY_LOSS, CB_ENERGY_LOSS,
CB_CONTROLLER_STATES, CB_CONTROLLER_STATES,
CB_GEYSER_TEMPERATURE_TOP, CB_GEYSER_TEMPERATURE_TOP,
CB_GEYSER_TEMPERATURE_BOTTOM, CB_GEYSER_TEMPERATURE_BOTTOM,
CB_GEYSER_TEMPERATURE_AMBIENT, CB_GEYSER_TEMPERATURE_AMBIENT,
CB_GEYSER_HEATING, CB_GEYSER_HEATING,
CB_GEYSER_ACTIVE_SCHEDULE, CB_GEYSER_ACTIVE_SCHEDULE,
}; };
//Property<int, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_set_battery_charge_voltage_limit, &cbf_sthome::_get_battery_charge_voltage_limit }; //Property<int, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_set_battery_charge_voltage_limit, &cbf_sthome::_get_battery_charge_voltage_limit };
//Property<float, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_get_battery_charge_voltage_limit}; //Property<float, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_get_battery_charge_voltage_limit};
static const std::string heartbeat; static const std::string heartbeat;
cbf_sthome() = default; cbf_sthome() = default;
cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr); cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
void clear(); void clear();
virtual std::string to_string() const override; virtual std::string to_string() const override;
cbf_sthome& operator=(cbf_sthome&& src); // required due to double inheritance in cbf_store_pylon cbf_sthome& operator=(cbf_sthome&& src); // required due to double inheritance in cbf_store_pylon
// using default (compiler auto generated) copy and move constructors and assignment operators // using default (compiler auto generated) copy and move constructors and assignment operators
virtual ~cbf_sthome() = default; virtual ~cbf_sthome() = default;
bool verify_heartbeat() const; bool verify_heartbeat() const;
}; };
} // namespace solar } // namespace solar
#endif // __SOLAR_CBF_STHOME_H #endif // __SOLAR_CBF_STHOME_H

View File

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

View File

@ -1,71 +1,71 @@
#include "cbf_store_pylon.h" #include "cbf_store_pylon.h"
namespace solar namespace solar
{ {
cbf_store_pylon::cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp) cbf_store_pylon::cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp)
: cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_pylon(msg_id, can_id, frame, rtr) : cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_pylon(msg_id, can_id, frame, rtr)
{ {
this->id = 0; this->id = 0;
this->count = 0; this->count = 0;
// ESP_LOGI(tag("store_pylon CTOR1").c_str(), "%-20s %s", "Created store_pylon", this->to_string().c_str()); // ESP_LOGI(tag("store_pylon CTOR1").c_str(), "%-20s %s", "Created store_pylon", this->to_string().c_str());
} }
cbf_store_pylon::cbf_store_pylon(const cbf_store_pylon& b) cbf_store_pylon::cbf_store_pylon(const cbf_store_pylon& b)
: cb_frame(b), cbf_store(b) // call base class copy constructor : cb_frame(b), cbf_store(b) // call base class copy constructor
{ {
// ESP_LOGI(tag("store_pylon CCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str()); // ESP_LOGI(tag("store_pylon CCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str());
} }
cbf_store_pylon& cbf_store_pylon::operator=(const cbf_store_pylon& b) cbf_store_pylon& cbf_store_pylon::operator=(const cbf_store_pylon& b)
{ {
if (this != &b) { if (this != &b) {
cbf_store_pylon tmp(b); cbf_store_pylon tmp(b);
swap(tmp); swap(tmp);
// ESP_LOGI(tag("store_pylon AOPP").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str()); // ESP_LOGI(tag("store_pylon AOPP").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
} }
return *this; return *this;
} }
cbf_store_pylon::cbf_store_pylon(cbf_store_pylon&& src) cbf_store_pylon::cbf_store_pylon(cbf_store_pylon&& src)
: cb_frame(src), cbf_store(src) : cb_frame(src), cbf_store(src)
{ {
src.clear(); src.clear();
// ESP_LOGI(tag("store_pylon MCCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str()); // ESP_LOGI(tag("store_pylon MCCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str());
} }
cbf_store_pylon& cbf_store_pylon::operator=(cbf_store_pylon&& src) cbf_store_pylon& cbf_store_pylon::operator=(cbf_store_pylon&& src)
{ {
if (this != &src) { if (this != &src) {
cbf_store::operator=(std::move(src)); cbf_store::operator=(std::move(src));
cb_frame::operator=(std::move(src)); cb_frame::operator=(std::move(src));
src.clear(); src.clear();
// ESP_LOGI(tag("store_pylon MOASS").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str()); // ESP_LOGI(tag("store_pylon MOASS").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
} }
return *this; return *this;
} }
std::shared_ptr<cbf_store_pylon> cbf_store_pylon::clone() const std::shared_ptr<cbf_store_pylon> cbf_store_pylon::clone() const
{ {
return std::static_pointer_cast<cbf_store_pylon>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_pylon return std::static_pointer_cast<cbf_store_pylon>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_pylon
} }
std::shared_ptr<cbf_store> cbf_store_pylon::clone_impl() const std::shared_ptr<cbf_store> cbf_store_pylon::clone_impl() const
{ {
//ESP_LOGI(tag("store_pylon CLONE").c_str(), "%-20s", "Cloning store_pylon"); //ESP_LOGI(tag("store_pylon CLONE").c_str(), "%-20s", "Cloning store_pylon");
return std::make_shared<cbf_store_pylon>(*this); return std::make_shared<cbf_store_pylon>(*this);
} }
void cbf_store_pylon::clear() void cbf_store_pylon::clear()
{ {
cb_frame::clear(); cb_frame::clear();
cbf_store::clear(); cbf_store::clear();
} }
void cbf_store_pylon::swap(cbf_store_pylon &s) void cbf_store_pylon::swap(cbf_store_pylon &s)
{ {
cb_frame::swap(s); cb_frame::swap(s);
cbf_store::swap(s); cbf_store::swap(s);
} }
cbf_store::cbf_updateresult cbf_store_pylon::update(const cbf_store& newitem, const int *comparecolumns) cbf_store::cbf_updateresult cbf_store_pylon::update(const cbf_store& newitem, const int *comparecolumns)
{ {
//ESP_LOGI(tag("store_pylon UPDATE").c_str(), "%-20s %s", "Updating store_pylon", this->to_string().c_str()); //ESP_LOGI(tag("store_pylon UPDATE").c_str(), "%-20s %s", "Updating store_pylon", this->to_string().c_str());
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns); return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
} }
std::string cbf_store_pylon::to_string() const std::string cbf_store_pylon::to_string() const
{ {
return cb_frame::to_string() + " " + cbf_pylon::to_string(); return cb_frame::to_string() + " " + cbf_pylon::to_string();
} }
} // namespace solar } // namespace solar

View File

@ -1,34 +1,34 @@
#ifndef __SOLAR_CBF_STORE_PYLON // include GUARD #ifndef __SOLAR_CBF_STORE_PYLON // include GUARD
#define __SOLAR_CBF_STORE_PYLON #define __SOLAR_CBF_STORE_PYLON
#include "esphome.h" #include "esphome.h"
#include <utility> #include <utility>
#include "common.h" #include "common.h"
#include "cbf_store.h" #include "cbf_store.h"
#include "cbf_pylon.h" #include "cbf_pylon.h"
using namespace esphome; using namespace esphome;
namespace solar namespace solar
{ {
class cbf_store_pylon : public cbf_store, public cbf_pylon class cbf_store_pylon : public cbf_store, public cbf_pylon
{ {
public: public:
cbf_store_pylon() = delete; // default constructor not allowed cbf_store_pylon() = delete; // default constructor not allowed
cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp); cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp);
virtual ~cbf_store_pylon() = default; // virtual destructor for base class virtual ~cbf_store_pylon() = default; // virtual destructor for base class
std::shared_ptr<cbf_store_pylon> clone() const; std::shared_ptr<cbf_store_pylon> clone() const;
void clear(); void clear();
void swap(cbf_store_pylon &s); void swap(cbf_store_pylon &s);
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override; virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
virtual std::string to_string() const override; virtual std::string to_string() const override;
cbf_store_pylon(const cbf_store_pylon& b); cbf_store_pylon(const cbf_store_pylon& b);
cbf_store_pylon& operator=(const cbf_store_pylon& b); cbf_store_pylon& operator=(const cbf_store_pylon& b);
cbf_store_pylon(cbf_store_pylon&& src); cbf_store_pylon(cbf_store_pylon&& src);
cbf_store_pylon& operator=(cbf_store_pylon&& src); cbf_store_pylon& operator=(cbf_store_pylon&& src);
private: private:
virtual std::shared_ptr<cbf_store> clone_impl() const override; virtual std::shared_ptr<cbf_store> clone_impl() const override;
}; };
} // namespace solar } // namespace solar
#endif // __SOLAR_CBF_STORE_PYLON #endif // __SOLAR_CBF_STORE_PYLON

View File

@ -1,71 +1,71 @@
#include "cbf_store_sthome.h" #include "cbf_store_sthome.h"
namespace solar namespace solar
{ {
cbf_store_sthome::cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp) cbf_store_sthome::cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp)
: cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_sthome(msg_id, can_id, frame, rtr) : cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_sthome(msg_id, can_id, frame, rtr)
{ {
this->id = 0; this->id = 0;
this->count = 0; this->count = 0;
// ESP_LOGI(tag("store_sthome CTOR1").c_str(), "%-20s %s", "Created store_sthome", this->to_string().c_str()); // ESP_LOGI(tag("store_sthome CTOR1").c_str(), "%-20s %s", "Created store_sthome", this->to_string().c_str());
} }
cbf_store_sthome::cbf_store_sthome(const cbf_store_sthome& b) cbf_store_sthome::cbf_store_sthome(const cbf_store_sthome& b)
: cb_frame(b), cbf_store(b) // call base class copy constructor : cb_frame(b), cbf_store(b) // call base class copy constructor
{ {
// ESP_LOGI(tag("store_sthome CCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str()); // ESP_LOGI(tag("store_sthome CCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str());
} }
cbf_store_sthome& cbf_store_sthome::operator=(const cbf_store_sthome& b) cbf_store_sthome& cbf_store_sthome::operator=(const cbf_store_sthome& b)
{ {
if (this != &b) { if (this != &b) {
cbf_store_sthome tmp(b); cbf_store_sthome tmp(b);
swap(tmp); swap(tmp);
// ESP_LOGI(tag("store_sthome AOPP").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str()); // ESP_LOGI(tag("store_sthome AOPP").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
} }
return *this; return *this;
} }
cbf_store_sthome::cbf_store_sthome(cbf_store_sthome&& src) cbf_store_sthome::cbf_store_sthome(cbf_store_sthome&& src)
: cb_frame(src), cbf_store(src) : cb_frame(src), cbf_store(src)
{ {
src.clear(); src.clear();
// ESP_LOGI(tag("store_sthome MCCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str()); // ESP_LOGI(tag("store_sthome MCCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str());
} }
cbf_store_sthome& cbf_store_sthome::operator=(cbf_store_sthome&& src) cbf_store_sthome& cbf_store_sthome::operator=(cbf_store_sthome&& src)
{ {
if (this != &src) { if (this != &src) {
cbf_store::operator=(std::move(src)); cbf_store::operator=(std::move(src));
cb_frame::operator=(std::move(src)); cb_frame::operator=(std::move(src));
src.clear(); src.clear();
// ESP_LOGI(tag("store_sthome MOASS").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str()); // ESP_LOGI(tag("store_sthome MOASS").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
} }
return *this; return *this;
} }
std::shared_ptr<cbf_store_sthome> cbf_store_sthome::clone() const std::shared_ptr<cbf_store_sthome> cbf_store_sthome::clone() const
{ {
return std::static_pointer_cast<cbf_store_sthome>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_sthome return std::static_pointer_cast<cbf_store_sthome>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_sthome
} }
std::shared_ptr<cbf_store> cbf_store_sthome::clone_impl() const std::shared_ptr<cbf_store> cbf_store_sthome::clone_impl() const
{ {
//ESP_LOGI(tag("store_sthome CLONE").c_str(), "%-20s", "Cloning store_sthome"); //ESP_LOGI(tag("store_sthome CLONE").c_str(), "%-20s", "Cloning store_sthome");
return std::make_shared<cbf_store_sthome>(*this); return std::make_shared<cbf_store_sthome>(*this);
} }
void cbf_store_sthome::clear() void cbf_store_sthome::clear()
{ {
cb_frame::clear(); cb_frame::clear();
cbf_store::clear(); cbf_store::clear();
} }
void cbf_store_sthome::swap(cbf_store_sthome &s) void cbf_store_sthome::swap(cbf_store_sthome &s)
{ {
cb_frame::swap(s); cb_frame::swap(s);
cbf_store::swap(s); cbf_store::swap(s);
} }
cbf_store::cbf_updateresult cbf_store_sthome::update(const cbf_store& newitem, const int *comparecolumns) cbf_store::cbf_updateresult cbf_store_sthome::update(const cbf_store& newitem, const int *comparecolumns)
{ {
//ESP_LOGI(tag("store_sthome UPDATE").c_str(), "%-20s %s", "Updating store_sthome", this->to_string().c_str()); //ESP_LOGI(tag("store_sthome UPDATE").c_str(), "%-20s %s", "Updating store_sthome", this->to_string().c_str());
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns); return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
} }
std::string cbf_store_sthome::to_string() const std::string cbf_store_sthome::to_string() const
{ {
return cb_frame::to_string() + " " + cbf_sthome::to_string(); return cb_frame::to_string() + " " + cbf_sthome::to_string();
} }
} // namespace solar } // namespace solar

View File

@ -1,34 +1,34 @@
#ifndef __SOLAR_CBF_STORE_STHOME_H // include GUARD #ifndef __SOLAR_CBF_STORE_STHOME_H // include GUARD
#define __SOLAR_CBF_STORE_STHOME_H #define __SOLAR_CBF_STORE_STHOME_H
#include "esphome.h" #include "esphome.h"
#include <utility> #include <utility>
#include "common.h" #include "common.h"
#include "cbf_store.h" #include "cbf_store.h"
#include "cbf_sthome.h" #include "cbf_sthome.h"
using namespace esphome; using namespace esphome;
namespace solar namespace solar
{ {
class cbf_store_sthome : public cbf_store, public cbf_sthome class cbf_store_sthome : public cbf_store, public cbf_sthome
{ {
public: public:
cbf_store_sthome() = delete; // default constructor not allowed cbf_store_sthome() = delete; // default constructor not allowed
cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp); cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp);
virtual ~cbf_store_sthome() = default; // virtual destructor for base class virtual ~cbf_store_sthome() = default; // virtual destructor for base class
std::shared_ptr<cbf_store_sthome> clone() const; std::shared_ptr<cbf_store_sthome> clone() const;
void clear(); void clear();
void swap(cbf_store_sthome &s); void swap(cbf_store_sthome &s);
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override; virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
virtual std::string to_string() const override; virtual std::string to_string() const override;
cbf_store_sthome(const cbf_store_sthome& b); cbf_store_sthome(const cbf_store_sthome& b);
cbf_store_sthome& operator=(const cbf_store_sthome& b); cbf_store_sthome& operator=(const cbf_store_sthome& b);
cbf_store_sthome(cbf_store_sthome&& src); cbf_store_sthome(cbf_store_sthome&& src);
cbf_store_sthome& operator=(cbf_store_sthome&& src); cbf_store_sthome& operator=(cbf_store_sthome&& src);
private: private:
virtual std::shared_ptr<cbf_store> clone_impl() const override; virtual std::shared_ptr<cbf_store> clone_impl() const override;
}; };
} // namespace solar } // namespace solar
#endif // __SOLAR_CBF_STORE_STHOME_H #endif // __SOLAR_CBF_STORE_STHOME_H

View File

@ -1,48 +1,48 @@
#ifndef __SOLAR_COMMON // include GUARD #ifndef __SOLAR_COMMON // include GUARD
#define __SOLAR_COMMON #define __SOLAR_COMMON
namespace solar namespace solar
{ {
#define FLAGBITS ~0x0FFF // bitwise one's complement (sort column info fits into lower three nibbles) #define FLAGBITS ~0x0FFF // bitwise one's complement (sort column info fits into lower three nibbles)
#define SORTCASE 0x0001000 // case sensitive #define SORTCASE 0x0001000 // case sensitive
#define SORTREVERSE 0x0002000 // reverse order #define SORTREVERSE 0x0002000 // reverse order
#define WILDNULL 0x0004000 // zero denotes wildcard, i.e. anything is equal to zero #define WILDNULL 0x0004000 // zero denotes wildcard, i.e. anything is equal to zero
#define STOPCOMPARE 0x0080000 // stop compare after this column if both items to be compared are valid and a compare was possible #define STOPCOMPARE 0x0080000 // stop compare after this column if both items to be compared are valid and a compare was possible
#define SEARCHWILD 0x0100000 // do wildcard comparison (only searchkey should contain wildcards) #define SEARCHWILD 0x0100000 // do wildcard comparison (only searchkey should contain wildcards)
#define num_compare(a, b) ((a) > (b)) ? +1 : ((a) < (b)) ? -1 : 0 #define num_compare(a, b) ((a) > (b)) ? +1 : ((a) < (b)) ? -1 : 0
#define bool_compare(a, b) ((!(a) && !(b)) || ((a) && (b))) ? 0 : (a) ? +1 : -1 #define bool_compare(a, b) ((!(a) && !(b)) || ((a) && (b))) ? 0 : (a) ? +1 : -1
#define max(a, b) ((a) > (b)) ? (a) : (b) #define max(a, b) ((a) > (b)) ? (a) : (b)
#define min(a, b) ((a) < (b)) ? (a) : (b) #define min(a, b) ((a) < (b)) ? (a) : (b)
typedef unsigned int uint; typedef unsigned int uint;
typedef std::vector<uint8_t> byte_vector; typedef std::vector<uint8_t> byte_vector;
struct publish_spec_t struct publish_spec_t
{ {
int on_count; // how many times a duplicate of this frame was received; used to signal publish int on_count; // how many times a duplicate of this frame was received; used to signal publish
int interval; // used for checking whether updateinterval has expired, i.e. to force a publish int interval; // used for checking whether updateinterval has expired, i.e. to force a publish
int timeout; // used for checking whether timeout has expired, i.e. frame has become stale int timeout; // used for checking whether timeout has expired, i.e. frame has become stale
publish_spec_t(int on_count, int interval, int timeout) publish_spec_t(int on_count, int interval, int timeout)
: on_count(on_count), interval(interval), timeout(timeout) {} : on_count(on_count), interval(interval), timeout(timeout) {}
}; };
inline std::string ltrim(const std::string& str, const std::string& chars = " \t\n\r\f\v") inline std::string ltrim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
{ {
size_t start = str.find_first_not_of(chars); size_t start = str.find_first_not_of(chars);
return (start == std::string::npos) ? "" : str.substr(start); return (start == std::string::npos) ? "" : str.substr(start);
} }
inline std::string rtrim(const std::string& str, const std::string& chars = " \t\n\r\f\v") inline std::string rtrim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
{ {
size_t end = str.find_last_not_of(chars); size_t end = str.find_last_not_of(chars);
return (end == std::string::npos) ? "" : str.substr(0, end + 1); return (end == std::string::npos) ? "" : str.substr(0, end + 1);
} }
inline std::string trim(const std::string& str, const std::string& chars = " \t\n\r\f\v") inline std::string trim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
{ {
return rtrim(ltrim(str, chars), chars); return rtrim(ltrim(str, chars), chars);
} }
} // namespace solar } // namespace solar
#endif // __SOLAR_COMMON #endif // __SOLAR_COMMON