updates after making Solar submodule of esphome config folder

This commit is contained in:
Chris Stuurman 2026-03-20 12:43:09 +02:00
parent 9b71eca610
commit 4fb865c0fa
17 changed files with 1802 additions and 1802 deletions

View File

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

View File

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

View File

@ -1,66 +1,66 @@
#ifndef __SOLAR_CB_FRAME // include GUARD
#define __SOLAR_CB_FRAME
#include "esphome.h"
#include <utility>
#include "common.h"
using namespace esphome;
namespace solar
{
class cbf_store;
class cb_frame {
private:
mutable bool publish; // whether this frame should be published
public:
enum cbf_sortcolumns : int
{
sisortNULL,
sisortcanid,
sisortrtr,
sisortframe,
sisortmsgid,
sisortEND,
sisortcase = SORTCASE, // case sensitive
sisortreverse = SORTREVERSE, // reverse order
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
};
int msg_id;
bool rtr;
uint32_t can_id;
byte_vector frame;
cb_frame();
cb_frame(int msg_id, uint32_t can_id);
cb_frame(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr);
void clear();
void swap(cb_frame &s);
virtual bool is_valid() 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;
int compare_frame(const byte_vector& _frame) const;
void setpublish(bool do_publish = true) const; // we promise only to modify the publish flag
bool getpublish() const;
virtual std::string tag(const std::string& prefix = "", int prefixlen = 18) const;
virtual std::string to_string() const;
virtual ~cb_frame() = default;
// using default (compiler auto generated) copy and move constructors and assignment operators
/// Helper functions to convert four scaled values into a byte stream
/// 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, 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, double value4, int scale4);
/// 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);
/// 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
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
};
extern const cb_frame emptyframe; // to return a reference to when no valid frame is found
} // namespace solar
#endif // __SOLAR_CB_FRAME
#ifndef __SOLAR_CB_FRAME // include GUARD
#define __SOLAR_CB_FRAME
#include "esphome.h"
#include <utility>
#include "common.h"
using namespace esphome;
namespace solar
{
class cbf_store;
class cb_frame {
private:
mutable bool publish; // whether this frame should be published
public:
enum cbf_sortcolumns : int
{
sisortNULL,
sisortcanid,
sisortrtr,
sisortframe,
sisortmsgid,
sisortEND,
sisortcase = SORTCASE, // case sensitive
sisortreverse = SORTREVERSE, // reverse order
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
};
int msg_id;
bool rtr;
uint32_t can_id;
byte_vector frame;
cb_frame();
cb_frame(int msg_id, uint32_t can_id);
cb_frame(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr);
void clear();
void swap(cb_frame &s);
virtual bool is_valid() 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;
int compare_frame(const byte_vector& _frame) const;
void setpublish(bool do_publish = true) const; // we promise only to modify the publish flag
bool getpublish() const;
virtual std::string tag(const std::string& prefix = "", int prefixlen = 18) const;
virtual std::string to_string() const;
virtual ~cb_frame() = default;
// using default (compiler auto generated) copy and move constructors and assignment operators
/// Helper functions to convert four scaled values into a byte stream
/// 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, 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, double value4, int scale4);
/// 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);
/// 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
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
};
extern const cb_frame emptyframe; // to return a reference to when no valid frame is found
} // namespace solar
#endif // __SOLAR_CB_FRAME

View File

@ -1,93 +1,93 @@
#include "source/solar/cbf_cache.h"
namespace solar
{
static const int comparecolumns[] = {cbf_store::sisortframe, cbf_store::sisortrtr, 0};
void cbf_cache::clear()
{
cache_map.clear();
}
int cbf_cache::size() const
{
return cache_map.size();
}
bool cbf_cache::hasitem(uint32_t can_id, bool remote_transmission_request) const
{
return hasitem(canid_rtr_t(can_id, remote_transmission_request));
}
bool cbf_cache::hasitem(canid_rtr_t key) const
{
return cache_map.find(key) != cache_map.end();
}
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));
}
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));
}
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));
if(ret.second) {
return false; // new item inserted, no publish
}
auto& kvp = *ret.first;
auto& item = kvp.second;
auto updateresult = item.update(storeitem, comparecolumns, 1);
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
// ESP_LOGI(item.store0.tag("== ST1 DUP == ").c_str(), item.store0.to_string().c_str());
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
}
// try next store to see if it has a duplicate of new item
updateresult = item.update(storeitem, comparecolumns, 2);
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
// ESP_LOGI(item.store1.tag("== ST2 DUP == ").c_str(), item.store1.to_string().c_str());
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
}
item.update(storeitem);
return false;
}
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));
}
const cb_frame& cbf_cache::get_frame(canid_rtr_t key) const
{
auto it = cache_map.find(key);
if(it == cache_map.end())
return emptyframe; // return reference to static empty frame if not found
const auto& item = it->second;
return item.get_frame();
}
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);
if(!this->hasitem(key))
return false;
const auto& cbf = get_frame(key);
if(cbf.getpublish()) {
return cbf.send_frame(canbus, extended_id, remote_transmission_request);
}
return false;
for(const auto& kvp : cache_map) {
const auto& item = kvp.second;
const auto& cbf = item.get_frame();
if(cbf.getpublish()) {
cbf.send_frame(canbus, extended_id, remote_transmission_request);
}
}
}
// 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)
{
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)
{
return cb_frame::send_frame(canbus, can_id, {}, extended_id, true);
}
#include "source/solar/cbf_cache.h"
namespace solar
{
static const int comparecolumns[] = {cbf_store::sisortframe, cbf_store::sisortrtr, 0};
void cbf_cache::clear()
{
cache_map.clear();
}
int cbf_cache::size() const
{
return cache_map.size();
}
bool cbf_cache::hasitem(uint32_t can_id, bool remote_transmission_request) const
{
return hasitem(canid_rtr_t(can_id, remote_transmission_request));
}
bool cbf_cache::hasitem(canid_rtr_t key) const
{
return cache_map.find(key) != cache_map.end();
}
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));
}
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));
}
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));
if(ret.second) {
return false; // new item inserted, no publish
}
auto& kvp = *ret.first;
auto& item = kvp.second;
auto updateresult = item.update(storeitem, comparecolumns, 1);
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
// ESP_LOGI(item.store0.tag("== ST1 DUP == ").c_str(), item.store0.to_string().c_str());
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
}
// try next store to see if it has a duplicate of new item
updateresult = item.update(storeitem, comparecolumns, 2);
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
// ESP_LOGI(item.store1.tag("== ST2 DUP == ").c_str(), item.store1.to_string().c_str());
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
}
item.update(storeitem);
return false;
}
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));
}
const cb_frame& cbf_cache::get_frame(canid_rtr_t key) const
{
auto it = cache_map.find(key);
if(it == cache_map.end())
return emptyframe; // return reference to static empty frame if not found
const auto& item = it->second;
return item.get_frame();
}
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);
if(!this->hasitem(key))
return false;
const auto& cbf = get_frame(key);
if(cbf.getpublish()) {
return cbf.send_frame(canbus, extended_id, remote_transmission_request);
}
return false;
for(const auto& kvp : cache_map) {
const auto& item = kvp.second;
const auto& cbf = item.get_frame();
if(cbf.getpublish()) {
cbf.send_frame(canbus, extended_id, remote_transmission_request);
}
}
}
// 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)
{
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)
{
return cb_frame::send_frame(canbus, can_id, {}, extended_id, true);
}
} // 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.
#ifndef __SOLAR_CBF_CACHE
#define __SOLAR_CBF_CACHE
#include <utility>
#include <map>
#include "esphome.h"
#include "cbf_store_pylon.h"
#include "cbf_cache_item.h"
using namespace esphome;
namespace solar
{
class cbf_cache {
public:
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
cbf_cache() = default;
void clear();
int size() const;
bool hasitem(canid_rtr_t key) const;
bool hasitem(uint32_t can_id, bool remote_transmission_request) const;
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;
// Add a new item to the cache or update an existing one
bool additem(const cbf_store& item);
const cb_frame& get_frame(canid_rtr_t key) const;
const cb_frame& get_frame(uint32_t can_id, bool remote_transmission_request) const;
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);
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
// using default (compiler auto generated) copy and move constructors and assignment operators
};
} // namespace solar
// 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
#define __SOLAR_CBF_CACHE
#include <utility>
#include <map>
#include "esphome.h"
#include "cbf_store_pylon.h"
#include "cbf_cache_item.h"
using namespace esphome;
namespace solar
{
class cbf_cache {
public:
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
cbf_cache() = default;
void clear();
int size() const;
bool hasitem(canid_rtr_t key) const;
bool hasitem(uint32_t can_id, bool remote_transmission_request) const;
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;
// Add a new item to the cache or update an existing one
bool additem(const cbf_store& item);
const cb_frame& get_frame(canid_rtr_t key) const;
const cb_frame& get_frame(uint32_t can_id, bool remote_transmission_request) const;
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);
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
// using default (compiler auto generated) copy and move constructors and assignment operators
};
} // namespace solar
#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
#include "source/solar/cbf_cache_item.h"
namespace solar
{
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->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->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)
{
this->store0 = store0.clone();
this->store1 = store0.clone();
this->store0->id = 1;
this->store1->id = 2;
}
void cbf_cache_item::clear()
{
store0 = nullptr;
store1 = nullptr;
}
void cbf_cache_item::swap(cbf_cache_item &s)
{
std::swap(this->store0, s.store0);
std::swap(this->store1, s.store1);
}
bool cbf_cache_item::update(const cbf_store& newitem)
{
if(!this->store0->is_valid()) {
store0 = newitem.clone();
store0->id = 1;
return true;
}
if(!this->store1->is_valid()) {
store1 = newitem.clone();
store1->id = 2;
return true;
}
bool result = store1->last_timestamp > store0->last_timestamp;
if(result) {
store0 = newitem.clone();
this->store0->id = 1;
}
else {
store1 = newitem.clone();
this->store1->id = 2;
}
return result;
}
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;
switch(store_selector) {
case 2:
result = this->store1->update(newitem, comparecolumns);
break;
default:
result = this->store0->update(newitem, comparecolumns);
break;
}
return result;
}
const cb_frame& cbf_cache_item::get_frame() const
{
return get_store(); // return most recent valid store's frame
}
const cbf_store& cbf_cache_item::get_store() const
{
if(this->store0->is_valid() && !this->store1->is_valid()) {
return *store0;
}
if(!this->store0->is_valid() && this->store1->is_valid()) {
return *store1;
}
if(this->store0->is_valid() && this->store1->is_valid()) {
if(this->store0->last_timestamp >= this->store1->last_timestamp) {
return *this->store0;
} else {
return *this->store1;
}
}
return *this->store0; // no valid stores, but return store0 which is always present
}
std::string cbf_cache_item::tag(const std::string& prefix) const
{
return get_store().tag(prefix, prefix.length());
}
std::string cbf_cache_item::to_string() const
{
return get_store().to_string();
}
std::string cbf_cache_item::tag0(const std::string& prefix) const
{
return store0->tag(prefix);
}
std::string cbf_cache_item::tag1(const std::string& prefix) const
{
return store1->tag(prefix);
}
std::string cbf_cache_item::st0_tostring() const
{
char buffer[150];
snprintf(buffer, sizeof(buffer), "ST1: [%s] %s", trim(tag0()).c_str(), store0->to_string().c_str());
return std::string(buffer);
}
std::string cbf_cache_item::st1_tostring() const
{
char buffer[150];
snprintf(buffer, sizeof(buffer), "ST2: [%s] %s", trim(tag1()).c_str(), store1->to_string().c_str());
return std::string(buffer);
}
} // namespace 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"
namespace solar
{
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->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->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)
{
this->store0 = store0.clone();
this->store1 = store0.clone();
this->store0->id = 1;
this->store1->id = 2;
}
void cbf_cache_item::clear()
{
store0 = nullptr;
store1 = nullptr;
}
void cbf_cache_item::swap(cbf_cache_item &s)
{
std::swap(this->store0, s.store0);
std::swap(this->store1, s.store1);
}
bool cbf_cache_item::update(const cbf_store& newitem)
{
if(!this->store0->is_valid()) {
store0 = newitem.clone();
store0->id = 1;
return true;
}
if(!this->store1->is_valid()) {
store1 = newitem.clone();
store1->id = 2;
return true;
}
bool result = store1->last_timestamp > store0->last_timestamp;
if(result) {
store0 = newitem.clone();
this->store0->id = 1;
}
else {
store1 = newitem.clone();
this->store1->id = 2;
}
return result;
}
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;
switch(store_selector) {
case 2:
result = this->store1->update(newitem, comparecolumns);
break;
default:
result = this->store0->update(newitem, comparecolumns);
break;
}
return result;
}
const cb_frame& cbf_cache_item::get_frame() const
{
return get_store(); // return most recent valid store's frame
}
const cbf_store& cbf_cache_item::get_store() const
{
if(this->store0->is_valid() && !this->store1->is_valid()) {
return *store0;
}
if(!this->store0->is_valid() && this->store1->is_valid()) {
return *store1;
}
if(this->store0->is_valid() && this->store1->is_valid()) {
if(this->store0->last_timestamp >= this->store1->last_timestamp) {
return *this->store0;
} else {
return *this->store1;
}
}
return *this->store0; // no valid stores, but return store0 which is always present
}
std::string cbf_cache_item::tag(const std::string& prefix) const
{
return get_store().tag(prefix, prefix.length());
}
std::string cbf_cache_item::to_string() const
{
return get_store().to_string();
}
std::string cbf_cache_item::tag0(const std::string& prefix) const
{
return store0->tag(prefix);
}
std::string cbf_cache_item::tag1(const std::string& prefix) const
{
return store1->tag(prefix);
}
std::string cbf_cache_item::st0_tostring() const
{
char buffer[150];
snprintf(buffer, sizeof(buffer), "ST1: [%s] %s", trim(tag0()).c_str(), store0->to_string().c_str());
return std::string(buffer);
}
std::string cbf_cache_item::st1_tostring() const
{
char buffer[150];
snprintf(buffer, sizeof(buffer), "ST2: [%s] %s", trim(tag1()).c_str(), store1->to_string().c_str());
return std::string(buffer);
}
} // namespace solar

View File

@ -1,42 +1,42 @@
#ifndef __SOLAR_CBF_CACHE_ITEM
#define __SOLAR_CBF_CACHE_ITEM
#include <utility>
#include <map>
#include "esphome.h"
#include "cbf_store_pylon.h"
using namespace esphome;
namespace solar
{
class cbf_cache_item {
private:
const int storecount = 2;
public:
std::shared_ptr<cbf_store> store0;
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(const cbf_store& store0);
void clear();
void swap(cbf_cache_item &s);
bool update(const cbf_store& newitem);
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 cbf_store& get_store() const; // returns the most recent valid store
virtual std::string tag0(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 to_string() const;
virtual std::string st0_tostring() const; // string of store 0
virtual std::string st1_tostring() const; // string of store 1
// the default copy and move constructors and assignment operators are fine
// because shared_ptr takes care of the underlying memory management
cbf_cache_item(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& operator=(cbf_cache_item&& src) = default;
virtual ~cbf_cache_item() = default; // virtual destructor for base class
};
} // namespace solar
#ifndef __SOLAR_CBF_CACHE_ITEM
#define __SOLAR_CBF_CACHE_ITEM
#include <utility>
#include <map>
#include "esphome.h"
#include "cbf_store_pylon.h"
using namespace esphome;
namespace solar
{
class cbf_cache_item {
private:
const int storecount = 2;
public:
std::shared_ptr<cbf_store> store0;
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(const cbf_store& store0);
void clear();
void swap(cbf_cache_item &s);
bool update(const cbf_store& newitem);
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 cbf_store& get_store() const; // returns the most recent valid store
virtual std::string tag0(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 to_string() const;
virtual std::string st0_tostring() const; // string of store 0
virtual std::string st1_tostring() const; // string of store 1
// the default copy and move constructors and assignment operators are fine
// because shared_ptr takes care of the underlying memory management
cbf_cache_item(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& operator=(cbf_cache_item&& src) = default;
virtual ~cbf_cache_item() = default; // virtual destructor for base class
};
} // namespace solar
#endif // __SOLAR_CBF_CACHE

View File

@ -1,158 +1,158 @@
#include "cbf_pylon.h"
namespace solar
{
// see common.h for definition of publish_spec_t
// 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::rtr_publish_spec = publish_spec_t(2, 10, 15); // for remote transmission requests
cbf_pylon& cbf_pylon::operator=(cbf_pylon&& src)
{
if (this != &src) {
cb_frame::operator=(std::move(src));
src.clear();
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
}
return *this;
}
void cbf_pylon::clear()
{
cb_frame::clear();
}
// float cbf_pylon::_get_battery_charge_voltage_limit() const
// {
// const auto& x = this->frame;
// return 0.1 * (float)((x[1] << 8) + x[0]);
// }
// Function to build a message from the pylon canbus frame
std::string cbf_pylon::to_string() const
{
char buffer[80];
switch (can_id)
{
case CB_BATTERY_LIMITS:
{
if(rtr) {
return "Request for BATTERY LIMITS info";
}
if (this->frame.size() < 6) {
return "Invalid frame size for CB_BATTERY_LIMITS";
}
const auto& x = this->frame;
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 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);
return buffer;
}
break;
case CB_BATTERY_STATE:
{
if(rtr) {
return "Request for BATTERY STATE info";
}
if (this->frame.size() < 4) {
return "Invalid frame size for CB_BATTERY_STATE";
}
const auto& x = this->frame;
uint soc = static_cast<uint16_t>((x[1] << 8) + x[0]);
uint soh = static_cast<uint16_t>((x[3] << 8) + x[2]);
snprintf(buffer, sizeof(buffer), "BATTERY STATE: SOC= %d%% SOH= %d%%", soc, soh);
return buffer;
}
break;
case CB_BATTERY_STATUS:
{
if(rtr) {
return "Request for BATTERY STATUS info";
}
if (this->frame.size() < 6) {
return "Invalid frame size for CB_BATTERY_STATUS";
}
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_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
snprintf(buffer, sizeof(buffer), "BATTERY STATUS: VSYS= %.2fV ISYS= %.1fA TSYS= %.1f°C", system_voltage, system_current, average_cell_temperature);
return buffer;
}
break;
case CB_BATTERY_FAULT:
{
if(rtr) {
return "Request for BATTERY FAULT info";
}
if (this->frame.size() < 8) {
return "Invalid frame size for CB_BATTERY_FAULT";
}
const auto& x = this->frame;
uint8_t protection1 = x[0];
uint8_t protection2 = x[1];
uint8_t alarm1 = x[2];
uint8_t alarm2 = x[3];
uint8_t module_numbers = x[4];
char ch5 = x[5];
char ch6 = x[6];
bool discharge_over_current = protection1 & 0x80;
bool cell_under_temperature = protection1 & 0x10;
bool cell_over_temperature = protection1 & 0x08;
bool cell_or_module_under_voltage = protection1 & 0x04;
bool cell_or_module_over_voltage = protection1 & 0x02;
bool system_error = protection2 & 0x8;
bool charge_over_current = protection2 & 0x01;
bool discharge_high_current = alarm1 & 0x80;
bool cell_low_temperature = alarm1 & 0x10;
bool cell_high_temperature = alarm1 & 0x08;
bool cell_or_module_low_voltage = alarm1 & 0x04;
bool cell_or_module_high_voltage = alarm1 & 0x02;
bool internal_communication_fail = alarm2 & 0x8;
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);
return buffer;
}
break;
case CB_BATTERY_REQUEST_FLAGS:
{
if(rtr) {
return "Request for BATTERY REQUEST FLAGS info";
}
if (this->frame.size() < 1) {
return "Invalid frame size for CB_BATTERY_REQUEST_FLAGS";
}
const auto& x = this->frame;
uint8_t request_flag = x[0];
bool charge_enable = request_flag & 0x80;
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_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.
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;
}
break;
case CB_BATTERY_MANUFACTURER:
{
if(rtr) {
return "Request for BATTERY MANUFACTURER info";
}
if (this->frame.size() < 8) {
return "Invalid frame size for CB_BATTERY_MANUFACTURER";
}
const auto& x = this->frame;
// Manufacturer name is in the first 8 bytes, padded with spaces
// Convert to string and trim trailing spaces
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());
return "BATTERY OEM: " + manufacturer;
}
break;
}
if(rtr) {
return "Request for unknown pylon CAN ID";
}
return "Unknown CAN ID";
}
#include "cbf_pylon.h"
namespace solar
{
// see common.h for definition of publish_spec_t
// 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::rtr_publish_spec = publish_spec_t(2, 10, 15); // for remote transmission requests
cbf_pylon& cbf_pylon::operator=(cbf_pylon&& src)
{
if (this != &src) {
cb_frame::operator=(std::move(src));
src.clear();
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
}
return *this;
}
void cbf_pylon::clear()
{
cb_frame::clear();
}
// float cbf_pylon::_get_battery_charge_voltage_limit() const
// {
// const auto& x = this->frame;
// return 0.1 * (float)((x[1] << 8) + x[0]);
// }
// Function to build a message from the pylon canbus frame
std::string cbf_pylon::to_string() const
{
char buffer[80];
switch (can_id)
{
case CB_BATTERY_LIMITS:
{
if(rtr) {
return "Request for BATTERY LIMITS info";
}
if (this->frame.size() < 6) {
return "Invalid frame size for CB_BATTERY_LIMITS";
}
const auto& x = this->frame;
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 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);
return buffer;
}
break;
case CB_BATTERY_STATE:
{
if(rtr) {
return "Request for BATTERY STATE info";
}
if (this->frame.size() < 4) {
return "Invalid frame size for CB_BATTERY_STATE";
}
const auto& x = this->frame;
uint soc = static_cast<uint16_t>((x[1] << 8) + x[0]);
uint soh = static_cast<uint16_t>((x[3] << 8) + x[2]);
snprintf(buffer, sizeof(buffer), "BATTERY STATE: SOC= %d%% SOH= %d%%", soc, soh);
return buffer;
}
break;
case CB_BATTERY_STATUS:
{
if(rtr) {
return "Request for BATTERY STATUS info";
}
if (this->frame.size() < 6) {
return "Invalid frame size for CB_BATTERY_STATUS";
}
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_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
snprintf(buffer, sizeof(buffer), "BATTERY STATUS: VSYS= %.2fV ISYS= %.1fA TSYS= %.1f°C", system_voltage, system_current, average_cell_temperature);
return buffer;
}
break;
case CB_BATTERY_FAULT:
{
if(rtr) {
return "Request for BATTERY FAULT info";
}
if (this->frame.size() < 8) {
return "Invalid frame size for CB_BATTERY_FAULT";
}
const auto& x = this->frame;
uint8_t protection1 = x[0];
uint8_t protection2 = x[1];
uint8_t alarm1 = x[2];
uint8_t alarm2 = x[3];
uint8_t module_numbers = x[4];
char ch5 = x[5];
char ch6 = x[6];
bool discharge_over_current = protection1 & 0x80;
bool cell_under_temperature = protection1 & 0x10;
bool cell_over_temperature = protection1 & 0x08;
bool cell_or_module_under_voltage = protection1 & 0x04;
bool cell_or_module_over_voltage = protection1 & 0x02;
bool system_error = protection2 & 0x8;
bool charge_over_current = protection2 & 0x01;
bool discharge_high_current = alarm1 & 0x80;
bool cell_low_temperature = alarm1 & 0x10;
bool cell_high_temperature = alarm1 & 0x08;
bool cell_or_module_low_voltage = alarm1 & 0x04;
bool cell_or_module_high_voltage = alarm1 & 0x02;
bool internal_communication_fail = alarm2 & 0x8;
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);
return buffer;
}
break;
case CB_BATTERY_REQUEST_FLAGS:
{
if(rtr) {
return "Request for BATTERY REQUEST FLAGS info";
}
if (this->frame.size() < 1) {
return "Invalid frame size for CB_BATTERY_REQUEST_FLAGS";
}
const auto& x = this->frame;
uint8_t request_flag = x[0];
bool charge_enable = request_flag & 0x80;
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_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.
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;
}
break;
case CB_BATTERY_MANUFACTURER:
{
if(rtr) {
return "Request for BATTERY MANUFACTURER info";
}
if (this->frame.size() < 8) {
return "Invalid frame size for CB_BATTERY_MANUFACTURER";
}
const auto& x = this->frame;
// Manufacturer name is in the first 8 bytes, padded with spaces
// Convert to string and trim trailing spaces
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());
return "BATTERY OEM: " + manufacturer;
}
break;
}
if(rtr) {
return "Request for unknown pylon CAN ID";
}
return "Unknown CAN ID";
}
} // namespace solar

View File

@ -1,40 +1,40 @@
#ifndef __SOLAR_CBF_PYLON // include GUARD
#define __SOLAR_CBF_PYLON
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cb_frame.h"
using namespace esphome;
namespace solar
{
class cbf_pylon : virtual public cb_frame {
public:
// Pylontech publish spec
// 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 rtr_publish_spec; // for remote transmission requests
// Pylontech battery CAN IDs
// 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
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_STATUS = 0x356; // VERIFIED: Pylontech battery status 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_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<float, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_get_battery_charge_voltage_limit};
cbf_pylon() = default;
cbf_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
void clear();
virtual std::string to_string() const override;
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
};
} // namespace solar
#ifndef __SOLAR_CBF_PYLON // include GUARD
#define __SOLAR_CBF_PYLON
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cb_frame.h"
using namespace esphome;
namespace solar
{
class cbf_pylon : virtual public cb_frame {
public:
// Pylontech publish spec
// 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 rtr_publish_spec; // for remote transmission requests
// Pylontech battery CAN IDs
// 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
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_STATUS = 0x356; // VERIFIED: Pylontech battery status 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_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<float, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_get_battery_charge_voltage_limit};
cbf_pylon() = default;
cbf_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
void clear();
virtual std::string to_string() const override;
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
};
} // namespace solar
#endif // __SOLAR_CBF_PYLON

View File

@ -1,342 +1,342 @@
#include "cbf_sthome.h"
namespace solar
{
// see common.h for definition of publish_spec_t
// 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::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
//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)
// {
// }
cbf_sthome& cbf_sthome::operator=(cbf_sthome&& src)
{
if (this != &src) {
cb_frame::operator=(std::move(src));
src.clear();
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
}
return *this;
}
void cbf_sthome::clear()
{
cb_frame::clear();
}
bool cbf_sthome::verify_heartbeat() const
{
const auto& x = this->frame;
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());
return hb == heartbeat;
}
// float cbf_sthome::_get_battery_charge_voltage_limit() const
// {
// const auto& x = this->frame;
// return 0.1 * (float)((x[1] << 8) + x[0]);
// }
// Function to build a message from the pylon canbus frame
std::string cbf_sthome::to_string() const
{
char buffer[128];
const auto& x = this->frame;
switch (can_id)
{
case CB_CANBUS_ID01:
return rtr ? "Request for sthome-ut1 heartbeat" : verify_heartbeat() ? "sthome-ut1 alive" : "";
case CB_CANBUS_ID02:
return rtr ? "Request for sthome-ut2 heartbeat" : verify_heartbeat() ? "sthome-ut2 alive" : "";
case CB_CANBUS_ID03:
return rtr ? "Request for sthome-u3 heartbeat" : verify_heartbeat() ? "sthome-ut3 alive " : "";
case CB_CANBUS_ID04:
return rtr ? "Request for sthome-ut4 heartbeat" : verify_heartbeat() ? "sthome-ut4 alive" : "";
case CB_CANBUS_ID05:
return rtr ? "Request for sthome-ut5 heartbeat" : verify_heartbeat() ? "sthome-ut5 alive" : "";
case CB_CANBUS_ID06:
return rtr ? "Request for sthome-ut6 heartbeat" : verify_heartbeat() ? "sthome-ut6 alive" : "";
case CB_CANBUS_ID07:
return rtr ? "Request for sthome-ut7 heartbeat" : verify_heartbeat() ? "sthome-ut7 alive" : "";
case CB_CANBUS_ID08:
return rtr ? "Request for sthome-ut8 heartbeat" : verify_heartbeat() ? "sthome-ut8 alive" : "";
case CB_CANBUS_ID09:
return rtr ? "Request for sthome-ut9 heartbeat" : verify_heartbeat() ? "sthome-ut9 alive" : "";
case CB_CANBUS_ID10:
return rtr ? "Request for sthome-ut10 heartbeat" : verify_heartbeat() ? "sthome-ut10 alive" : "";
case CB_GEYSER_TEMPERATURE_TOP:
{
if(rtr) {
return "Request for geyser top temp";
}
double temp_top = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER TOP: %.2f°C", temp_top);
return buffer;
}
case CB_GEYSER_TEMPERATURE_BOTTOM:
{
if(rtr) {
return "Request for geyser bottom temp";
}
double temp_bot = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER BOT: %.2f°C", temp_bot);
return buffer;
}
case CB_GEYSER_TEMPERATURE_AMBIENT:
{
if(rtr) {
return "Request for geyser ambient temp";
}
double temp_amb = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER AMB: %.2f°C", temp_amb);
return buffer;
}
case CB_GEYSER_HEATING:
{
if(rtr) {
return "Request for geyser heating";
}
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 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
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;
}
case CB_GEYSER_ACTIVE_SCHEDULE:
{
if(rtr) {
return "Request for geyser schedule";
}
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
double heating_overshoot_time = get_double(x[4], x[5], -64); // -512 to 512s
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);
return buffer;
}
case CB_POWER_MAINS:
{
if(rtr) {
return "Request for power mains";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Mains : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_INVERTER:
{
if(rtr) {
return "Request for power inverter";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "InvOut: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_PLUGS:
{
if(rtr) {
return "Request for power plugs";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Plugs : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_LIGHTS:
{
if(rtr) {
return "Request for power lights";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Lights: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_GEYSER:
{
if(rtr) {
return "Request for power geyser";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Geyser: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_POOL:
{
if(rtr) {
return "Request for power pool";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Pool : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_GENERATED:
{
if(rtr) {
return "Request for power generated";
}
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
snprintf (buffer, sizeof(buffer), "Power : Generated %.3fkW, Loss %.3fkW", power_generated, power_loss);
return buffer;
}
case CB_ENERGY_MAINS:
{
if(rtr) {
return "Request for energy mains";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_GEYSER:
{
if(rtr) {
return "Request for energy geyser";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_POOL:
{
if(rtr) {
return "Request for energy pool";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_PLUGS:
{
if(rtr) {
return "Request for energy plugs";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_LIGHTS:
{
if(rtr) {
return "Request for energy lights";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_HOUSE:
{
if(rtr) {
return "Request for energy house";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_GENERATED:
{
if(rtr) {
return "Request for energy generated";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_LOSS:
{
if(rtr) {
return "Request for energy loss";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_CONTROLLER_STATES:
{
if(rtr) {
return "Request for controller states";
}
uint8_t alarms = x[0];
uint8_t states = x[1];
uint8_t modes = x[2];
bool inverter_1_battery_low = alarms & 0x80;
bool inverter_2_battery_low = alarms & 0x10;
bool inverter_overload = alarms & 0x08;
bool geyser_heating = states & 0x80;
bool geyser_energised = states & 0x40;
bool mains_supply_present = states & 0x20;
bool battery_charging = states & 0x08;
bool vacation_mode = modes & 0x80; // nobody at home
std::string mode = "";
int geysermode = (modes & 0x70) >> 4;
switch (geysermode) {
case GM_SUNDAY:
mode = "SUN";
break;
case GM_WORKDAY:
mode = "WRK";
break;
case GM_SATURDAY:
mode = "SAT";
break;
case GM_PUBLIC_HOLIDAY:
mode = "PUB";
break;
case GM_SCHOOL_HOLIDAY:
mode = "SCH";
break;
default:
mode = "UNK";
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 " : "");
return buffer;
}
}
if(rtr) {
return "Request for unknown sthome CAN ID";
}
return "Unknown CAN ID";
}
#include "cbf_sthome.h"
namespace solar
{
// see common.h for definition of publish_spec_t
// 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::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
//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)
// {
// }
cbf_sthome& cbf_sthome::operator=(cbf_sthome&& src)
{
if (this != &src) {
cb_frame::operator=(std::move(src));
src.clear();
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
}
return *this;
}
void cbf_sthome::clear()
{
cb_frame::clear();
}
bool cbf_sthome::verify_heartbeat() const
{
const auto& x = this->frame;
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());
return hb == heartbeat;
}
// float cbf_sthome::_get_battery_charge_voltage_limit() const
// {
// const auto& x = this->frame;
// return 0.1 * (float)((x[1] << 8) + x[0]);
// }
// Function to build a message from the pylon canbus frame
std::string cbf_sthome::to_string() const
{
char buffer[128];
const auto& x = this->frame;
switch (can_id)
{
case CB_CANBUS_ID01:
return rtr ? "Request for sthome-ut1 heartbeat" : verify_heartbeat() ? "sthome-ut1 alive" : "";
case CB_CANBUS_ID02:
return rtr ? "Request for sthome-ut2 heartbeat" : verify_heartbeat() ? "sthome-ut2 alive" : "";
case CB_CANBUS_ID03:
return rtr ? "Request for sthome-u3 heartbeat" : verify_heartbeat() ? "sthome-ut3 alive " : "";
case CB_CANBUS_ID04:
return rtr ? "Request for sthome-ut4 heartbeat" : verify_heartbeat() ? "sthome-ut4 alive" : "";
case CB_CANBUS_ID05:
return rtr ? "Request for sthome-ut5 heartbeat" : verify_heartbeat() ? "sthome-ut5 alive" : "";
case CB_CANBUS_ID06:
return rtr ? "Request for sthome-ut6 heartbeat" : verify_heartbeat() ? "sthome-ut6 alive" : "";
case CB_CANBUS_ID07:
return rtr ? "Request for sthome-ut7 heartbeat" : verify_heartbeat() ? "sthome-ut7 alive" : "";
case CB_CANBUS_ID08:
return rtr ? "Request for sthome-ut8 heartbeat" : verify_heartbeat() ? "sthome-ut8 alive" : "";
case CB_CANBUS_ID09:
return rtr ? "Request for sthome-ut9 heartbeat" : verify_heartbeat() ? "sthome-ut9 alive" : "";
case CB_CANBUS_ID10:
return rtr ? "Request for sthome-ut10 heartbeat" : verify_heartbeat() ? "sthome-ut10 alive" : "";
case CB_GEYSER_TEMPERATURE_TOP:
{
if(rtr) {
return "Request for geyser top temp";
}
double temp_top = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER TOP: %.2f°C", temp_top);
return buffer;
}
case CB_GEYSER_TEMPERATURE_BOTTOM:
{
if(rtr) {
return "Request for geyser bottom temp";
}
double temp_bot = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER BOT: %.2f°C", temp_bot);
return buffer;
}
case CB_GEYSER_TEMPERATURE_AMBIENT:
{
if(rtr) {
return "Request for geyser ambient temp";
}
double temp_amb = get_double(x[0], x[1], 256, true);
snprintf (buffer, sizeof(buffer), "GEYSER AMB: %.2f°C", temp_amb);
return buffer;
}
case CB_GEYSER_HEATING:
{
if(rtr) {
return "Request for geyser heating";
}
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 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
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;
}
case CB_GEYSER_ACTIVE_SCHEDULE:
{
if(rtr) {
return "Request for geyser schedule";
}
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
double heating_overshoot_time = get_double(x[4], x[5], -64); // -512 to 512s
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);
return buffer;
}
case CB_POWER_MAINS:
{
if(rtr) {
return "Request for power mains";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Mains : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_INVERTER:
{
if(rtr) {
return "Request for power inverter";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "InvOut: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_PLUGS:
{
if(rtr) {
return "Request for power plugs";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Plugs : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_LIGHTS:
{
if(rtr) {
return "Request for power lights";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Lights: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_GEYSER:
{
if(rtr) {
return "Request for power geyser";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Geyser: %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_POOL:
{
if(rtr) {
return "Request for power pool";
}
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 current = get_double(x[4], x[5], 512); // 0 to 128A
snprintf (buffer, sizeof(buffer), "Pool : %.3fV, %.3fA, %.3fkW", voltage, current, power);
return buffer;
}
case CB_POWER_GENERATED:
{
if(rtr) {
return "Request for power generated";
}
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
snprintf (buffer, sizeof(buffer), "Power : Generated %.3fkW, Loss %.3fkW", power_generated, power_loss);
return buffer;
}
case CB_ENERGY_MAINS:
{
if(rtr) {
return "Request for energy mains";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_GEYSER:
{
if(rtr) {
return "Request for energy geyser";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_POOL:
{
if(rtr) {
return "Request for energy pool";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_PLUGS:
{
if(rtr) {
return "Request for energy plugs";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_LIGHTS:
{
if(rtr) {
return "Request for energy lights";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_HOUSE:
{
if(rtr) {
return "Request for energy house";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_GENERATED:
{
if(rtr) {
return "Request for energy generated";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_ENERGY_LOSS:
{
if(rtr) {
return "Request for energy loss";
}
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_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
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);
return buffer;
}
case CB_CONTROLLER_STATES:
{
if(rtr) {
return "Request for controller states";
}
uint8_t alarms = x[0];
uint8_t states = x[1];
uint8_t modes = x[2];
bool inverter_1_battery_low = alarms & 0x80;
bool inverter_2_battery_low = alarms & 0x10;
bool inverter_overload = alarms & 0x08;
bool geyser_heating = states & 0x80;
bool geyser_energised = states & 0x40;
bool mains_supply_present = states & 0x20;
bool battery_charging = states & 0x08;
bool vacation_mode = modes & 0x80; // nobody at home
std::string mode = "";
int geysermode = (modes & 0x70) >> 4;
switch (geysermode) {
case GM_SUNDAY:
mode = "SUN";
break;
case GM_WORKDAY:
mode = "WRK";
break;
case GM_SATURDAY:
mode = "SAT";
break;
case GM_PUBLIC_HOLIDAY:
mode = "PUB";
break;
case GM_SCHOOL_HOLIDAY:
mode = "SCH";
break;
default:
mode = "UNK";
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 " : "");
return buffer;
}
}
if(rtr) {
return "Request for unknown sthome CAN ID";
}
return "Unknown CAN ID";
}
} // namespace solar

View File

@ -1,78 +1,78 @@
#ifndef __SOLAR_CBF_STHOME_H // include GUARD
#define __SOLAR_CBF_STHOME_H
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cb_frame.h"
using namespace esphome;
namespace solar
{
class cbf_sthome : virtual public cb_frame {
private:
// float _get_battery_charge_voltage_limit() const;
public:
// STHOME publish spec
// 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 rtr_publish_spec; // for remote transmission requests
enum geyser_modes : int {
GM_SUNDAY,
GM_WORKDAY,
GM_SATURDAY,
GM_PUBLIC_HOLIDAY,
GM_SCHOOL_HOLIDAY,
};
enum canbus_ids : int {
CB_CANBUS_ID01 = 0x501,
CB_CANBUS_ID02,
CB_CANBUS_ID03,
CB_CANBUS_ID04,
CB_CANBUS_ID05,
CB_CANBUS_ID06,
CB_CANBUS_ID07,
CB_CANBUS_ID08,
CB_CANBUS_ID09,
CB_CANBUS_ID10,
CB_POWER_MAINS = 0x401,
CB_POWER_INVERTER,
CB_POWER_PLUGS,
CB_POWER_LIGHTS,
CB_POWER_GEYSER,
CB_POWER_POOL,
CB_POWER_GENERATED,
CB_ENERGY_MAINS,
CB_ENERGY_GEYSER,
CB_ENERGY_POOL,
CB_ENERGY_PLUGS,
CB_ENERGY_LIGHTS,
CB_ENERGY_HOUSE,
CB_ENERGY_GENERATED,
CB_ENERGY_LOSS,
CB_CONTROLLER_STATES,
CB_GEYSER_TEMPERATURE_TOP,
CB_GEYSER_TEMPERATURE_BOTTOM,
CB_GEYSER_TEMPERATURE_AMBIENT,
CB_GEYSER_HEATING,
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<float, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_get_battery_charge_voltage_limit};
static const std::string heartbeat;
cbf_sthome() = default;
cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
void clear();
virtual std::string to_string() const override;
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
virtual ~cbf_sthome() = default;
bool verify_heartbeat() const;
};
} // namespace solar
#ifndef __SOLAR_CBF_STHOME_H // include GUARD
#define __SOLAR_CBF_STHOME_H
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cb_frame.h"
using namespace esphome;
namespace solar
{
class cbf_sthome : virtual public cb_frame {
private:
// float _get_battery_charge_voltage_limit() const;
public:
// STHOME publish spec
// 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 rtr_publish_spec; // for remote transmission requests
enum geyser_modes : int {
GM_SUNDAY,
GM_WORKDAY,
GM_SATURDAY,
GM_PUBLIC_HOLIDAY,
GM_SCHOOL_HOLIDAY,
};
enum canbus_ids : int {
CB_CANBUS_ID01 = 0x501,
CB_CANBUS_ID02,
CB_CANBUS_ID03,
CB_CANBUS_ID04,
CB_CANBUS_ID05,
CB_CANBUS_ID06,
CB_CANBUS_ID07,
CB_CANBUS_ID08,
CB_CANBUS_ID09,
CB_CANBUS_ID10,
CB_POWER_MAINS = 0x401,
CB_POWER_INVERTER,
CB_POWER_PLUGS,
CB_POWER_LIGHTS,
CB_POWER_GEYSER,
CB_POWER_POOL,
CB_POWER_GENERATED,
CB_ENERGY_MAINS,
CB_ENERGY_GEYSER,
CB_ENERGY_POOL,
CB_ENERGY_PLUGS,
CB_ENERGY_LIGHTS,
CB_ENERGY_HOUSE,
CB_ENERGY_GENERATED,
CB_ENERGY_LOSS,
CB_CONTROLLER_STATES,
CB_GEYSER_TEMPERATURE_TOP,
CB_GEYSER_TEMPERATURE_BOTTOM,
CB_GEYSER_TEMPERATURE_AMBIENT,
CB_GEYSER_HEATING,
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<float, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_get_battery_charge_voltage_limit};
static const std::string heartbeat;
cbf_sthome() = default;
cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
void clear();
virtual std::string to_string() const override;
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
virtual ~cbf_sthome() = default;
bool verify_heartbeat() const;
};
} // namespace solar
#endif // __SOLAR_CBF_STHOME_H

View File

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

View File

@ -1,71 +1,71 @@
#include "cbf_store_pylon.h"
namespace solar
{
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)
{
this->id = 0;
this->count = 0;
// 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)
: 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());
}
cbf_store_pylon& cbf_store_pylon::operator=(const cbf_store_pylon& b)
{
if (this != &b) {
cbf_store_pylon tmp(b);
swap(tmp);
// ESP_LOGI(tag("store_pylon AOPP").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
}
return *this;
}
cbf_store_pylon::cbf_store_pylon(cbf_store_pylon&& src)
: cb_frame(src), cbf_store(src)
{
src.clear();
// 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)
{
if (this != &src) {
cbf_store::operator=(std::move(src));
cb_frame::operator=(std::move(src));
src.clear();
// ESP_LOGI(tag("store_pylon MOASS").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
}
return *this;
}
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
}
std::shared_ptr<cbf_store> cbf_store_pylon::clone_impl() const
{
//ESP_LOGI(tag("store_pylon CLONE").c_str(), "%-20s", "Cloning store_pylon");
return std::make_shared<cbf_store_pylon>(*this);
}
void cbf_store_pylon::clear()
{
cb_frame::clear();
cbf_store::clear();
}
void cbf_store_pylon::swap(cbf_store_pylon &s)
{
cb_frame::swap(s);
cbf_store::swap(s);
}
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());
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
}
std::string cbf_store_pylon::to_string() const
{
return cb_frame::to_string() + " " + cbf_pylon::to_string();
}
#include "cbf_store_pylon.h"
namespace solar
{
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)
{
this->id = 0;
this->count = 0;
// 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)
: 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());
}
cbf_store_pylon& cbf_store_pylon::operator=(const cbf_store_pylon& b)
{
if (this != &b) {
cbf_store_pylon tmp(b);
swap(tmp);
// ESP_LOGI(tag("store_pylon AOPP").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
}
return *this;
}
cbf_store_pylon::cbf_store_pylon(cbf_store_pylon&& src)
: cb_frame(src), cbf_store(src)
{
src.clear();
// 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)
{
if (this != &src) {
cbf_store::operator=(std::move(src));
cb_frame::operator=(std::move(src));
src.clear();
// ESP_LOGI(tag("store_pylon MOASS").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
}
return *this;
}
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
}
std::shared_ptr<cbf_store> cbf_store_pylon::clone_impl() const
{
//ESP_LOGI(tag("store_pylon CLONE").c_str(), "%-20s", "Cloning store_pylon");
return std::make_shared<cbf_store_pylon>(*this);
}
void cbf_store_pylon::clear()
{
cb_frame::clear();
cbf_store::clear();
}
void cbf_store_pylon::swap(cbf_store_pylon &s)
{
cb_frame::swap(s);
cbf_store::swap(s);
}
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());
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
}
std::string cbf_store_pylon::to_string() const
{
return cb_frame::to_string() + " " + cbf_pylon::to_string();
}
} // namespace solar

View File

@ -1,34 +1,34 @@
#ifndef __SOLAR_CBF_STORE_PYLON // include GUARD
#define __SOLAR_CBF_STORE_PYLON
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cbf_store.h"
#include "cbf_pylon.h"
using namespace esphome;
namespace solar
{
class cbf_store_pylon : public cbf_store, public cbf_pylon
{
public:
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);
virtual ~cbf_store_pylon() = default; // virtual destructor for base class
std::shared_ptr<cbf_store_pylon> clone() const;
void clear();
void swap(cbf_store_pylon &s);
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
virtual std::string to_string() const override;
cbf_store_pylon(const cbf_store_pylon& b);
cbf_store_pylon& operator=(const cbf_store_pylon& b);
cbf_store_pylon(cbf_store_pylon&& src);
cbf_store_pylon& operator=(cbf_store_pylon&& src);
private:
virtual std::shared_ptr<cbf_store> clone_impl() const override;
};
} // namespace solar
#ifndef __SOLAR_CBF_STORE_PYLON // include GUARD
#define __SOLAR_CBF_STORE_PYLON
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cbf_store.h"
#include "cbf_pylon.h"
using namespace esphome;
namespace solar
{
class cbf_store_pylon : public cbf_store, public cbf_pylon
{
public:
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);
virtual ~cbf_store_pylon() = default; // virtual destructor for base class
std::shared_ptr<cbf_store_pylon> clone() const;
void clear();
void swap(cbf_store_pylon &s);
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
virtual std::string to_string() const override;
cbf_store_pylon(const cbf_store_pylon& b);
cbf_store_pylon& operator=(const cbf_store_pylon& b);
cbf_store_pylon(cbf_store_pylon&& src);
cbf_store_pylon& operator=(cbf_store_pylon&& src);
private:
virtual std::shared_ptr<cbf_store> clone_impl() const override;
};
} // namespace solar
#endif // __SOLAR_CBF_STORE_PYLON

View File

@ -1,71 +1,71 @@
#include "cbf_store_sthome.h"
namespace solar
{
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)
{
this->id = 0;
this->count = 0;
// 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)
: 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());
}
cbf_store_sthome& cbf_store_sthome::operator=(const cbf_store_sthome& b)
{
if (this != &b) {
cbf_store_sthome tmp(b);
swap(tmp);
// ESP_LOGI(tag("store_sthome AOPP").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
}
return *this;
}
cbf_store_sthome::cbf_store_sthome(cbf_store_sthome&& src)
: cb_frame(src), cbf_store(src)
{
src.clear();
// 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)
{
if (this != &src) {
cbf_store::operator=(std::move(src));
cb_frame::operator=(std::move(src));
src.clear();
// ESP_LOGI(tag("store_sthome MOASS").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
}
return *this;
}
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
}
std::shared_ptr<cbf_store> cbf_store_sthome::clone_impl() const
{
//ESP_LOGI(tag("store_sthome CLONE").c_str(), "%-20s", "Cloning store_sthome");
return std::make_shared<cbf_store_sthome>(*this);
}
void cbf_store_sthome::clear()
{
cb_frame::clear();
cbf_store::clear();
}
void cbf_store_sthome::swap(cbf_store_sthome &s)
{
cb_frame::swap(s);
cbf_store::swap(s);
}
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());
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
}
std::string cbf_store_sthome::to_string() const
{
return cb_frame::to_string() + " " + cbf_sthome::to_string();
}
#include "cbf_store_sthome.h"
namespace solar
{
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)
{
this->id = 0;
this->count = 0;
// 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)
: 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());
}
cbf_store_sthome& cbf_store_sthome::operator=(const cbf_store_sthome& b)
{
if (this != &b) {
cbf_store_sthome tmp(b);
swap(tmp);
// ESP_LOGI(tag("store_sthome AOPP").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
}
return *this;
}
cbf_store_sthome::cbf_store_sthome(cbf_store_sthome&& src)
: cb_frame(src), cbf_store(src)
{
src.clear();
// 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)
{
if (this != &src) {
cbf_store::operator=(std::move(src));
cb_frame::operator=(std::move(src));
src.clear();
// ESP_LOGI(tag("store_sthome MOASS").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
}
return *this;
}
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
}
std::shared_ptr<cbf_store> cbf_store_sthome::clone_impl() const
{
//ESP_LOGI(tag("store_sthome CLONE").c_str(), "%-20s", "Cloning store_sthome");
return std::make_shared<cbf_store_sthome>(*this);
}
void cbf_store_sthome::clear()
{
cb_frame::clear();
cbf_store::clear();
}
void cbf_store_sthome::swap(cbf_store_sthome &s)
{
cb_frame::swap(s);
cbf_store::swap(s);
}
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());
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
}
std::string cbf_store_sthome::to_string() const
{
return cb_frame::to_string() + " " + cbf_sthome::to_string();
}
} // namespace solar

View File

@ -1,34 +1,34 @@
#ifndef __SOLAR_CBF_STORE_STHOME_H // include GUARD
#define __SOLAR_CBF_STORE_STHOME_H
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cbf_store.h"
#include "cbf_sthome.h"
using namespace esphome;
namespace solar
{
class cbf_store_sthome : public cbf_store, public cbf_sthome
{
public:
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);
virtual ~cbf_store_sthome() = default; // virtual destructor for base class
std::shared_ptr<cbf_store_sthome> clone() const;
void clear();
void swap(cbf_store_sthome &s);
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
virtual std::string to_string() const override;
cbf_store_sthome(const cbf_store_sthome& b);
cbf_store_sthome& operator=(const cbf_store_sthome& b);
cbf_store_sthome(cbf_store_sthome&& src);
cbf_store_sthome& operator=(cbf_store_sthome&& src);
private:
virtual std::shared_ptr<cbf_store> clone_impl() const override;
};
} // namespace solar
#ifndef __SOLAR_CBF_STORE_STHOME_H // include GUARD
#define __SOLAR_CBF_STORE_STHOME_H
#include "esphome.h"
#include <utility>
#include "common.h"
#include "cbf_store.h"
#include "cbf_sthome.h"
using namespace esphome;
namespace solar
{
class cbf_store_sthome : public cbf_store, public cbf_sthome
{
public:
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);
virtual ~cbf_store_sthome() = default; // virtual destructor for base class
std::shared_ptr<cbf_store_sthome> clone() const;
void clear();
void swap(cbf_store_sthome &s);
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
virtual std::string to_string() const override;
cbf_store_sthome(const cbf_store_sthome& b);
cbf_store_sthome& operator=(const cbf_store_sthome& b);
cbf_store_sthome(cbf_store_sthome&& src);
cbf_store_sthome& operator=(cbf_store_sthome&& src);
private:
virtual std::shared_ptr<cbf_store> clone_impl() const override;
};
} // namespace solar
#endif // __SOLAR_CBF_STORE_STHOME_H

View File

@ -1,48 +1,48 @@
#ifndef __SOLAR_COMMON // include GUARD
#define __SOLAR_COMMON
namespace solar
{
#define FLAGBITS ~0x0FFF // bitwise one's complement (sort column info fits into lower three nibbles)
#define SORTCASE 0x0001000 // case sensitive
#define SORTREVERSE 0x0002000 // reverse order
#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 SEARCHWILD 0x0100000 // do wildcard comparison (only searchkey should contain wildcards)
#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 max(a, b) ((a) > (b)) ? (a) : (b)
#define min(a, b) ((a) < (b)) ? (a) : (b)
typedef unsigned int uint;
typedef std::vector<uint8_t> byte_vector;
struct publish_spec_t
{
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 timeout; // used for checking whether timeout has expired, i.e. frame has become stale
publish_spec_t(int on_count, int interval, int 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")
{
size_t start = str.find_first_not_of(chars);
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")
{
size_t end = str.find_last_not_of(chars);
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")
{
return rtrim(ltrim(str, chars), chars);
}
} // namespace solar
#ifndef __SOLAR_COMMON // include GUARD
#define __SOLAR_COMMON
namespace solar
{
#define FLAGBITS ~0x0FFF // bitwise one's complement (sort column info fits into lower three nibbles)
#define SORTCASE 0x0001000 // case sensitive
#define SORTREVERSE 0x0002000 // reverse order
#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 SEARCHWILD 0x0100000 // do wildcard comparison (only searchkey should contain wildcards)
#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 max(a, b) ((a) > (b)) ? (a) : (b)
#define min(a, b) ((a) < (b)) ? (a) : (b)
typedef unsigned int uint;
typedef std::vector<uint8_t> byte_vector;
struct publish_spec_t
{
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 timeout; // used for checking whether timeout has expired, i.e. frame has become stale
publish_spec_t(int on_count, int interval, int 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")
{
size_t start = str.find_first_not_of(chars);
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")
{
size_t end = str.find_last_not_of(chars);
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")
{
return rtrim(ltrim(str, chars), chars);
}
} // namespace solar
#endif // __SOLAR_COMMON