merge prep
This commit is contained in:
parent
4fb865c0fa
commit
605523ede5
@ -1,6 +1,6 @@
|
|||||||
# ESPHome config/source/solar Folder
|
# ESPHome config/source/solar Folder
|
||||||
|
|
||||||
This folder contains Contains source code files and related resources for solar projects.
|
This folder contains Contains source code files and related resources for solar projects.
|
||||||
|
|
||||||
|
|
||||||
For more information, visit the [ESPHome documentation](https://esphome.io/).
|
For more information, visit the [ESPHome documentation](https://esphome.io/).
|
||||||
@ -1,342 +1,342 @@
|
|||||||
#include "source/solar/cb_frame.h"
|
#include "source/solar/cb_frame.h"
|
||||||
// limit the amount of logging from this class as it can be called very frequently. Too much logging can lead to instability and no connectivity.
|
// limit the amount of logging from this class as it can be called very frequently. Too much logging can lead to instability and no connectivity.
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
const cb_frame emptyframe = cb_frame(); // to return a reference to when no valid frame is found
|
const cb_frame emptyframe = cb_frame(); // to return a reference to when no valid frame is found
|
||||||
|
|
||||||
cb_frame::cb_frame()
|
cb_frame::cb_frame()
|
||||||
{
|
{
|
||||||
this->msg_id = 0;
|
this->msg_id = 0;
|
||||||
this->publish = false;
|
this->publish = false;
|
||||||
this->rtr = false;
|
this->rtr = false;
|
||||||
this->can_id = 0;
|
this->can_id = 0;
|
||||||
this->frame.clear();
|
this->frame.clear();
|
||||||
// ESP_LOGI(tag("frame CTOR0").c_str(), "%-20s %s", "Created frame", this->to_string().c_str());
|
// ESP_LOGI(tag("frame CTOR0").c_str(), "%-20s %s", "Created frame", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cb_frame::cb_frame(int msg_id, uint32_t can_id)
|
cb_frame::cb_frame(int msg_id, uint32_t can_id)
|
||||||
{
|
{
|
||||||
this->msg_id = msg_id;
|
this->msg_id = msg_id;
|
||||||
this->publish = false;
|
this->publish = false;
|
||||||
this->rtr = false;
|
this->rtr = false;
|
||||||
this->can_id = can_id;
|
this->can_id = can_id;
|
||||||
this->frame.clear();
|
this->frame.clear();
|
||||||
// ESP_LOGI(tag("frame CTOR1").c_str(), "%-20s %s", "Created frame", this->to_string().c_str());
|
// ESP_LOGI(tag("frame CTOR1").c_str(), "%-20s %s", "Created frame", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cb_frame::cb_frame(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr)
|
cb_frame::cb_frame(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr)
|
||||||
{
|
{
|
||||||
this->msg_id = msg_id;
|
this->msg_id = msg_id;
|
||||||
this->publish = false;
|
this->publish = false;
|
||||||
this->rtr = rtr;
|
this->rtr = rtr;
|
||||||
this->can_id = can_id;
|
this->can_id = can_id;
|
||||||
this->frame = frame;
|
this->frame = frame;
|
||||||
// ESP_LOGI(tag("frame CTOR2").c_str(), "%-20s %s", "Created frame", this->to_string().c_str());
|
// ESP_LOGI(tag("frame CTOR2").c_str(), "%-20s %s", "Created frame", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
void cb_frame::clear()
|
void cb_frame::clear()
|
||||||
{
|
{
|
||||||
this->msg_id = 0;
|
this->msg_id = 0;
|
||||||
this->publish = false;
|
this->publish = false;
|
||||||
this->rtr = false;
|
this->rtr = false;
|
||||||
this->can_id = 0;
|
this->can_id = 0;
|
||||||
this->frame.clear();
|
this->frame.clear();
|
||||||
}
|
}
|
||||||
void cb_frame::swap(cb_frame &s)
|
void cb_frame::swap(cb_frame &s)
|
||||||
{
|
{
|
||||||
std::swap(this->msg_id, s.msg_id);
|
std::swap(this->msg_id, s.msg_id);
|
||||||
std::swap(this->publish, s.publish);
|
std::swap(this->publish, s.publish);
|
||||||
std::swap(this->rtr, s.rtr);
|
std::swap(this->rtr, s.rtr);
|
||||||
std::swap(this->can_id, s.can_id);
|
std::swap(this->can_id, s.can_id);
|
||||||
this->frame.swap(s.frame);
|
this->frame.swap(s.frame);
|
||||||
}
|
}
|
||||||
bool cb_frame::is_valid() const
|
bool cb_frame::is_valid() const
|
||||||
{
|
{
|
||||||
return this->can_id != 0;
|
return this->can_id != 0;
|
||||||
}
|
}
|
||||||
void cb_frame::setpublish(bool do_publish) const
|
void cb_frame::setpublish(bool do_publish) const
|
||||||
{
|
{
|
||||||
this->publish = do_publish;
|
this->publish = do_publish;
|
||||||
}
|
}
|
||||||
bool cb_frame::getpublish() const
|
bool cb_frame::getpublish() const
|
||||||
{
|
{
|
||||||
return this->publish;
|
return this->publish;
|
||||||
}
|
}
|
||||||
bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, bool extended_id, bool remote_transmission_request) const
|
bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, bool extended_id, bool remote_transmission_request) const
|
||||||
{
|
{
|
||||||
if(canbus == nullptr) {
|
if(canbus == nullptr) {
|
||||||
ESP_LOGE(tag("cb_frame::send_data").c_str(), "CAN bus interface is null, cannot send data");
|
ESP_LOGE(tag("cb_frame::send_data").c_str(), "CAN bus interface is null, cannot send data");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(!this->is_valid()) {
|
if(!this->is_valid()) {
|
||||||
ESP_LOGE(tag("cb_frame::send_data").c_str(), "Frame is not valid, cannot send data: %s", this->to_string().c_str());
|
ESP_LOGE(tag("cb_frame::send_data").c_str(), "Frame is not valid, cannot send data: %s", this->to_string().c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return cb_frame::send_frame(canbus, this->can_id, this->frame, extended_id, remote_transmission_request);
|
return cb_frame::send_frame(canbus, this->can_id, this->frame, extended_id, remote_transmission_request);
|
||||||
}
|
}
|
||||||
bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id, bool remote_transmission_request)
|
bool cb_frame::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id, bool remote_transmission_request)
|
||||||
{
|
{
|
||||||
if(canbus == nullptr) {
|
if(canbus == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
canbus->send_data(can_id, extended_id, remote_transmission_request, frame);
|
canbus->send_data(can_id, extended_id, remote_transmission_request, frame);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int cb_frame::compare(const cb_frame& b, const int *comparecolumns) const
|
int cb_frame::compare(const cb_frame& b, const int *comparecolumns) const
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
bool stopcompare = false;
|
bool stopcompare = false;
|
||||||
bool isdefaultcompare = comparecolumns == nullptr || *comparecolumns == 0;
|
bool isdefaultcompare = comparecolumns == nullptr || *comparecolumns == 0;
|
||||||
int ncmpcols = 0;
|
int ncmpcols = 0;
|
||||||
while (comparecolumns[ncmpcols] != 0) ncmpcols++;
|
while (comparecolumns[ncmpcols] != 0) ncmpcols++;
|
||||||
if (isdefaultcompare) {
|
if (isdefaultcompare) {
|
||||||
for (int i = sisortNULL + 1; i < sisortEND && !stopcompare && result == 0; i++) {
|
for (int i = sisortNULL + 1; i < sisortEND && !stopcompare && result == 0; i++) {
|
||||||
bool validnextcol = i < sisortEND - 1;
|
bool validnextcol = i < sisortEND - 1;
|
||||||
result = compare(b, i, &stopcompare, validnextcol);
|
result = compare(b, i, &stopcompare, validnextcol);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < ncmpcols && !stopcompare && result == 0; i++) {
|
for (int i = 0; i < ncmpcols && !stopcompare && result == 0; i++) {
|
||||||
result = compare(b, comparecolumns[i], &stopcompare, true);
|
result = compare(b, comparecolumns[i], &stopcompare, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int cb_frame::compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const
|
int cb_frame::compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int reverseorderflag = cmpflag & sisortreverse;
|
int reverseorderflag = cmpflag & sisortreverse;
|
||||||
int casesensitiveflag = cmpflag & sisortcase;
|
int casesensitiveflag = cmpflag & sisortcase;
|
||||||
int nulliswildcardflag = cmpflag & sisortwild;
|
int nulliswildcardflag = cmpflag & sisortwild;
|
||||||
int stopcompareflag = cmpflag & sistopcomparecol;
|
int stopcompareflag = cmpflag & sistopcomparecol;
|
||||||
cmpflag = cmpflag & ~FLAGBITS;
|
cmpflag = cmpflag & ~FLAGBITS;
|
||||||
if (cmpflag == 0)
|
if (cmpflag == 0)
|
||||||
return result;
|
return result;
|
||||||
bool casesensitive = casesensitiveflag != 0;
|
bool casesensitive = casesensitiveflag != 0;
|
||||||
bool sortwild = nulliswildcardflag != 0;
|
bool sortwild = nulliswildcardflag != 0;
|
||||||
bool stopcompare = stopcompareflag != 0;
|
bool stopcompare = stopcompareflag != 0;
|
||||||
*stopcomparesignal = false;
|
*stopcomparesignal = false;
|
||||||
|
|
||||||
switch (cmpflag) {
|
switch (cmpflag) {
|
||||||
case sisortNULL:
|
case sisortNULL:
|
||||||
return 0;
|
return 0;
|
||||||
case sisortcanid:
|
case sisortcanid:
|
||||||
{
|
{
|
||||||
bool bothvalid = this->is_valid() && b.is_valid();
|
bool bothvalid = this->is_valid() && b.is_valid();
|
||||||
if (bothvalid) {
|
if (bothvalid) {
|
||||||
result = num_compare(this->can_id, b.can_id);
|
result = num_compare(this->can_id, b.can_id);
|
||||||
*stopcomparesignal = stopcompare; // set flag only if both items are valid
|
*stopcomparesignal = stopcompare; // set flag only if both items are valid
|
||||||
} else {
|
} else {
|
||||||
if (sortwild)
|
if (sortwild)
|
||||||
result = 0;
|
result = 0;
|
||||||
else {
|
else {
|
||||||
bool bothinvalid = !(this->is_valid() || b.is_valid());
|
bool bothinvalid = !(this->is_valid() || b.is_valid());
|
||||||
if (validnextcol && bothinvalid)
|
if (validnextcol && bothinvalid)
|
||||||
result = 0; //if validnextcol then return 0 if both IDs are invalid
|
result = 0; //if validnextcol then return 0 if both IDs are invalid
|
||||||
result = this->can_id ? 1 : -1;
|
result = this->can_id ? 1 : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case sisortmsgid:
|
case sisortmsgid:
|
||||||
result = num_compare(this->msg_id, b.msg_id);
|
result = num_compare(this->msg_id, b.msg_id);
|
||||||
break;
|
break;
|
||||||
case sisortrtr:
|
case sisortrtr:
|
||||||
result = bool_compare(this->rtr, b.rtr);
|
result = bool_compare(this->rtr, b.rtr);
|
||||||
break;
|
break;
|
||||||
case sisortframe:
|
case sisortframe:
|
||||||
{
|
{
|
||||||
result = this->compare_frame(b.frame);
|
result = this->compare_frame(b.frame);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
result = 0;
|
result = 0;
|
||||||
ESP_LOGE("cb_frame::compare", "Unknown compare column %d", cmpflag);
|
ESP_LOGE("cb_frame::compare", "Unknown compare column %d", cmpflag);
|
||||||
*stopcomparesignal = true; // stop compare as we don't know how to handle
|
*stopcomparesignal = true; // stop compare as we don't know how to handle
|
||||||
// should never reach here as all cases must be dealt with
|
// should never reach here as all cases must be dealt with
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (reverseorderflag != 0)
|
if (reverseorderflag != 0)
|
||||||
result = -result;
|
result = -result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int cb_frame::compare_frame(const byte_vector& _frame) const
|
int cb_frame::compare_frame(const byte_vector& _frame) const
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
auto j = _frame.begin();
|
auto j = _frame.begin();
|
||||||
for(auto i = this->frame.begin(); i != this->frame.end() && result == 0; ++i) {
|
for(auto i = this->frame.begin(); i != this->frame.end() && result == 0; ++i) {
|
||||||
if(j == _frame.end()) {
|
if(j == _frame.end()) {
|
||||||
// ESP_LOGW("cb_frame::compare", "Frame size mismatch: this.frame.size()=%zu, _frame.size()=%zu", this->frame.size(), _frame.size());
|
// ESP_LOGW("cb_frame::compare", "Frame size mismatch: this.frame.size()=%zu, _frame.size()=%zu", this->frame.size(), _frame.size());
|
||||||
result = 1; // this frame is longer than _frame
|
result = 1; // this frame is longer than _frame
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// ESP_LOGI("cb_frame::compare", "Comparing frame byte %zu: %02X vs %02X", i - this->frame.begin(), *i, *j);
|
// ESP_LOGI("cb_frame::compare", "Comparing frame byte %zu: %02X vs %02X", i - this->frame.begin(), *i, *j);
|
||||||
result = num_compare(*i, *j);
|
result = num_compare(*i, *j);
|
||||||
// if (result != 0) {
|
// if (result != 0) {
|
||||||
// ESP_LOGI("cb_frame::compare", "Frame byte %zu mismatch: %02X vs %02X", i - this->frame.begin(), *i, *j);
|
// ESP_LOGI("cb_frame::compare", "Frame byte %zu mismatch: %02X vs %02X", i - this->frame.begin(), *i, *j);
|
||||||
// }
|
// }
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
std::string cb_frame::tag(const std::string& prefix, int prefixlen) const
|
std::string cb_frame::tag(const std::string& prefix, int prefixlen) const
|
||||||
{
|
{
|
||||||
char tag[48];
|
char tag[48];
|
||||||
if(prefix.length() == 0)
|
if(prefix.length() == 0)
|
||||||
snprintf(tag, sizeof(tag), "%04d 0x%03X ", msg_id, can_id);
|
snprintf(tag, sizeof(tag), "%04d 0x%03X ", msg_id, can_id);
|
||||||
else
|
else
|
||||||
snprintf(tag, sizeof(tag), "%-*s %04d 0x%03X ", prefixlen, prefix.c_str(), msg_id, can_id);
|
snprintf(tag, sizeof(tag), "%-*s %04d 0x%03X ", prefixlen, prefix.c_str(), msg_id, can_id);
|
||||||
return std::string(tag);
|
return std::string(tag);
|
||||||
}
|
}
|
||||||
std::string cb_frame::to_string() const
|
std::string cb_frame::to_string() const
|
||||||
{
|
{
|
||||||
// Be extra careful with the placement of printf format specifiers and their corresponding arguments to avoid buffer overflows and ensure correct formatting.
|
// Be extra careful with the placement of printf format specifiers and their corresponding arguments to avoid buffer overflows and ensure correct formatting.
|
||||||
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
||||||
std::string text = "";
|
std::string text = "";
|
||||||
char hex[10];
|
char hex[10];
|
||||||
std::string line;
|
std::string line;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
line = "<remote request> ";
|
line = "<remote request> ";
|
||||||
text = " ";
|
text = " ";
|
||||||
n = 6; // to align text output
|
n = 6; // to align text output
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (this->frame.empty()) {
|
if (this->frame.empty()) {
|
||||||
return "<empty>";
|
return "<empty>";
|
||||||
}
|
}
|
||||||
for (const auto& byte : frame) {
|
for (const auto& byte : frame) {
|
||||||
n++;
|
n++;
|
||||||
snprintf(hex, sizeof(hex), "%02X ", byte);
|
snprintf(hex, sizeof(hex), "%02X ", byte);
|
||||||
line += hex;
|
line += hex;
|
||||||
if(byte > 31 && byte < 127) {
|
if(byte > 31 && byte < 127) {
|
||||||
text += (char) byte;
|
text += (char) byte;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
text += ".";
|
text += ".";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = n; i < 8; i++) {
|
for (int i = n; i < 8; i++) {
|
||||||
line += " ";
|
line += " ";
|
||||||
text += " ";
|
text += " ";
|
||||||
}
|
}
|
||||||
return line + " " + text;
|
return line + " " + text;
|
||||||
}
|
}
|
||||||
// helper function to extract a scaled double value from two bytes
|
// helper function to extract a scaled double value from two bytes
|
||||||
double cb_frame::get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed)
|
double cb_frame::get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed)
|
||||||
{
|
{
|
||||||
int value = 0;
|
int value = 0;
|
||||||
if ((lsb == 0xFF && msb == 0xFF) || inv_scale == 0) {
|
if ((lsb == 0xFF && msb == 0xFF) || inv_scale == 0) {
|
||||||
return std::numeric_limits<double>::quiet_NaN();
|
return std::numeric_limits<double>::quiet_NaN();
|
||||||
}
|
}
|
||||||
if (is_signed) {
|
if (is_signed) {
|
||||||
value = static_cast<int16_t>((msb << 8) + lsb);
|
value = static_cast<int16_t>((msb << 8) + lsb);
|
||||||
} else {
|
} else {
|
||||||
value = static_cast<uint16_t>((msb << 8) + lsb);
|
value = static_cast<uint16_t>((msb << 8) + lsb);
|
||||||
}
|
}
|
||||||
//ESP_LOGI("cb_frame::get_double", "0x%02X%02X unsigned %d, scale %d", msb, lsb, value, scale);
|
//ESP_LOGI("cb_frame::get_double", "0x%02X%02X unsigned %d, scale %d", msb, lsb, value, scale);
|
||||||
return static_cast<double>(value) / inv_scale;
|
return static_cast<double>(value) / inv_scale;
|
||||||
}
|
}
|
||||||
// Helper function to convert four scaled values into a byte stream
|
// Helper function to convert four scaled values into a byte stream
|
||||||
// set scale to negative for signed values
|
// set scale to negative for signed values
|
||||||
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1)
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> byte_stream(2, 0);
|
std::vector<uint8_t> byte_stream(2, 0);
|
||||||
int value;
|
int value;
|
||||||
if(scale1 != 0) {
|
if(scale1 != 0) {
|
||||||
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
||||||
byte_stream[0] = value % 256;
|
byte_stream[0] = value % 256;
|
||||||
byte_stream[1] = (value >> 8) % 256;
|
byte_stream[1] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
return byte_stream;
|
return byte_stream;
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2)
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> byte_stream(4, 0);
|
std::vector<uint8_t> byte_stream(4, 0);
|
||||||
int value;
|
int value;
|
||||||
if(scale1 != 0) {
|
if(scale1 != 0) {
|
||||||
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
||||||
byte_stream[0] = value % 256;
|
byte_stream[0] = value % 256;
|
||||||
byte_stream[1] = (value >> 8) % 256;
|
byte_stream[1] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale2 != 0) {
|
if(scale2 != 0) {
|
||||||
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
||||||
byte_stream[2] = value % 256;
|
byte_stream[2] = value % 256;
|
||||||
byte_stream[3] = (value >> 8) % 256;
|
byte_stream[3] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
return byte_stream;
|
return byte_stream;
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3)
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> byte_stream(6, 0);
|
std::vector<uint8_t> byte_stream(6, 0);
|
||||||
int value;
|
int value;
|
||||||
if(scale1 != 0) {
|
if(scale1 != 0) {
|
||||||
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
||||||
byte_stream[0] = value % 256;
|
byte_stream[0] = value % 256;
|
||||||
byte_stream[1] = (value >> 8) % 256;
|
byte_stream[1] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale2 != 0) {
|
if(scale2 != 0) {
|
||||||
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
||||||
byte_stream[2] = value % 256;
|
byte_stream[2] = value % 256;
|
||||||
byte_stream[3] = (value >> 8) % 256;
|
byte_stream[3] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale3 != 0) {
|
if(scale3 != 0) {
|
||||||
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
||||||
byte_stream[4] = value % 256;
|
byte_stream[4] = value % 256;
|
||||||
byte_stream[5] = (value >> 8) % 256;
|
byte_stream[5] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
return byte_stream;
|
return byte_stream;
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, int scale4)
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, int scale4)
|
||||||
{
|
{
|
||||||
//ESP_LOGI("cb_frame::get_byte_stream", "1: %.3f 2: %.3f 3: %.3f 4: %.3f", value1, value2, value3, value4);
|
//ESP_LOGI("cb_frame::get_byte_stream", "1: %.3f 2: %.3f 3: %.3f 4: %.3f", value1, value2, value3, value4);
|
||||||
std::vector<uint8_t> byte_stream(8, 0);
|
std::vector<uint8_t> byte_stream(8, 0);
|
||||||
int value;
|
int value;
|
||||||
if(scale1 != 0) {
|
if(scale1 != 0) {
|
||||||
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
||||||
byte_stream[0] = value % 256;
|
byte_stream[0] = value % 256;
|
||||||
byte_stream[1] = (value >> 8) % 256;
|
byte_stream[1] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale2 != 0) {
|
if(scale2 != 0) {
|
||||||
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
||||||
byte_stream[2] = value % 256;
|
byte_stream[2] = value % 256;
|
||||||
byte_stream[3] = (value >> 8) % 256;
|
byte_stream[3] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale3 != 0) {
|
if(scale3 != 0) {
|
||||||
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
||||||
byte_stream[4] = value % 256;
|
byte_stream[4] = value % 256;
|
||||||
byte_stream[5] = (value >> 8) % 256;
|
byte_stream[5] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale4 != 0) {
|
if(scale4 != 0) {
|
||||||
value = (scale4 < 0) ? static_cast<int16_t>(value4 * -scale4) : static_cast<uint16_t>(value4 * scale4);
|
value = (scale4 < 0) ? static_cast<int16_t>(value4 * -scale4) : static_cast<uint16_t>(value4 * scale4);
|
||||||
byte_stream[6] = value % 256;
|
byte_stream[6] = value % 256;
|
||||||
byte_stream[7] = (value >> 8) % 256;
|
byte_stream[7] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
return byte_stream;
|
return byte_stream;
|
||||||
}
|
}
|
||||||
// Helper function to convert four scaled values into a byte stream
|
// Helper function to convert four scaled values into a byte stream
|
||||||
// set scale to negative for signed values
|
// set scale to negative for signed values
|
||||||
// Overloaded function to handle double scale for the fourth value
|
// Overloaded function to handle double scale for the fourth value
|
||||||
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4)
|
std::vector<uint8_t> cb_frame::get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> byte_stream(8, 0);
|
std::vector<uint8_t> byte_stream(8, 0);
|
||||||
int value;
|
int value;
|
||||||
if(scale1 != 0) {
|
if(scale1 != 0) {
|
||||||
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
value = (scale1 < 0) ? static_cast<int16_t>(value1 * -scale1) : static_cast<uint16_t>(value1 * scale1);
|
||||||
byte_stream[0] = value % 256;
|
byte_stream[0] = value % 256;
|
||||||
byte_stream[1] = (value >> 8) % 256;
|
byte_stream[1] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale2 != 0) {
|
if(scale2 != 0) {
|
||||||
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
value = (scale2 < 0) ? static_cast<int16_t>(value2 * -scale2) : static_cast<uint16_t>(value2 * scale2);
|
||||||
byte_stream[2] = value % 256;
|
byte_stream[2] = value % 256;
|
||||||
byte_stream[3] = (value >> 8) % 256;
|
byte_stream[3] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
if(scale3 != 0) {
|
if(scale3 != 0) {
|
||||||
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
value = (scale3 < 0) ? static_cast<int16_t>(value3 * -scale3) : static_cast<uint16_t>(value3 * scale3);
|
||||||
byte_stream[4] = value % 256;
|
byte_stream[4] = value % 256;
|
||||||
byte_stream[5] = (value >> 8) % 256;
|
byte_stream[5] = (value >> 8) % 256;
|
||||||
}
|
}
|
||||||
value = (scale4 < 0) ? static_cast<int16_t>(round(value4 * -scale4)) : static_cast<uint16_t>(round(value4 * scale4));
|
value = (scale4 < 0) ? static_cast<int16_t>(round(value4 * -scale4)) : static_cast<uint16_t>(round(value4 * scale4));
|
||||||
byte_stream[6] = value % 256;
|
byte_stream[6] = value % 256;
|
||||||
byte_stream[7] = (value >> 8) % 256;
|
byte_stream[7] = (value >> 8) % 256;
|
||||||
return byte_stream;
|
return byte_stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
@ -1,66 +1,66 @@
|
|||||||
#ifndef __SOLAR_CB_FRAME // include GUARD
|
#ifndef __SOLAR_CB_FRAME // include GUARD
|
||||||
#define __SOLAR_CB_FRAME
|
#define __SOLAR_CB_FRAME
|
||||||
#include "esphome.h"
|
#include "esphome.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
using namespace esphome;
|
using namespace esphome;
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
class cbf_store;
|
class cbf_store;
|
||||||
class cb_frame {
|
class cb_frame {
|
||||||
private:
|
private:
|
||||||
mutable bool publish; // whether this frame should be published
|
mutable bool publish; // whether this frame should be published
|
||||||
public:
|
public:
|
||||||
enum cbf_sortcolumns : int
|
enum cbf_sortcolumns : int
|
||||||
{
|
{
|
||||||
sisortNULL,
|
sisortNULL,
|
||||||
sisortcanid,
|
sisortcanid,
|
||||||
sisortrtr,
|
sisortrtr,
|
||||||
sisortframe,
|
sisortframe,
|
||||||
sisortmsgid,
|
sisortmsgid,
|
||||||
sisortEND,
|
sisortEND,
|
||||||
sisortcase = SORTCASE, // case sensitive
|
sisortcase = SORTCASE, // case sensitive
|
||||||
sisortreverse = SORTREVERSE, // reverse order
|
sisortreverse = SORTREVERSE, // reverse order
|
||||||
sisortwild = WILDNULL, // an empty, or null entry equates to wildcard
|
sisortwild = WILDNULL, // an empty, or null entry equates to wildcard
|
||||||
sistopcomparecol = STOPCOMPARE // stop compare after this column if both items to be compared are valid and a compare was possible
|
sistopcomparecol = STOPCOMPARE // stop compare after this column if both items to be compared are valid and a compare was possible
|
||||||
};
|
};
|
||||||
int msg_id;
|
int msg_id;
|
||||||
bool rtr;
|
bool rtr;
|
||||||
uint32_t can_id;
|
uint32_t can_id;
|
||||||
byte_vector frame;
|
byte_vector frame;
|
||||||
cb_frame();
|
cb_frame();
|
||||||
cb_frame(int msg_id, uint32_t can_id);
|
cb_frame(int msg_id, uint32_t can_id);
|
||||||
cb_frame(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr);
|
cb_frame(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr);
|
||||||
void clear();
|
void clear();
|
||||||
void swap(cb_frame &s);
|
void swap(cb_frame &s);
|
||||||
virtual bool is_valid() const;
|
virtual bool is_valid() const;
|
||||||
virtual int compare(const cb_frame& b, const int *comparecolumns) const;
|
virtual int compare(const cb_frame& b, const int *comparecolumns) const;
|
||||||
virtual int compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const;
|
virtual int compare(const cb_frame &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const;
|
||||||
int compare_frame(const byte_vector& _frame) const;
|
int compare_frame(const byte_vector& _frame) const;
|
||||||
void setpublish(bool do_publish = true) const; // we promise only to modify the publish flag
|
void setpublish(bool do_publish = true) const; // we promise only to modify the publish flag
|
||||||
bool getpublish() const;
|
bool getpublish() const;
|
||||||
virtual std::string tag(const std::string& prefix = "", int prefixlen = 18) const;
|
virtual std::string tag(const std::string& prefix = "", int prefixlen = 18) const;
|
||||||
virtual std::string to_string() const;
|
virtual std::string to_string() const;
|
||||||
virtual ~cb_frame() = default;
|
virtual ~cb_frame() = default;
|
||||||
// using default (compiler auto generated) copy and move constructors and assignment operators
|
// using default (compiler auto generated) copy and move constructors and assignment operators
|
||||||
|
|
||||||
/// Helper functions to convert four scaled values into a byte stream
|
/// Helper functions to convert four scaled values into a byte stream
|
||||||
/// set scale to negative for signed values
|
/// set scale to negative for signed values
|
||||||
static std::vector<uint8_t> get_byte_stream(double value1, int scale1);
|
static std::vector<uint8_t> get_byte_stream(double value1, int scale1);
|
||||||
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2);
|
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2);
|
||||||
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3);
|
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3);
|
||||||
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, int scale4);
|
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, int scale4);
|
||||||
/// Overloaded function to handle double scale for the fourth value
|
/// Overloaded function to handle double scale for the fourth value
|
||||||
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4);
|
static std::vector<uint8_t> get_byte_stream(double value1, int scale1, double value2, int scale2, double value3, int scale3, double value4, double scale4);
|
||||||
|
|
||||||
/// helper function to extract a scaled double value from two bytes
|
/// helper function to extract a scaled double value from two bytes
|
||||||
static double get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed = false); //inv_scale is the reciprocal of scale factor
|
static double get_double(uint8_t lsb, uint8_t msb, int inv_scale, bool is_signed = false); //inv_scale is the reciprocal of scale factor
|
||||||
bool send_frame(esphome::canbus::Canbus *canbus, bool extended_id = false, bool remote_transmission_request = false) const;
|
bool send_frame(esphome::canbus::Canbus *canbus, bool extended_id = false, bool remote_transmission_request = false) const;
|
||||||
static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame
|
static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const cb_frame emptyframe; // to return a reference to when no valid frame is found
|
extern const cb_frame emptyframe; // to return a reference to when no valid frame is found
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
#endif // __SOLAR_CB_FRAME
|
#endif // __SOLAR_CB_FRAME
|
||||||
@ -1,93 +1,93 @@
|
|||||||
#include "source/solar/cbf_cache.h"
|
#include "source/solar/cbf_cache.h"
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
static const int comparecolumns[] = {cbf_store::sisortframe, cbf_store::sisortrtr, 0};
|
static const int comparecolumns[] = {cbf_store::sisortframe, cbf_store::sisortrtr, 0};
|
||||||
|
|
||||||
void cbf_cache::clear()
|
void cbf_cache::clear()
|
||||||
{
|
{
|
||||||
cache_map.clear();
|
cache_map.clear();
|
||||||
}
|
}
|
||||||
int cbf_cache::size() const
|
int cbf_cache::size() const
|
||||||
{
|
{
|
||||||
return cache_map.size();
|
return cache_map.size();
|
||||||
}
|
}
|
||||||
bool cbf_cache::hasitem(uint32_t can_id, bool remote_transmission_request) const
|
bool cbf_cache::hasitem(uint32_t can_id, bool remote_transmission_request) const
|
||||||
{
|
{
|
||||||
return hasitem(canid_rtr_t(can_id, remote_transmission_request));
|
return hasitem(canid_rtr_t(can_id, remote_transmission_request));
|
||||||
}
|
}
|
||||||
bool cbf_cache::hasitem(canid_rtr_t key) const
|
bool cbf_cache::hasitem(canid_rtr_t key) const
|
||||||
{
|
{
|
||||||
return cache_map.find(key) != cache_map.end();
|
return cache_map.find(key) != cache_map.end();
|
||||||
}
|
}
|
||||||
cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request)
|
cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request)
|
||||||
{
|
{
|
||||||
return cache_map.at(canid_rtr_t(can_id, remote_transmission_request));
|
return cache_map.at(canid_rtr_t(can_id, remote_transmission_request));
|
||||||
}
|
}
|
||||||
const cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request) const
|
const cbf_cache_item& cbf_cache::getitem(uint32_t can_id, bool remote_transmission_request) const
|
||||||
{
|
{
|
||||||
return cache_map.at(canid_rtr_t(can_id, remote_transmission_request));
|
return cache_map.at(canid_rtr_t(can_id, remote_transmission_request));
|
||||||
}
|
}
|
||||||
bool cbf_cache::additem(const cbf_store& storeitem)
|
bool cbf_cache::additem(const cbf_store& storeitem)
|
||||||
{
|
{
|
||||||
const auto& ret = cache_map.emplace(canid_rtr_t(storeitem.can_id, storeitem.rtr), cbf_cache_item(storeitem));
|
const auto& ret = cache_map.emplace(canid_rtr_t(storeitem.can_id, storeitem.rtr), cbf_cache_item(storeitem));
|
||||||
if(ret.second) {
|
if(ret.second) {
|
||||||
return false; // new item inserted, no publish
|
return false; // new item inserted, no publish
|
||||||
}
|
}
|
||||||
auto& kvp = *ret.first;
|
auto& kvp = *ret.first;
|
||||||
auto& item = kvp.second;
|
auto& item = kvp.second;
|
||||||
auto updateresult = item.update(storeitem, comparecolumns, 1);
|
auto updateresult = item.update(storeitem, comparecolumns, 1);
|
||||||
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
|
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
|
||||||
// ESP_LOGI(item.store0.tag("== ST1 DUP == ").c_str(), item.store0.to_string().c_str());
|
// ESP_LOGI(item.store0.tag("== ST1 DUP == ").c_str(), item.store0.to_string().c_str());
|
||||||
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
|
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
|
||||||
}
|
}
|
||||||
// try next store to see if it has a duplicate of new item
|
// try next store to see if it has a duplicate of new item
|
||||||
updateresult = item.update(storeitem, comparecolumns, 2);
|
updateresult = item.update(storeitem, comparecolumns, 2);
|
||||||
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
|
if(updateresult & cbf_store::cbf_updateresult::stu_DUPLICATE) {
|
||||||
// ESP_LOGI(item.store1.tag("== ST2 DUP == ").c_str(), item.store1.to_string().c_str());
|
// ESP_LOGI(item.store1.tag("== ST2 DUP == ").c_str(), item.store1.to_string().c_str());
|
||||||
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
|
return updateresult & cbf_store::cbf_updateresult::stu_PUBLISH;
|
||||||
}
|
}
|
||||||
item.update(storeitem);
|
item.update(storeitem);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const cb_frame& cbf_cache::get_frame(uint32_t can_id, bool remote_transmission_request) const
|
const cb_frame& cbf_cache::get_frame(uint32_t can_id, bool remote_transmission_request) const
|
||||||
{
|
{
|
||||||
return get_frame(canid_rtr_t(can_id, remote_transmission_request));
|
return get_frame(canid_rtr_t(can_id, remote_transmission_request));
|
||||||
}
|
}
|
||||||
const cb_frame& cbf_cache::get_frame(canid_rtr_t key) const
|
const cb_frame& cbf_cache::get_frame(canid_rtr_t key) const
|
||||||
{
|
{
|
||||||
auto it = cache_map.find(key);
|
auto it = cache_map.find(key);
|
||||||
if(it == cache_map.end())
|
if(it == cache_map.end())
|
||||||
return emptyframe; // return reference to static empty frame if not found
|
return emptyframe; // return reference to static empty frame if not found
|
||||||
const auto& item = it->second;
|
const auto& item = it->second;
|
||||||
return item.get_frame();
|
return item.get_frame();
|
||||||
}
|
}
|
||||||
bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id, bool remote_transmission_request)
|
bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id, bool remote_transmission_request)
|
||||||
{
|
{
|
||||||
auto key = canid_rtr_t(can_id, remote_transmission_request);
|
auto key = canid_rtr_t(can_id, remote_transmission_request);
|
||||||
if(!this->hasitem(key))
|
if(!this->hasitem(key))
|
||||||
return false;
|
return false;
|
||||||
const auto& cbf = get_frame(key);
|
const auto& cbf = get_frame(key);
|
||||||
if(cbf.getpublish()) {
|
if(cbf.getpublish()) {
|
||||||
return cbf.send_frame(canbus, extended_id, remote_transmission_request);
|
return cbf.send_frame(canbus, extended_id, remote_transmission_request);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
for(const auto& kvp : cache_map) {
|
for(const auto& kvp : cache_map) {
|
||||||
const auto& item = kvp.second;
|
const auto& item = kvp.second;
|
||||||
const auto& cbf = item.get_frame();
|
const auto& cbf = item.get_frame();
|
||||||
if(cbf.getpublish()) {
|
if(cbf.getpublish()) {
|
||||||
cbf.send_frame(canbus, extended_id, remote_transmission_request);
|
cbf.send_frame(canbus, extended_id, remote_transmission_request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// static version to send arbitrary frame
|
// static version to send arbitrary frame
|
||||||
bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id, bool remote_transmission_request)
|
bool cbf_cache::send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id, bool remote_transmission_request)
|
||||||
{
|
{
|
||||||
return cb_frame::send_frame(canbus, can_id, frame, extended_id, remote_transmission_request);
|
return cb_frame::send_frame(canbus, can_id, frame, extended_id, remote_transmission_request);
|
||||||
}
|
}
|
||||||
bool cbf_cache::send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id)
|
bool cbf_cache::send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id)
|
||||||
{
|
{
|
||||||
return cb_frame::send_frame(canbus, can_id, {}, extended_id, true);
|
return cb_frame::send_frame(canbus, can_id, {}, extended_id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
@ -1,39 +1,39 @@
|
|||||||
// NB! A lot of comments in .h and .cpp files were auto generated by CoPilot. Applicable comments have been retained, others removed.
|
// NB! A lot of comments in .h and .cpp files were auto generated by CoPilot. Applicable comments have been retained, others removed.
|
||||||
|
|
||||||
#ifndef __SOLAR_CBF_CACHE
|
#ifndef __SOLAR_CBF_CACHE
|
||||||
#define __SOLAR_CBF_CACHE
|
#define __SOLAR_CBF_CACHE
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "esphome.h"
|
#include "esphome.h"
|
||||||
#include "cbf_store_pylon.h"
|
#include "cbf_store_pylon.h"
|
||||||
#include "cbf_cache_item.h"
|
#include "cbf_cache_item.h"
|
||||||
using namespace esphome;
|
using namespace esphome;
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
class cbf_cache {
|
class cbf_cache {
|
||||||
public:
|
public:
|
||||||
typedef std::pair<uint32_t, bool> canid_rtr_t; // pair of CAN ID and RTR flag
|
typedef std::pair<uint32_t, bool> canid_rtr_t; // pair of CAN ID and RTR flag
|
||||||
std::map<canid_rtr_t, cbf_cache_item> cache_map; // map of rtr/CAN IDs to cache items
|
std::map<canid_rtr_t, cbf_cache_item> cache_map; // map of rtr/CAN IDs to cache items
|
||||||
cbf_cache() = default;
|
cbf_cache() = default;
|
||||||
void clear();
|
void clear();
|
||||||
int size() const;
|
int size() const;
|
||||||
bool hasitem(canid_rtr_t key) const;
|
bool hasitem(canid_rtr_t key) const;
|
||||||
bool hasitem(uint32_t can_id, bool remote_transmission_request) const;
|
bool hasitem(uint32_t can_id, bool remote_transmission_request) const;
|
||||||
cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request);
|
cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request);
|
||||||
const cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request) const;
|
const cbf_cache_item& getitem(uint32_t can_id, bool remote_transmission_request) const;
|
||||||
// Add a new item to the cache or update an existing one
|
// Add a new item to the cache or update an existing one
|
||||||
bool additem(const cbf_store& item);
|
bool additem(const cbf_store& item);
|
||||||
const cb_frame& get_frame(canid_rtr_t key) const;
|
const cb_frame& get_frame(canid_rtr_t key) const;
|
||||||
const cb_frame& get_frame(uint32_t can_id, bool remote_transmission_request) const;
|
const cb_frame& get_frame(uint32_t can_id, bool remote_transmission_request) const;
|
||||||
virtual ~cbf_cache() = default; // virtual destructor for base class
|
virtual ~cbf_cache() = default; // virtual destructor for base class
|
||||||
bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false, bool remote_transmission_request = false);
|
bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false, bool remote_transmission_request = false);
|
||||||
static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame
|
static bool send_frame(esphome::canbus::Canbus *canbus, uint32_t can_id, const byte_vector& frame, bool extended_id = false, bool remote_transmission_request = false); // static version to send arbitrary frame
|
||||||
static bool send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false); // static version to send remote transmission request frame
|
static bool send_request(esphome::canbus::Canbus *canbus, uint32_t can_id, bool extended_id = false); // static version to send remote transmission request frame
|
||||||
// using default (compiler auto generated) copy and move constructors and assignment operators
|
// using default (compiler auto generated) copy and move constructors and assignment operators
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
|
|
||||||
#endif // __SOLAR_CBF_CACHE
|
#endif // __SOLAR_CBF_CACHE
|
||||||
@ -1,117 +1,117 @@
|
|||||||
// if filename is changes, remove old .o file from \\TRUENAS\esphome\config\.esphome\build\sthome-ut8\.pioenvs\sthome-ut8\src\source\solar
|
// if filename is changes, remove old .o file from \\TRUENAS\esphome\config\.esphome\build\sthome-ut8\.pioenvs\sthome-ut8\src\source\solar
|
||||||
#include "source/solar/cbf_cache_item.h"
|
#include "source/solar/cbf_cache_item.h"
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
cbf_cache_item::cbf_cache_item()
|
cbf_cache_item::cbf_cache_item()
|
||||||
{
|
{
|
||||||
this->store0 = std::make_shared<cbf_store>(0, 0, 1, byte_vector(), false, 0, 0); // make generic empty store
|
this->store0 = std::make_shared<cbf_store>(0, 0, 1, byte_vector(), false, 0, 0); // make generic empty store
|
||||||
this->store1 = std::make_shared<cbf_store>(0, 0, 2, byte_vector(), false, 0, 0); // make generic empty store
|
this->store1 = std::make_shared<cbf_store>(0, 0, 2, byte_vector(), false, 0, 0); // make generic empty store
|
||||||
ESP_LOGI( this->store0->tag("cache_item CTOR0A ").c_str(), "%-20s %s", "Created cache item", this->store0->to_string().c_str());
|
ESP_LOGI( this->store0->tag("cache_item CTOR0A ").c_str(), "%-20s %s", "Created cache item", this->store0->to_string().c_str());
|
||||||
ESP_LOGI( this->store1->tag("cache_item CTOR0B ").c_str(), "%-20s %s", "Created cache item", this->store1->to_string().c_str());
|
ESP_LOGI( this->store1->tag("cache_item CTOR0B ").c_str(), "%-20s %s", "Created cache item", this->store1->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_cache_item::cbf_cache_item(const cbf_store& store0)
|
cbf_cache_item::cbf_cache_item(const cbf_store& store0)
|
||||||
{
|
{
|
||||||
this->store0 = store0.clone();
|
this->store0 = store0.clone();
|
||||||
this->store1 = store0.clone();
|
this->store1 = store0.clone();
|
||||||
this->store0->id = 1;
|
this->store0->id = 1;
|
||||||
this->store1->id = 2;
|
this->store1->id = 2;
|
||||||
}
|
}
|
||||||
void cbf_cache_item::clear()
|
void cbf_cache_item::clear()
|
||||||
{
|
{
|
||||||
store0 = nullptr;
|
store0 = nullptr;
|
||||||
store1 = nullptr;
|
store1 = nullptr;
|
||||||
}
|
}
|
||||||
void cbf_cache_item::swap(cbf_cache_item &s)
|
void cbf_cache_item::swap(cbf_cache_item &s)
|
||||||
{
|
{
|
||||||
std::swap(this->store0, s.store0);
|
std::swap(this->store0, s.store0);
|
||||||
std::swap(this->store1, s.store1);
|
std::swap(this->store1, s.store1);
|
||||||
}
|
}
|
||||||
bool cbf_cache_item::update(const cbf_store& newitem)
|
bool cbf_cache_item::update(const cbf_store& newitem)
|
||||||
{
|
{
|
||||||
if(!this->store0->is_valid()) {
|
if(!this->store0->is_valid()) {
|
||||||
store0 = newitem.clone();
|
store0 = newitem.clone();
|
||||||
store0->id = 1;
|
store0->id = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(!this->store1->is_valid()) {
|
if(!this->store1->is_valid()) {
|
||||||
store1 = newitem.clone();
|
store1 = newitem.clone();
|
||||||
store1->id = 2;
|
store1->id = 2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool result = store1->last_timestamp > store0->last_timestamp;
|
bool result = store1->last_timestamp > store0->last_timestamp;
|
||||||
if(result) {
|
if(result) {
|
||||||
store0 = newitem.clone();
|
store0 = newitem.clone();
|
||||||
this->store0->id = 1;
|
this->store0->id = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
store1 = newitem.clone();
|
store1 = newitem.clone();
|
||||||
this->store1->id = 2;
|
this->store1->id = 2;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
cbf_store::cbf_updateresult cbf_cache_item::update(const cbf_store& newitem, const int *comparecolumns, int store_selector)
|
cbf_store::cbf_updateresult cbf_cache_item::update(const cbf_store& newitem, const int *comparecolumns, int store_selector)
|
||||||
{
|
{
|
||||||
cbf_store::cbf_updateresult result = cbf_store::cbf_updateresult::stu_NONE;
|
cbf_store::cbf_updateresult result = cbf_store::cbf_updateresult::stu_NONE;
|
||||||
switch(store_selector) {
|
switch(store_selector) {
|
||||||
case 2:
|
case 2:
|
||||||
result = this->store1->update(newitem, comparecolumns);
|
result = this->store1->update(newitem, comparecolumns);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
result = this->store0->update(newitem, comparecolumns);
|
result = this->store0->update(newitem, comparecolumns);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
const cb_frame& cbf_cache_item::get_frame() const
|
const cb_frame& cbf_cache_item::get_frame() const
|
||||||
{
|
{
|
||||||
return get_store(); // return most recent valid store's frame
|
return get_store(); // return most recent valid store's frame
|
||||||
}
|
}
|
||||||
const cbf_store& cbf_cache_item::get_store() const
|
const cbf_store& cbf_cache_item::get_store() const
|
||||||
{
|
{
|
||||||
if(this->store0->is_valid() && !this->store1->is_valid()) {
|
if(this->store0->is_valid() && !this->store1->is_valid()) {
|
||||||
return *store0;
|
return *store0;
|
||||||
}
|
}
|
||||||
if(!this->store0->is_valid() && this->store1->is_valid()) {
|
if(!this->store0->is_valid() && this->store1->is_valid()) {
|
||||||
return *store1;
|
return *store1;
|
||||||
}
|
}
|
||||||
if(this->store0->is_valid() && this->store1->is_valid()) {
|
if(this->store0->is_valid() && this->store1->is_valid()) {
|
||||||
if(this->store0->last_timestamp >= this->store1->last_timestamp) {
|
if(this->store0->last_timestamp >= this->store1->last_timestamp) {
|
||||||
return *this->store0;
|
return *this->store0;
|
||||||
} else {
|
} else {
|
||||||
return *this->store1;
|
return *this->store1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return *this->store0; // no valid stores, but return store0 which is always present
|
return *this->store0; // no valid stores, but return store0 which is always present
|
||||||
}
|
}
|
||||||
std::string cbf_cache_item::tag(const std::string& prefix) const
|
std::string cbf_cache_item::tag(const std::string& prefix) const
|
||||||
{
|
{
|
||||||
return get_store().tag(prefix, prefix.length());
|
return get_store().tag(prefix, prefix.length());
|
||||||
}
|
}
|
||||||
std::string cbf_cache_item::to_string() const
|
std::string cbf_cache_item::to_string() const
|
||||||
{
|
{
|
||||||
return get_store().to_string();
|
return get_store().to_string();
|
||||||
}
|
}
|
||||||
std::string cbf_cache_item::tag0(const std::string& prefix) const
|
std::string cbf_cache_item::tag0(const std::string& prefix) const
|
||||||
{
|
{
|
||||||
return store0->tag(prefix);
|
return store0->tag(prefix);
|
||||||
}
|
}
|
||||||
std::string cbf_cache_item::tag1(const std::string& prefix) const
|
std::string cbf_cache_item::tag1(const std::string& prefix) const
|
||||||
{
|
{
|
||||||
return store1->tag(prefix);
|
return store1->tag(prefix);
|
||||||
}
|
}
|
||||||
std::string cbf_cache_item::st0_tostring() const
|
std::string cbf_cache_item::st0_tostring() const
|
||||||
{
|
{
|
||||||
char buffer[150];
|
char buffer[150];
|
||||||
snprintf(buffer, sizeof(buffer), "ST1: [%s] %s", trim(tag0()).c_str(), store0->to_string().c_str());
|
snprintf(buffer, sizeof(buffer), "ST1: [%s] %s", trim(tag0()).c_str(), store0->to_string().c_str());
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
std::string cbf_cache_item::st1_tostring() const
|
std::string cbf_cache_item::st1_tostring() const
|
||||||
{
|
{
|
||||||
char buffer[150];
|
char buffer[150];
|
||||||
snprintf(buffer, sizeof(buffer), "ST2: [%s] %s", trim(tag1()).c_str(), store1->to_string().c_str());
|
snprintf(buffer, sizeof(buffer), "ST2: [%s] %s", trim(tag1()).c_str(), store1->to_string().c_str());
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
|
|
||||||
@ -1,42 +1,42 @@
|
|||||||
#ifndef __SOLAR_CBF_CACHE_ITEM
|
#ifndef __SOLAR_CBF_CACHE_ITEM
|
||||||
#define __SOLAR_CBF_CACHE_ITEM
|
#define __SOLAR_CBF_CACHE_ITEM
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "esphome.h"
|
#include "esphome.h"
|
||||||
#include "cbf_store_pylon.h"
|
#include "cbf_store_pylon.h"
|
||||||
using namespace esphome;
|
using namespace esphome;
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
class cbf_cache_item {
|
class cbf_cache_item {
|
||||||
private:
|
private:
|
||||||
const int storecount = 2;
|
const int storecount = 2;
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<cbf_store> store0;
|
std::shared_ptr<cbf_store> store0;
|
||||||
std::shared_ptr<cbf_store> store1;
|
std::shared_ptr<cbf_store> store1;
|
||||||
|
|
||||||
cbf_cache_item(); // to allow object to be used as a value element in map operator[] method
|
cbf_cache_item(); // to allow object to be used as a value element in map operator[] method
|
||||||
cbf_cache_item(const cbf_store& store0);
|
cbf_cache_item(const cbf_store& store0);
|
||||||
void clear();
|
void clear();
|
||||||
void swap(cbf_cache_item &s);
|
void swap(cbf_cache_item &s);
|
||||||
bool update(const cbf_store& newitem);
|
bool update(const cbf_store& newitem);
|
||||||
cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns, int store_selector);
|
cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns, int store_selector);
|
||||||
const cb_frame& get_frame() const; // returns the most recent valid frame
|
const cb_frame& get_frame() const; // returns the most recent valid frame
|
||||||
const cbf_store& get_store() const; // returns the most recent valid store
|
const cbf_store& get_store() const; // returns the most recent valid store
|
||||||
virtual std::string tag0(const std::string& prefix = "") const;
|
virtual std::string tag0(const std::string& prefix = "") const;
|
||||||
virtual std::string tag1(const std::string& prefix = "") const;
|
virtual std::string tag1(const std::string& prefix = "") const;
|
||||||
virtual std::string tag(const std::string& prefix = "") const;
|
virtual std::string tag(const std::string& prefix = "") const;
|
||||||
virtual std::string to_string() const;
|
virtual std::string to_string() const;
|
||||||
virtual std::string st0_tostring() const; // string of store 0
|
virtual std::string st0_tostring() const; // string of store 0
|
||||||
virtual std::string st1_tostring() const; // string of store 1
|
virtual std::string st1_tostring() const; // string of store 1
|
||||||
// the default copy and move constructors and assignment operators are fine
|
// the default copy and move constructors and assignment operators are fine
|
||||||
// because shared_ptr takes care of the underlying memory management
|
// because shared_ptr takes care of the underlying memory management
|
||||||
cbf_cache_item(const cbf_cache_item& b) = default;
|
cbf_cache_item(const cbf_cache_item& b) = default;
|
||||||
cbf_cache_item& operator=(const cbf_cache_item& b) = default;
|
cbf_cache_item& operator=(const cbf_cache_item& b) = default;
|
||||||
cbf_cache_item(cbf_cache_item&& src) = default;
|
cbf_cache_item(cbf_cache_item&& src) = default;
|
||||||
cbf_cache_item& operator=(cbf_cache_item&& src) = default;
|
cbf_cache_item& operator=(cbf_cache_item&& src) = default;
|
||||||
virtual ~cbf_cache_item() = default; // virtual destructor for base class
|
virtual ~cbf_cache_item() = default; // virtual destructor for base class
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
#endif // __SOLAR_CBF_CACHE
|
#endif // __SOLAR_CBF_CACHE
|
||||||
@ -1,158 +1,158 @@
|
|||||||
#include "cbf_pylon.h"
|
#include "cbf_pylon.h"
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
// see common.h for definition of publish_spec_t
|
// see common.h for definition of publish_spec_t
|
||||||
// publish_spec_t(int on_count, int interval, int timeout)
|
// publish_spec_t(int on_count, int interval, int timeout)
|
||||||
const publish_spec_t cbf_pylon::publish_spec = publish_spec_t(3, 15, 30); // default publish spec for Pylontech battery messages
|
const publish_spec_t cbf_pylon::publish_spec = publish_spec_t(3, 15, 30); // default publish spec for Pylontech battery messages
|
||||||
const publish_spec_t cbf_pylon::rtr_publish_spec = publish_spec_t(2, 10, 15); // for remote transmission requests
|
const publish_spec_t cbf_pylon::rtr_publish_spec = publish_spec_t(2, 10, 15); // for remote transmission requests
|
||||||
|
|
||||||
cbf_pylon& cbf_pylon::operator=(cbf_pylon&& src)
|
cbf_pylon& cbf_pylon::operator=(cbf_pylon&& src)
|
||||||
{
|
{
|
||||||
if (this != &src) {
|
if (this != &src) {
|
||||||
cb_frame::operator=(std::move(src));
|
cb_frame::operator=(std::move(src));
|
||||||
src.clear();
|
src.clear();
|
||||||
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
|
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
void cbf_pylon::clear()
|
void cbf_pylon::clear()
|
||||||
{
|
{
|
||||||
cb_frame::clear();
|
cb_frame::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// float cbf_pylon::_get_battery_charge_voltage_limit() const
|
// float cbf_pylon::_get_battery_charge_voltage_limit() const
|
||||||
// {
|
// {
|
||||||
// const auto& x = this->frame;
|
// const auto& x = this->frame;
|
||||||
// return 0.1 * (float)((x[1] << 8) + x[0]);
|
// return 0.1 * (float)((x[1] << 8) + x[0]);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Function to build a message from the pylon canbus frame
|
// Function to build a message from the pylon canbus frame
|
||||||
std::string cbf_pylon::to_string() const
|
std::string cbf_pylon::to_string() const
|
||||||
{
|
{
|
||||||
char buffer[80];
|
char buffer[80];
|
||||||
switch (can_id)
|
switch (can_id)
|
||||||
{
|
{
|
||||||
case CB_BATTERY_LIMITS:
|
case CB_BATTERY_LIMITS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for BATTERY LIMITS info";
|
return "Request for BATTERY LIMITS info";
|
||||||
}
|
}
|
||||||
if (this->frame.size() < 6) {
|
if (this->frame.size() < 6) {
|
||||||
return "Invalid frame size for CB_BATTERY_LIMITS";
|
return "Invalid frame size for CB_BATTERY_LIMITS";
|
||||||
}
|
}
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
float battery_charge_voltage_limit = 0.1 * ((x[1] << 8) + x[0]); // unit = 0.1V
|
float battery_charge_voltage_limit = 0.1 * ((x[1] << 8) + x[0]); // unit = 0.1V
|
||||||
float charge_current_limit = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A
|
float charge_current_limit = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A
|
||||||
float discharge_current_limit = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1A
|
float discharge_current_limit = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1A
|
||||||
snprintf(buffer, sizeof(buffer), "BATTERY MAX CHARGE: VMax= %.1fV IMaxChg= %.1fA IMaxDis= %.1fA", battery_charge_voltage_limit, charge_current_limit, discharge_current_limit);
|
snprintf(buffer, sizeof(buffer), "BATTERY MAX CHARGE: VMax= %.1fV IMaxChg= %.1fA IMaxDis= %.1fA", battery_charge_voltage_limit, charge_current_limit, discharge_current_limit);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CB_BATTERY_STATE:
|
case CB_BATTERY_STATE:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for BATTERY STATE info";
|
return "Request for BATTERY STATE info";
|
||||||
}
|
}
|
||||||
if (this->frame.size() < 4) {
|
if (this->frame.size() < 4) {
|
||||||
return "Invalid frame size for CB_BATTERY_STATE";
|
return "Invalid frame size for CB_BATTERY_STATE";
|
||||||
}
|
}
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
uint soc = static_cast<uint16_t>((x[1] << 8) + x[0]);
|
uint soc = static_cast<uint16_t>((x[1] << 8) + x[0]);
|
||||||
uint soh = static_cast<uint16_t>((x[3] << 8) + x[2]);
|
uint soh = static_cast<uint16_t>((x[3] << 8) + x[2]);
|
||||||
snprintf(buffer, sizeof(buffer), "BATTERY STATE: SOC= %d%% SOH= %d%%", soc, soh);
|
snprintf(buffer, sizeof(buffer), "BATTERY STATE: SOC= %d%% SOH= %d%%", soc, soh);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CB_BATTERY_STATUS:
|
case CB_BATTERY_STATUS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for BATTERY STATUS info";
|
return "Request for BATTERY STATUS info";
|
||||||
}
|
}
|
||||||
if (this->frame.size() < 6) {
|
if (this->frame.size() < 6) {
|
||||||
return "Invalid frame size for CB_BATTERY_STATUS";
|
return "Invalid frame size for CB_BATTERY_STATUS";
|
||||||
}
|
}
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
float system_voltage = 0.01 * static_cast<int16_t>((x[1] << 8) + x[0]); // unit = 0.01V Voltage of single module or average module voltage of system
|
float system_voltage = 0.01 * static_cast<int16_t>((x[1] << 8) + x[0]); // unit = 0.01V Voltage of single module or average module voltage of system
|
||||||
float system_current = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A Module or system total current
|
float system_current = 0.1 * static_cast<int16_t>((x[3] << 8) + x[2]); // unit = 0.1A Module or system total current
|
||||||
float average_cell_temperature = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1°C
|
float average_cell_temperature = 0.1 * static_cast<int16_t>((x[5] << 8) + x[4]); // unit = 0.1°C
|
||||||
snprintf(buffer, sizeof(buffer), "BATTERY STATUS: VSYS= %.2fV ISYS= %.1fA TSYS= %.1f°C", system_voltage, system_current, average_cell_temperature);
|
snprintf(buffer, sizeof(buffer), "BATTERY STATUS: VSYS= %.2fV ISYS= %.1fA TSYS= %.1f°C", system_voltage, system_current, average_cell_temperature);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CB_BATTERY_FAULT:
|
case CB_BATTERY_FAULT:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for BATTERY FAULT info";
|
return "Request for BATTERY FAULT info";
|
||||||
}
|
}
|
||||||
if (this->frame.size() < 8) {
|
if (this->frame.size() < 8) {
|
||||||
return "Invalid frame size for CB_BATTERY_FAULT";
|
return "Invalid frame size for CB_BATTERY_FAULT";
|
||||||
}
|
}
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
uint8_t protection1 = x[0];
|
uint8_t protection1 = x[0];
|
||||||
uint8_t protection2 = x[1];
|
uint8_t protection2 = x[1];
|
||||||
uint8_t alarm1 = x[2];
|
uint8_t alarm1 = x[2];
|
||||||
uint8_t alarm2 = x[3];
|
uint8_t alarm2 = x[3];
|
||||||
uint8_t module_numbers = x[4];
|
uint8_t module_numbers = x[4];
|
||||||
char ch5 = x[5];
|
char ch5 = x[5];
|
||||||
char ch6 = x[6];
|
char ch6 = x[6];
|
||||||
bool discharge_over_current = protection1 & 0x80;
|
bool discharge_over_current = protection1 & 0x80;
|
||||||
bool cell_under_temperature = protection1 & 0x10;
|
bool cell_under_temperature = protection1 & 0x10;
|
||||||
bool cell_over_temperature = protection1 & 0x08;
|
bool cell_over_temperature = protection1 & 0x08;
|
||||||
bool cell_or_module_under_voltage = protection1 & 0x04;
|
bool cell_or_module_under_voltage = protection1 & 0x04;
|
||||||
bool cell_or_module_over_voltage = protection1 & 0x02;
|
bool cell_or_module_over_voltage = protection1 & 0x02;
|
||||||
bool system_error = protection2 & 0x8;
|
bool system_error = protection2 & 0x8;
|
||||||
bool charge_over_current = protection2 & 0x01;
|
bool charge_over_current = protection2 & 0x01;
|
||||||
bool discharge_high_current = alarm1 & 0x80;
|
bool discharge_high_current = alarm1 & 0x80;
|
||||||
bool cell_low_temperature = alarm1 & 0x10;
|
bool cell_low_temperature = alarm1 & 0x10;
|
||||||
bool cell_high_temperature = alarm1 & 0x08;
|
bool cell_high_temperature = alarm1 & 0x08;
|
||||||
bool cell_or_module_low_voltage = alarm1 & 0x04;
|
bool cell_or_module_low_voltage = alarm1 & 0x04;
|
||||||
bool cell_or_module_high_voltage = alarm1 & 0x02;
|
bool cell_or_module_high_voltage = alarm1 & 0x02;
|
||||||
bool internal_communication_fail = alarm2 & 0x8;
|
bool internal_communication_fail = alarm2 & 0x8;
|
||||||
bool charge_high_current = alarm2 & 0x01;
|
bool charge_high_current = alarm2 & 0x01;
|
||||||
snprintf(buffer, sizeof(buffer), "BATTERY PROTECT: %s%s%s%s%s%s%s ALARM= %s%s%s%s%s%s%s MN=%d %c%c", discharge_over_current ? "DOC " : "", cell_under_temperature ? "CUT " : "", cell_over_temperature ? "COT " : "", cell_or_module_under_voltage ? "CMUV " : "", cell_or_module_over_voltage ? "CMOV" : "", system_error ? "SERR " : "", charge_over_current ? "COC ": "", discharge_high_current ? "DHC " : "", cell_low_temperature ? "CLT " : "", cell_high_temperature ? "CHT " : "", cell_or_module_low_voltage ? "CMLV " : "", cell_or_module_high_voltage ? "CMHV" : "", internal_communication_fail ? "ICF " : "", charge_high_current ? "CHC ": "", module_numbers, ch5, ch6);
|
snprintf(buffer, sizeof(buffer), "BATTERY PROTECT: %s%s%s%s%s%s%s ALARM= %s%s%s%s%s%s%s MN=%d %c%c", discharge_over_current ? "DOC " : "", cell_under_temperature ? "CUT " : "", cell_over_temperature ? "COT " : "", cell_or_module_under_voltage ? "CMUV " : "", cell_or_module_over_voltage ? "CMOV" : "", system_error ? "SERR " : "", charge_over_current ? "COC ": "", discharge_high_current ? "DHC " : "", cell_low_temperature ? "CLT " : "", cell_high_temperature ? "CHT " : "", cell_or_module_low_voltage ? "CMLV " : "", cell_or_module_high_voltage ? "CMHV" : "", internal_communication_fail ? "ICF " : "", charge_high_current ? "CHC ": "", module_numbers, ch5, ch6);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CB_BATTERY_REQUEST_FLAGS:
|
case CB_BATTERY_REQUEST_FLAGS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for BATTERY REQUEST FLAGS info";
|
return "Request for BATTERY REQUEST FLAGS info";
|
||||||
}
|
}
|
||||||
if (this->frame.size() < 1) {
|
if (this->frame.size() < 1) {
|
||||||
return "Invalid frame size for CB_BATTERY_REQUEST_FLAGS";
|
return "Invalid frame size for CB_BATTERY_REQUEST_FLAGS";
|
||||||
}
|
}
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
uint8_t request_flag = x[0];
|
uint8_t request_flag = x[0];
|
||||||
bool charge_enable = request_flag & 0x80;
|
bool charge_enable = request_flag & 0x80;
|
||||||
bool discharge_enable = request_flag & 0x40;
|
bool discharge_enable = request_flag & 0x40;
|
||||||
bool request_force_charge1 = request_flag & 0x20; // use bit 5, the SOC range is: 15~19%. Bit 4 is NULL. Bit 5 is designed for inverter allows battery to shut down, and able to wake battery up to charge it.
|
bool request_force_charge1 = request_flag & 0x20; // use bit 5, the SOC range is: 15~19%. Bit 4 is NULL. Bit 5 is designed for inverter allows battery to shut down, and able to wake battery up to charge it.
|
||||||
bool request_force_charge2 = request_flag & 0x10; // Bit 5 the SOC range is 5~10%, Bit 4 the SOC range is 9~13%. Bit 4 is designed for inverter doesn`t want battery to shut down, able to charge battery before shut down to avoid low energy. We suggest inverter to use this bit, In this case, inverter itself should set a threshold of SOC: after force charge, only when battery SOC is higher than this threshold then inverter will allow discharge, to avoid force charge and discharge status change frequently.
|
bool request_force_charge2 = request_flag & 0x10; // Bit 5 the SOC range is 5~10%, Bit 4 the SOC range is 9~13%. Bit 4 is designed for inverter doesn`t want battery to shut down, able to charge battery before shut down to avoid low energy. We suggest inverter to use this bit, In this case, inverter itself should set a threshold of SOC: after force charge, only when battery SOC is higher than this threshold then inverter will allow discharge, to avoid force charge and discharge status change frequently.
|
||||||
bool request_full_charge = request_flag & 0x08; // Reason: when battery is not full charged for long time, the accumulative error of SOC calculation will be too high and may not able to be charged or discharged as expected capacity. Logic: if SOC never higher than 97% in 30 days, will set this flag to 1. And when the SOC is 97%, the flag will be 0. How to: we suggest inverter to charge the battery by grid when this flag is 1.
|
bool request_full_charge = request_flag & 0x08; // Reason: when battery is not full charged for long time, the accumulative error of SOC calculation will be too high and may not able to be charged or discharged as expected capacity. Logic: if SOC never higher than 97% in 30 days, will set this flag to 1. And when the SOC is 97%, the flag will be 0. How to: we suggest inverter to charge the battery by grid when this flag is 1.
|
||||||
snprintf(buffer, sizeof(buffer), "BATTERY REQUEST: %s%s%s%s%s", charge_enable ? "CE " : "", discharge_enable ? "DE " : "", request_force_charge1 ? "RFORCECH1 " : "", request_force_charge2 ? "RFORCECH2 " : "", request_full_charge ? "RFULLCH" : "");
|
snprintf(buffer, sizeof(buffer), "BATTERY REQUEST: %s%s%s%s%s", charge_enable ? "CE " : "", discharge_enable ? "DE " : "", request_force_charge1 ? "RFORCECH1 " : "", request_force_charge2 ? "RFORCECH2 " : "", request_full_charge ? "RFULLCH" : "");
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CB_BATTERY_MANUFACTURER:
|
case CB_BATTERY_MANUFACTURER:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for BATTERY MANUFACTURER info";
|
return "Request for BATTERY MANUFACTURER info";
|
||||||
}
|
}
|
||||||
if (this->frame.size() < 8) {
|
if (this->frame.size() < 8) {
|
||||||
return "Invalid frame size for CB_BATTERY_MANUFACTURER";
|
return "Invalid frame size for CB_BATTERY_MANUFACTURER";
|
||||||
}
|
}
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
// Manufacturer name is in the first 8 bytes, padded with spaces
|
// Manufacturer name is in the first 8 bytes, padded with spaces
|
||||||
// Convert to string and trim trailing spaces
|
// Convert to string and trim trailing spaces
|
||||||
std::string manufacturer(reinterpret_cast<const char*>(x.data()), 8);
|
std::string manufacturer(reinterpret_cast<const char*>(x.data()), 8);
|
||||||
manufacturer.erase(std::find_if(manufacturer.rbegin(), manufacturer.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), manufacturer.end());
|
manufacturer.erase(std::find_if(manufacturer.rbegin(), manufacturer.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), manufacturer.end());
|
||||||
return "BATTERY OEM: " + manufacturer;
|
return "BATTERY OEM: " + manufacturer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for unknown pylon CAN ID";
|
return "Request for unknown pylon CAN ID";
|
||||||
}
|
}
|
||||||
return "Unknown CAN ID";
|
return "Unknown CAN ID";
|
||||||
}
|
}
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
@ -1,40 +1,40 @@
|
|||||||
#ifndef __SOLAR_CBF_PYLON // include GUARD
|
#ifndef __SOLAR_CBF_PYLON // include GUARD
|
||||||
#define __SOLAR_CBF_PYLON
|
#define __SOLAR_CBF_PYLON
|
||||||
#include "esphome.h"
|
#include "esphome.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cb_frame.h"
|
#include "cb_frame.h"
|
||||||
using namespace esphome;
|
using namespace esphome;
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
class cbf_pylon : virtual public cb_frame {
|
class cbf_pylon : virtual public cb_frame {
|
||||||
public:
|
public:
|
||||||
// Pylontech publish spec
|
// Pylontech publish spec
|
||||||
// This is used to determine when to publish the battery data from the Pylontech battery
|
// This is used to determine when to publish the battery data from the Pylontech battery
|
||||||
static const publish_spec_t publish_spec; // default publish spec for Pylontech battery messages
|
static const publish_spec_t publish_spec; // default publish spec for Pylontech battery messages
|
||||||
static const publish_spec_t rtr_publish_spec; // for remote transmission requests
|
static const publish_spec_t rtr_publish_spec; // for remote transmission requests
|
||||||
|
|
||||||
// Pylontech battery CAN IDs
|
// Pylontech battery CAN IDs
|
||||||
// https://domosimple.eu/en/documentation/manuels/pylontech-can-bus-protocole
|
// https://domosimple.eu/en/documentation/manuels/pylontech-can-bus-protocole
|
||||||
// VERIFIED IDs have been confirmed by capturing actual CAN bus traffic from a Pylontech battery
|
// VERIFIED IDs have been confirmed by capturing actual CAN bus traffic from a Pylontech battery
|
||||||
static const int CB_BATTERY_LIMITS = 0x351; // VERIFIED: Pylontech battery max charging/discharging values message
|
static const int CB_BATTERY_LIMITS = 0x351; // VERIFIED: Pylontech battery max charging/discharging values message
|
||||||
static const int CB_BATTERY_STATE = 0x355; // VERIFIED: Pylontech battery state message
|
static const int CB_BATTERY_STATE = 0x355; // VERIFIED: Pylontech battery state message
|
||||||
static const int CB_BATTERY_STATUS = 0x356; // VERIFIED: Pylontech battery status message
|
static const int CB_BATTERY_STATUS = 0x356; // VERIFIED: Pylontech battery status message
|
||||||
static const int CB_BATTERY_FAULT = 0x359; // VERIFIED: Pylontech battery fault message
|
static const int CB_BATTERY_FAULT = 0x359; // VERIFIED: Pylontech battery fault message
|
||||||
static const int CB_BATTERY_REQUEST_FLAGS = 0x35C; // VERIFIED: Pylontech battery request flag message
|
static const int CB_BATTERY_REQUEST_FLAGS = 0x35C; // VERIFIED: Pylontech battery request flag message
|
||||||
static const int CB_BATTERY_MANUFACTURER = 0x35E; // VERIFIED: Pylontech battery manufacturer message
|
static const int CB_BATTERY_MANUFACTURER = 0x35E; // VERIFIED: Pylontech battery manufacturer message
|
||||||
|
|
||||||
//Property<int, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_set_battery_charge_voltage_limit, &cbf_pylon::_get_battery_charge_voltage_limit };
|
//Property<int, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_set_battery_charge_voltage_limit, &cbf_pylon::_get_battery_charge_voltage_limit };
|
||||||
//Property<float, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_get_battery_charge_voltage_limit};
|
//Property<float, cbf_pylon> battery_charge_voltage_limit {this, &cbf_pylon::_get_battery_charge_voltage_limit};
|
||||||
|
|
||||||
cbf_pylon() = default;
|
cbf_pylon() = default;
|
||||||
cbf_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
|
cbf_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
|
||||||
void clear();
|
void clear();
|
||||||
virtual std::string to_string() const override;
|
virtual std::string to_string() const override;
|
||||||
cbf_pylon& operator=(cbf_pylon&& src); // required due to double inheritance in cbf_store_pylon
|
cbf_pylon& operator=(cbf_pylon&& src); // required due to double inheritance in cbf_store_pylon
|
||||||
// using default (compiler auto generated) copy and move constructors and assignment operators
|
// using default (compiler auto generated) copy and move constructors and assignment operators
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
#endif // __SOLAR_CBF_PYLON
|
#endif // __SOLAR_CBF_PYLON
|
||||||
@ -1,342 +1,342 @@
|
|||||||
#include "cbf_sthome.h"
|
#include "cbf_sthome.h"
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
// see common.h for definition of publish_spec_t
|
// see common.h for definition of publish_spec_t
|
||||||
// publish_spec_t(int on_count, int interval, int timeout)
|
// publish_spec_t(int on_count, int interval, int timeout)
|
||||||
const publish_spec_t cbf_sthome::publish_spec = publish_spec_t(3, 15, 30); // default publish spec
|
const publish_spec_t cbf_sthome::publish_spec = publish_spec_t(3, 15, 30); // default publish spec
|
||||||
const publish_spec_t cbf_sthome::rtr_publish_spec = publish_spec_t(2, 10, 40); // for remote transmission requests
|
const publish_spec_t cbf_sthome::rtr_publish_spec = publish_spec_t(2, 10, 40); // for remote transmission requests
|
||||||
|
|
||||||
const std::string cbf_sthome::heartbeat = "HELLO\0\0\0"; // must be exactly 8 bytes including null terminators
|
const std::string cbf_sthome::heartbeat = "HELLO\0\0\0"; // must be exactly 8 bytes including null terminators
|
||||||
|
|
||||||
//cbf_sthome::cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr)
|
//cbf_sthome::cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr)
|
||||||
// : cb_frame(msg_id, can_id, frame, rtr)
|
// : cb_frame(msg_id, can_id, frame, rtr)
|
||||||
// {
|
// {
|
||||||
// }
|
// }
|
||||||
cbf_sthome& cbf_sthome::operator=(cbf_sthome&& src)
|
cbf_sthome& cbf_sthome::operator=(cbf_sthome&& src)
|
||||||
{
|
{
|
||||||
if (this != &src) {
|
if (this != &src) {
|
||||||
cb_frame::operator=(std::move(src));
|
cb_frame::operator=(std::move(src));
|
||||||
src.clear();
|
src.clear();
|
||||||
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
|
//ESP_LOGI(tag("pylon MOASS").c_str(), "%-20s %s", "Assigned pylon", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
void cbf_sthome::clear()
|
void cbf_sthome::clear()
|
||||||
{
|
{
|
||||||
cb_frame::clear();
|
cb_frame::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cbf_sthome::verify_heartbeat() const
|
bool cbf_sthome::verify_heartbeat() const
|
||||||
{
|
{
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
std::string hb(reinterpret_cast<const char*>(x.data()), x.size());
|
std::string hb(reinterpret_cast<const char*>(x.data()), x.size());
|
||||||
hb.erase(std::find_if(hb.rbegin(), hb.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), hb.end());
|
hb.erase(std::find_if(hb.rbegin(), hb.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), hb.end());
|
||||||
return hb == heartbeat;
|
return hb == heartbeat;
|
||||||
}
|
}
|
||||||
// float cbf_sthome::_get_battery_charge_voltage_limit() const
|
// float cbf_sthome::_get_battery_charge_voltage_limit() const
|
||||||
// {
|
// {
|
||||||
// const auto& x = this->frame;
|
// const auto& x = this->frame;
|
||||||
// return 0.1 * (float)((x[1] << 8) + x[0]);
|
// return 0.1 * (float)((x[1] << 8) + x[0]);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Function to build a message from the pylon canbus frame
|
// Function to build a message from the pylon canbus frame
|
||||||
std::string cbf_sthome::to_string() const
|
std::string cbf_sthome::to_string() const
|
||||||
{
|
{
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
const auto& x = this->frame;
|
const auto& x = this->frame;
|
||||||
switch (can_id)
|
switch (can_id)
|
||||||
{
|
{
|
||||||
case CB_CANBUS_ID01:
|
case CB_CANBUS_ID01:
|
||||||
return rtr ? "Request for sthome-ut1 heartbeat" : verify_heartbeat() ? "sthome-ut1 alive" : "";
|
return rtr ? "Request for sthome-ut1 heartbeat" : verify_heartbeat() ? "sthome-ut1 alive" : "";
|
||||||
case CB_CANBUS_ID02:
|
case CB_CANBUS_ID02:
|
||||||
return rtr ? "Request for sthome-ut2 heartbeat" : verify_heartbeat() ? "sthome-ut2 alive" : "";
|
return rtr ? "Request for sthome-ut2 heartbeat" : verify_heartbeat() ? "sthome-ut2 alive" : "";
|
||||||
case CB_CANBUS_ID03:
|
case CB_CANBUS_ID03:
|
||||||
return rtr ? "Request for sthome-u3 heartbeat" : verify_heartbeat() ? "sthome-ut3 alive " : "";
|
return rtr ? "Request for sthome-u3 heartbeat" : verify_heartbeat() ? "sthome-ut3 alive " : "";
|
||||||
case CB_CANBUS_ID04:
|
case CB_CANBUS_ID04:
|
||||||
return rtr ? "Request for sthome-ut4 heartbeat" : verify_heartbeat() ? "sthome-ut4 alive" : "";
|
return rtr ? "Request for sthome-ut4 heartbeat" : verify_heartbeat() ? "sthome-ut4 alive" : "";
|
||||||
case CB_CANBUS_ID05:
|
case CB_CANBUS_ID05:
|
||||||
return rtr ? "Request for sthome-ut5 heartbeat" : verify_heartbeat() ? "sthome-ut5 alive" : "";
|
return rtr ? "Request for sthome-ut5 heartbeat" : verify_heartbeat() ? "sthome-ut5 alive" : "";
|
||||||
case CB_CANBUS_ID06:
|
case CB_CANBUS_ID06:
|
||||||
return rtr ? "Request for sthome-ut6 heartbeat" : verify_heartbeat() ? "sthome-ut6 alive" : "";
|
return rtr ? "Request for sthome-ut6 heartbeat" : verify_heartbeat() ? "sthome-ut6 alive" : "";
|
||||||
case CB_CANBUS_ID07:
|
case CB_CANBUS_ID07:
|
||||||
return rtr ? "Request for sthome-ut7 heartbeat" : verify_heartbeat() ? "sthome-ut7 alive" : "";
|
return rtr ? "Request for sthome-ut7 heartbeat" : verify_heartbeat() ? "sthome-ut7 alive" : "";
|
||||||
case CB_CANBUS_ID08:
|
case CB_CANBUS_ID08:
|
||||||
return rtr ? "Request for sthome-ut8 heartbeat" : verify_heartbeat() ? "sthome-ut8 alive" : "";
|
return rtr ? "Request for sthome-ut8 heartbeat" : verify_heartbeat() ? "sthome-ut8 alive" : "";
|
||||||
case CB_CANBUS_ID09:
|
case CB_CANBUS_ID09:
|
||||||
return rtr ? "Request for sthome-ut9 heartbeat" : verify_heartbeat() ? "sthome-ut9 alive" : "";
|
return rtr ? "Request for sthome-ut9 heartbeat" : verify_heartbeat() ? "sthome-ut9 alive" : "";
|
||||||
case CB_CANBUS_ID10:
|
case CB_CANBUS_ID10:
|
||||||
return rtr ? "Request for sthome-ut10 heartbeat" : verify_heartbeat() ? "sthome-ut10 alive" : "";
|
return rtr ? "Request for sthome-ut10 heartbeat" : verify_heartbeat() ? "sthome-ut10 alive" : "";
|
||||||
case CB_GEYSER_TEMPERATURE_TOP:
|
case CB_GEYSER_TEMPERATURE_TOP:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for geyser top temp";
|
return "Request for geyser top temp";
|
||||||
}
|
}
|
||||||
double temp_top = get_double(x[0], x[1], 256, true);
|
double temp_top = get_double(x[0], x[1], 256, true);
|
||||||
snprintf (buffer, sizeof(buffer), "GEYSER TOP: %.2f°C", temp_top);
|
snprintf (buffer, sizeof(buffer), "GEYSER TOP: %.2f°C", temp_top);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_GEYSER_TEMPERATURE_BOTTOM:
|
case CB_GEYSER_TEMPERATURE_BOTTOM:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for geyser bottom temp";
|
return "Request for geyser bottom temp";
|
||||||
}
|
}
|
||||||
double temp_bot = get_double(x[0], x[1], 256, true);
|
double temp_bot = get_double(x[0], x[1], 256, true);
|
||||||
snprintf (buffer, sizeof(buffer), "GEYSER BOT: %.2f°C", temp_bot);
|
snprintf (buffer, sizeof(buffer), "GEYSER BOT: %.2f°C", temp_bot);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_GEYSER_TEMPERATURE_AMBIENT:
|
case CB_GEYSER_TEMPERATURE_AMBIENT:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for geyser ambient temp";
|
return "Request for geyser ambient temp";
|
||||||
}
|
}
|
||||||
double temp_amb = get_double(x[0], x[1], 256, true);
|
double temp_amb = get_double(x[0], x[1], 256, true);
|
||||||
snprintf (buffer, sizeof(buffer), "GEYSER AMB: %.2f°C", temp_amb);
|
snprintf (buffer, sizeof(buffer), "GEYSER AMB: %.2f°C", temp_amb);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_GEYSER_HEATING:
|
case CB_GEYSER_HEATING:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for geyser heating";
|
return "Request for geyser heating";
|
||||||
}
|
}
|
||||||
double heating_loss = get_double(x[0], x[1], 64, true); // -512W to 512W
|
double heating_loss = get_double(x[0], x[1], 64, true); // -512W to 512W
|
||||||
double heat_gained = get_double(x[2], x[3], 128); // 0 to 512W
|
double heat_gained = get_double(x[2], x[3], 128); // 0 to 512W
|
||||||
double calculated_heat_loss = get_double(x[4], x[5], 128); // 0 to 512W
|
double calculated_heat_loss = get_double(x[4], x[5], 128); // 0 to 512W
|
||||||
unsigned int est_heating_time = (unsigned int) static_cast<uint16_t>(256 * x[7] + x[6]); // 0 to 65536 seconds
|
unsigned int est_heating_time = (unsigned int) static_cast<uint16_t>(256 * x[7] + x[6]); // 0 to 65536 seconds
|
||||||
snprintf (buffer, sizeof(buffer), "Heat: loss %.3fW, gain %.3fW, calc loss %.3fW, est. heat time %ds", heating_loss, heat_gained, calculated_heat_loss, est_heating_time);
|
snprintf (buffer, sizeof(buffer), "Heat: loss %.3fW, gain %.3fW, calc loss %.3fW, est. heat time %ds", heating_loss, heat_gained, calculated_heat_loss, est_heating_time);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_GEYSER_ACTIVE_SCHEDULE:
|
case CB_GEYSER_ACTIVE_SCHEDULE:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for geyser schedule";
|
return "Request for geyser schedule";
|
||||||
}
|
}
|
||||||
double active_schedule_temp = get_double(x[0], x[1], 256, true); // -128 to 128°C
|
double active_schedule_temp = get_double(x[0], x[1], 256, true); // -128 to 128°C
|
||||||
int active_heating_time = static_cast<int16_t>(256 * x[3] + x[2]); // -32768 to 32768s
|
int active_heating_time = static_cast<int16_t>(256 * x[3] + x[2]); // -32768 to 32768s
|
||||||
double heating_overshoot_time = get_double(x[4], x[5], -64); // -512 to 512s
|
double heating_overshoot_time = get_double(x[4], x[5], -64); // -512 to 512s
|
||||||
int active_schedule_day = static_cast<uint8_t>(x[6]);
|
int active_schedule_day = static_cast<uint8_t>(x[6]);
|
||||||
snprintf (buffer, sizeof(buffer), "Geyser Schedule: target %.2f°C, heating time %ds, overshoot %.1fs, day %d", active_schedule_temp, active_heating_time, heating_overshoot_time, active_schedule_day);
|
snprintf (buffer, sizeof(buffer), "Geyser Schedule: target %.2f°C, heating time %ds, overshoot %.1fs, day %d", active_schedule_temp, active_heating_time, heating_overshoot_time, active_schedule_day);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_POWER_MAINS:
|
case CB_POWER_MAINS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for power mains";
|
return "Request for power mains";
|
||||||
}
|
}
|
||||||
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
||||||
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
||||||
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
||||||
snprintf (buffer, sizeof(buffer), "Mains : %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
snprintf (buffer, sizeof(buffer), "Mains : %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_POWER_INVERTER:
|
case CB_POWER_INVERTER:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for power inverter";
|
return "Request for power inverter";
|
||||||
}
|
}
|
||||||
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
||||||
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
||||||
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
||||||
snprintf (buffer, sizeof(buffer), "InvOut: %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
snprintf (buffer, sizeof(buffer), "InvOut: %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_POWER_PLUGS:
|
case CB_POWER_PLUGS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for power plugs";
|
return "Request for power plugs";
|
||||||
}
|
}
|
||||||
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
||||||
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
||||||
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
||||||
snprintf (buffer, sizeof(buffer), "Plugs : %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
snprintf (buffer, sizeof(buffer), "Plugs : %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_POWER_LIGHTS:
|
case CB_POWER_LIGHTS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for power lights";
|
return "Request for power lights";
|
||||||
}
|
}
|
||||||
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
||||||
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
||||||
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
||||||
snprintf (buffer, sizeof(buffer), "Lights: %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
snprintf (buffer, sizeof(buffer), "Lights: %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_POWER_GEYSER:
|
case CB_POWER_GEYSER:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for power geyser";
|
return "Request for power geyser";
|
||||||
}
|
}
|
||||||
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
||||||
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
||||||
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
||||||
snprintf (buffer, sizeof(buffer), "Geyser: %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
snprintf (buffer, sizeof(buffer), "Geyser: %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_POWER_POOL:
|
case CB_POWER_POOL:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for power pool";
|
return "Request for power pool";
|
||||||
}
|
}
|
||||||
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
double power = get_double(x[0], x[1], 2048); // 0 to 32kW
|
||||||
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
double voltage = get_double(x[2], x[3], 128); // 0 to 512V
|
||||||
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
double current = get_double(x[4], x[5], 512); // 0 to 128A
|
||||||
snprintf (buffer, sizeof(buffer), "Pool : %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
snprintf (buffer, sizeof(buffer), "Pool : %.3fV, %.3fA, %.3fkW", voltage, current, power);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_POWER_GENERATED:
|
case CB_POWER_GENERATED:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for power generated";
|
return "Request for power generated";
|
||||||
}
|
}
|
||||||
double power_generated = get_double(x[0], x[1], 2048); // 0 to 32kW
|
double power_generated = get_double(x[0], x[1], 2048); // 0 to 32kW
|
||||||
double power_loss = get_double(x[2], x[3], 2048); // 0 to 32kW
|
double power_loss = get_double(x[2], x[3], 2048); // 0 to 32kW
|
||||||
snprintf (buffer, sizeof(buffer), "Power : Generated %.3fkW, Loss %.3fkW", power_generated, power_loss);
|
snprintf (buffer, sizeof(buffer), "Power : Generated %.3fkW, Loss %.3fkW", power_generated, power_loss);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_MAINS:
|
case CB_ENERGY_MAINS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy mains";
|
return "Request for energy mains";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "Mains Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "Mains Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_GEYSER:
|
case CB_ENERGY_GEYSER:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy geyser";
|
return "Request for energy geyser";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "Geyser Energy day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "Geyser Energy day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_POOL:
|
case CB_ENERGY_POOL:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy pool";
|
return "Request for energy pool";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "Pool Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "Pool Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_PLUGS:
|
case CB_ENERGY_PLUGS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy plugs";
|
return "Request for energy plugs";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "Plugs Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "Plugs Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_LIGHTS:
|
case CB_ENERGY_LIGHTS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy lights";
|
return "Request for energy lights";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "Lights Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "Lights Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_HOUSE:
|
case CB_ENERGY_HOUSE:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy house";
|
return "Request for energy house";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "House Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "House Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_GENERATED:
|
case CB_ENERGY_GENERATED:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy generated";
|
return "Request for energy generated";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "Generated Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "Generated Energy: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_ENERGY_LOSS:
|
case CB_ENERGY_LOSS:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for energy loss";
|
return "Request for energy loss";
|
||||||
}
|
}
|
||||||
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
double energy_daily = get_double(x[0], x[1], 512); // 0 to 128kWh
|
||||||
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
double energy_monthly = get_double(x[2], x[3], 32); // 0 to 2048kWh
|
||||||
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
double energy_yearly = get_double(x[4], x[5], 2); // 0 to 32768kWh
|
||||||
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
double energy_lifetime = 0.01 * ((x[7] << 8) + x[6]); // 0 to 655MWh
|
||||||
snprintf (buffer, sizeof(buffer), "Energy Loss: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
snprintf (buffer, sizeof(buffer), "Energy Loss: day %.3fkWh, month %.3fkWh, year %.1fkWh, lifetime %.2fMWh", energy_daily, energy_monthly, energy_yearly, energy_lifetime);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
case CB_CONTROLLER_STATES:
|
case CB_CONTROLLER_STATES:
|
||||||
{
|
{
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for controller states";
|
return "Request for controller states";
|
||||||
}
|
}
|
||||||
uint8_t alarms = x[0];
|
uint8_t alarms = x[0];
|
||||||
uint8_t states = x[1];
|
uint8_t states = x[1];
|
||||||
uint8_t modes = x[2];
|
uint8_t modes = x[2];
|
||||||
bool inverter_1_battery_low = alarms & 0x80;
|
bool inverter_1_battery_low = alarms & 0x80;
|
||||||
bool inverter_2_battery_low = alarms & 0x10;
|
bool inverter_2_battery_low = alarms & 0x10;
|
||||||
bool inverter_overload = alarms & 0x08;
|
bool inverter_overload = alarms & 0x08;
|
||||||
bool geyser_heating = states & 0x80;
|
bool geyser_heating = states & 0x80;
|
||||||
bool geyser_energised = states & 0x40;
|
bool geyser_energised = states & 0x40;
|
||||||
bool mains_supply_present = states & 0x20;
|
bool mains_supply_present = states & 0x20;
|
||||||
bool battery_charging = states & 0x08;
|
bool battery_charging = states & 0x08;
|
||||||
bool vacation_mode = modes & 0x80; // nobody at home
|
bool vacation_mode = modes & 0x80; // nobody at home
|
||||||
std::string mode = "";
|
std::string mode = "";
|
||||||
int geysermode = (modes & 0x70) >> 4;
|
int geysermode = (modes & 0x70) >> 4;
|
||||||
switch (geysermode) {
|
switch (geysermode) {
|
||||||
case GM_SUNDAY:
|
case GM_SUNDAY:
|
||||||
mode = "SUN";
|
mode = "SUN";
|
||||||
break;
|
break;
|
||||||
case GM_WORKDAY:
|
case GM_WORKDAY:
|
||||||
mode = "WRK";
|
mode = "WRK";
|
||||||
break;
|
break;
|
||||||
case GM_SATURDAY:
|
case GM_SATURDAY:
|
||||||
mode = "SAT";
|
mode = "SAT";
|
||||||
break;
|
break;
|
||||||
case GM_PUBLIC_HOLIDAY:
|
case GM_PUBLIC_HOLIDAY:
|
||||||
mode = "PUB";
|
mode = "PUB";
|
||||||
break;
|
break;
|
||||||
case GM_SCHOOL_HOLIDAY:
|
case GM_SCHOOL_HOLIDAY:
|
||||||
mode = "SCH";
|
mode = "SCH";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mode = "UNK";
|
mode = "UNK";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
snprintf(buffer, sizeof(buffer), "STATES: %s%s%s%s MODE: %s%s INV: %s%s%s", geyser_heating ? "HEAT " : "", geyser_energised ? "GEYSER " : "", mains_supply_present ? "MAINS " : "", battery_charging ? "BATCHG " : "", vacation_mode ? "VACA ": "", mode.c_str(), inverter_1_battery_low ? "INV LOBAT " : "", inverter_2_battery_low ? "INV2 LOBAT " : "", inverter_overload ? "INV OVL " : "");
|
snprintf(buffer, sizeof(buffer), "STATES: %s%s%s%s MODE: %s%s INV: %s%s%s", geyser_heating ? "HEAT " : "", geyser_energised ? "GEYSER " : "", mains_supply_present ? "MAINS " : "", battery_charging ? "BATCHG " : "", vacation_mode ? "VACA ": "", mode.c_str(), inverter_1_battery_low ? "INV LOBAT " : "", inverter_2_battery_low ? "INV2 LOBAT " : "", inverter_overload ? "INV OVL " : "");
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if(rtr) {
|
if(rtr) {
|
||||||
return "Request for unknown sthome CAN ID";
|
return "Request for unknown sthome CAN ID";
|
||||||
}
|
}
|
||||||
return "Unknown CAN ID";
|
return "Unknown CAN ID";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
@ -1,78 +1,78 @@
|
|||||||
#ifndef __SOLAR_CBF_STHOME_H // include GUARD
|
#ifndef __SOLAR_CBF_STHOME_H // include GUARD
|
||||||
#define __SOLAR_CBF_STHOME_H
|
#define __SOLAR_CBF_STHOME_H
|
||||||
#include "esphome.h"
|
#include "esphome.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cb_frame.h"
|
#include "cb_frame.h"
|
||||||
using namespace esphome;
|
using namespace esphome;
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
|
|
||||||
class cbf_sthome : virtual public cb_frame {
|
class cbf_sthome : virtual public cb_frame {
|
||||||
private:
|
private:
|
||||||
// float _get_battery_charge_voltage_limit() const;
|
// float _get_battery_charge_voltage_limit() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// STHOME publish spec
|
// STHOME publish spec
|
||||||
// This is used to determine when to publish the sthome data
|
// This is used to determine when to publish the sthome data
|
||||||
static const publish_spec_t publish_spec; // default publish spec for sthome messages
|
static const publish_spec_t publish_spec; // default publish spec for sthome messages
|
||||||
static const publish_spec_t rtr_publish_spec; // for remote transmission requests
|
static const publish_spec_t rtr_publish_spec; // for remote transmission requests
|
||||||
|
|
||||||
enum geyser_modes : int {
|
enum geyser_modes : int {
|
||||||
GM_SUNDAY,
|
GM_SUNDAY,
|
||||||
GM_WORKDAY,
|
GM_WORKDAY,
|
||||||
GM_SATURDAY,
|
GM_SATURDAY,
|
||||||
GM_PUBLIC_HOLIDAY,
|
GM_PUBLIC_HOLIDAY,
|
||||||
GM_SCHOOL_HOLIDAY,
|
GM_SCHOOL_HOLIDAY,
|
||||||
};
|
};
|
||||||
enum canbus_ids : int {
|
enum canbus_ids : int {
|
||||||
CB_CANBUS_ID01 = 0x501,
|
CB_CANBUS_ID01 = 0x501,
|
||||||
CB_CANBUS_ID02,
|
CB_CANBUS_ID02,
|
||||||
CB_CANBUS_ID03,
|
CB_CANBUS_ID03,
|
||||||
CB_CANBUS_ID04,
|
CB_CANBUS_ID04,
|
||||||
CB_CANBUS_ID05,
|
CB_CANBUS_ID05,
|
||||||
CB_CANBUS_ID06,
|
CB_CANBUS_ID06,
|
||||||
CB_CANBUS_ID07,
|
CB_CANBUS_ID07,
|
||||||
CB_CANBUS_ID08,
|
CB_CANBUS_ID08,
|
||||||
CB_CANBUS_ID09,
|
CB_CANBUS_ID09,
|
||||||
CB_CANBUS_ID10,
|
CB_CANBUS_ID10,
|
||||||
CB_POWER_MAINS = 0x401,
|
CB_POWER_MAINS = 0x401,
|
||||||
CB_POWER_INVERTER,
|
CB_POWER_INVERTER,
|
||||||
CB_POWER_PLUGS,
|
CB_POWER_PLUGS,
|
||||||
CB_POWER_LIGHTS,
|
CB_POWER_LIGHTS,
|
||||||
CB_POWER_GEYSER,
|
CB_POWER_GEYSER,
|
||||||
CB_POWER_POOL,
|
CB_POWER_POOL,
|
||||||
CB_POWER_GENERATED,
|
CB_POWER_GENERATED,
|
||||||
CB_ENERGY_MAINS,
|
CB_ENERGY_MAINS,
|
||||||
CB_ENERGY_GEYSER,
|
CB_ENERGY_GEYSER,
|
||||||
CB_ENERGY_POOL,
|
CB_ENERGY_POOL,
|
||||||
CB_ENERGY_PLUGS,
|
CB_ENERGY_PLUGS,
|
||||||
CB_ENERGY_LIGHTS,
|
CB_ENERGY_LIGHTS,
|
||||||
CB_ENERGY_HOUSE,
|
CB_ENERGY_HOUSE,
|
||||||
CB_ENERGY_GENERATED,
|
CB_ENERGY_GENERATED,
|
||||||
CB_ENERGY_LOSS,
|
CB_ENERGY_LOSS,
|
||||||
CB_CONTROLLER_STATES,
|
CB_CONTROLLER_STATES,
|
||||||
CB_GEYSER_TEMPERATURE_TOP,
|
CB_GEYSER_TEMPERATURE_TOP,
|
||||||
CB_GEYSER_TEMPERATURE_BOTTOM,
|
CB_GEYSER_TEMPERATURE_BOTTOM,
|
||||||
CB_GEYSER_TEMPERATURE_AMBIENT,
|
CB_GEYSER_TEMPERATURE_AMBIENT,
|
||||||
CB_GEYSER_HEATING,
|
CB_GEYSER_HEATING,
|
||||||
CB_GEYSER_ACTIVE_SCHEDULE,
|
CB_GEYSER_ACTIVE_SCHEDULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
//Property<int, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_set_battery_charge_voltage_limit, &cbf_sthome::_get_battery_charge_voltage_limit };
|
//Property<int, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_set_battery_charge_voltage_limit, &cbf_sthome::_get_battery_charge_voltage_limit };
|
||||||
//Property<float, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_get_battery_charge_voltage_limit};
|
//Property<float, cbf_sthome> battery_charge_voltage_limit {this, &cbf_sthome::_get_battery_charge_voltage_limit};
|
||||||
static const std::string heartbeat;
|
static const std::string heartbeat;
|
||||||
|
|
||||||
cbf_sthome() = default;
|
cbf_sthome() = default;
|
||||||
cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
|
cbf_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr);
|
||||||
void clear();
|
void clear();
|
||||||
virtual std::string to_string() const override;
|
virtual std::string to_string() const override;
|
||||||
cbf_sthome& operator=(cbf_sthome&& src); // required due to double inheritance in cbf_store_pylon
|
cbf_sthome& operator=(cbf_sthome&& src); // required due to double inheritance in cbf_store_pylon
|
||||||
// using default (compiler auto generated) copy and move constructors and assignment operators
|
// using default (compiler auto generated) copy and move constructors and assignment operators
|
||||||
virtual ~cbf_sthome() = default;
|
virtual ~cbf_sthome() = default;
|
||||||
bool verify_heartbeat() const;
|
bool verify_heartbeat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
#endif // __SOLAR_CBF_STHOME_H
|
#endif // __SOLAR_CBF_STHOME_H
|
||||||
@ -1,235 +1,235 @@
|
|||||||
#include "source/solar/cbf_store.h"
|
#include "source/solar/cbf_store.h"
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id)
|
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id)
|
||||||
: cb_frame(msg_id, can_id)
|
: cb_frame(msg_id, can_id)
|
||||||
{
|
{
|
||||||
this->id = id; // used for debugging, can be omitted
|
this->id = id; // used for debugging, can be omitted
|
||||||
this->count = 0;
|
this->count = 0;
|
||||||
this->first_timestamp = 0;
|
this->first_timestamp = 0;
|
||||||
this->last_timestamp = 0;
|
this->last_timestamp = 0;
|
||||||
// ESP_LOGI(tag("store CTOR1").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
// ESP_LOGI(tag("store CTOR1").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id, time_t first_timestamp, time_t last_timestamp)
|
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id, time_t first_timestamp, time_t last_timestamp)
|
||||||
: cb_frame(msg_id, can_id)
|
: cb_frame(msg_id, can_id)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id;
|
||||||
this->count = 0;
|
this->count = 0;
|
||||||
this->first_timestamp = first_timestamp;
|
this->first_timestamp = first_timestamp;
|
||||||
this->last_timestamp = last_timestamp;
|
this->last_timestamp = last_timestamp;
|
||||||
// ESP_LOGI(tag("store CTOR2").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
// ESP_LOGI(tag("store CTOR2").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id, const byte_vector& frame, bool rtr, time_t first_timestamp, time_t last_timestamp)
|
cbf_store::cbf_store(int msg_id, uint32_t can_id, int id, const byte_vector& frame, bool rtr, time_t first_timestamp, time_t last_timestamp)
|
||||||
: cb_frame(msg_id, can_id, frame, rtr)
|
: cb_frame(msg_id, can_id, frame, rtr)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id;
|
||||||
this->count = 0;
|
this->count = 0;
|
||||||
this->first_timestamp = first_timestamp;
|
this->first_timestamp = first_timestamp;
|
||||||
this->last_timestamp = last_timestamp;
|
this->last_timestamp = last_timestamp;
|
||||||
// ESP_LOGI(tag("store CTOR3").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
// ESP_LOGI(tag("store CTOR3").c_str(), "%-20s %s", "Created store", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store::cbf_store(const cbf_store& b, int id)
|
cbf_store::cbf_store(const cbf_store& b, int id)
|
||||||
: cb_frame(b)
|
: cb_frame(b)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id;
|
||||||
this->count = b.count;
|
this->count = b.count;
|
||||||
this->first_timestamp = b.first_timestamp;
|
this->first_timestamp = b.first_timestamp;
|
||||||
this->last_timestamp = b.last_timestamp;
|
this->last_timestamp = b.last_timestamp;
|
||||||
// ESP_LOGI(tag("store CCTOR2").c_str(), "%-20s %s", "Copied store", this->to_string().c_str());
|
// ESP_LOGI(tag("store CCTOR2").c_str(), "%-20s %s", "Copied store", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store::cbf_store(const cbf_store&& b, int id)
|
cbf_store::cbf_store(const cbf_store&& b, int id)
|
||||||
: cb_frame(b)
|
: cb_frame(b)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id;
|
||||||
this->count = b.count;
|
this->count = b.count;
|
||||||
this->first_timestamp = b.first_timestamp;
|
this->first_timestamp = b.first_timestamp;
|
||||||
this->last_timestamp = b.last_timestamp;
|
this->last_timestamp = b.last_timestamp;
|
||||||
// ESP_LOGI(tag("store MCCTOR2").c_str(), "%-20s %s", "Copied store", this->to_string().c_str());
|
// ESP_LOGI(tag("store MCCTOR2").c_str(), "%-20s %s", "Copied store", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
std::shared_ptr<cbf_store> cbf_store::clone() const
|
std::shared_ptr<cbf_store> cbf_store::clone() const
|
||||||
{
|
{
|
||||||
return clone_impl();
|
return clone_impl();
|
||||||
}
|
}
|
||||||
std::shared_ptr<cbf_store> cbf_store::clone_impl() const
|
std::shared_ptr<cbf_store> cbf_store::clone_impl() const
|
||||||
{
|
{
|
||||||
ESP_LOGW(tag("store CLONE").c_str(), "%-20s", "Cloning store"); // this should happen as all cloning should be done by derived classes
|
ESP_LOGW(tag("store CLONE").c_str(), "%-20s", "Cloning store"); // this should happen as all cloning should be done by derived classes
|
||||||
return std::make_shared<cbf_store>(*this);
|
return std::make_shared<cbf_store>(*this);
|
||||||
}
|
}
|
||||||
bool cbf_store::is_publish_expired(time_t currenttime, int update_interval) const
|
bool cbf_store::is_publish_expired(time_t currenttime, int update_interval) const
|
||||||
{
|
{
|
||||||
return (this->last_timestamp == 0) || ((currenttime - this->last_timestamp) >= update_interval);
|
return (this->last_timestamp == 0) || ((currenttime - this->last_timestamp) >= update_interval);
|
||||||
}
|
}
|
||||||
bool cbf_store::is_validity_expired(time_t currenttime, int timeout_interval) const
|
bool cbf_store::is_validity_expired(time_t currenttime, int timeout_interval) const
|
||||||
{
|
{
|
||||||
return (this->last_timestamp == 0) || ((currenttime - this->last_timestamp) >= timeout_interval);
|
return (this->last_timestamp == 0) || ((currenttime - this->last_timestamp) >= timeout_interval);
|
||||||
}
|
}
|
||||||
cbf_store::cbf_updateresult cbf_store::update(const cbf_store& newitem, const int *comparecolumns)
|
cbf_store::cbf_updateresult cbf_store::update(const cbf_store& newitem, const int *comparecolumns)
|
||||||
{
|
{
|
||||||
ESP_LOGW(this->tag("store UPDATE").c_str(), "%-20s %s", "Default update call", this->to_string().c_str()); // this should happen as all updating should be called from derived classes, i.e. providing the publish_specs as parameters
|
ESP_LOGW(this->tag("store UPDATE").c_str(), "%-20s %s", "Default update call", this->to_string().c_str()); // this should happen as all updating should be called from derived classes, i.e. providing the publish_specs as parameters
|
||||||
return update(newitem, publish_spec_t{1, 5, 10}, publish_spec_t{1, 5, 10}, comparecolumns); // we are forced to instantiate this class, so we provide a default implementation
|
return update(newitem, publish_spec_t{1, 5, 10}, publish_spec_t{1, 5, 10}, comparecolumns); // we are forced to instantiate this class, so we provide a default implementation
|
||||||
}
|
}
|
||||||
cbf_store::cbf_updateresult cbf_store::update(const cbf_store& newitem, publish_spec_t publish_spec, publish_spec_t rtr_publish_spec, const int *comparecolumns)
|
cbf_store::cbf_updateresult cbf_store::update(const cbf_store& newitem, publish_spec_t publish_spec, publish_spec_t rtr_publish_spec, const int *comparecolumns)
|
||||||
{
|
{
|
||||||
if(!is_valid()) {
|
if(!is_valid()) {
|
||||||
return cbf_updateresult::stu_NONE; // cannot update an invalid store
|
return cbf_updateresult::stu_NONE; // cannot update an invalid store
|
||||||
}
|
}
|
||||||
auto pbspec = rtr ? rtr_publish_spec : publish_spec;
|
auto pbspec = rtr ? rtr_publish_spec : publish_spec;
|
||||||
time_t newtime = newitem.last_timestamp;
|
time_t newtime = newitem.last_timestamp;
|
||||||
bool publish_expired = this->is_publish_expired(newtime, pbspec.interval);
|
bool publish_expired = this->is_publish_expired(newtime, pbspec.interval);
|
||||||
bool validity_expired = this->is_validity_expired(newtime, pbspec.timeout);
|
bool validity_expired = this->is_validity_expired(newtime, pbspec.timeout);
|
||||||
bool isduplicate = this->compare(newitem, comparecolumns) == 0;
|
bool isduplicate = this->compare(newitem, comparecolumns) == 0;
|
||||||
time_t reset_timer = this->first_timestamp + pbspec.interval - newtime;
|
time_t reset_timer = this->first_timestamp + pbspec.interval - newtime;
|
||||||
int timediff = is_valid() ? (int)(newtime - this->last_timestamp) : -1;
|
int timediff = is_valid() ? (int)(newtime - this->last_timestamp) : -1;
|
||||||
auto result = isduplicate ? cbf_updateresult::stu_DUPLICATE : cbf_updateresult::stu_NONE;
|
auto result = isduplicate ? cbf_updateresult::stu_DUPLICATE : cbf_updateresult::stu_NONE;
|
||||||
// auto ftstime = ESPTime::from_epoch_local(this->first_timestamp).strftime("%H:%M:%S");
|
// auto ftstime = ESPTime::from_epoch_local(this->first_timestamp).strftime("%H:%M:%S");
|
||||||
// auto ltstime = ESPTime::from_epoch_local(this->last_timestamp).strftime("%H:%M:%S");
|
// auto ltstime = ESPTime::from_epoch_local(this->last_timestamp).strftime("%H:%M:%S");
|
||||||
// auto ntstime = ESPTime::from_epoch_local(newtime).strftime("%H:%M:%S");
|
// auto ntstime = ESPTime::from_epoch_local(newtime).strftime("%H:%M:%S");
|
||||||
// if(rtr)
|
// if(rtr)
|
||||||
// ESP_LOGI(this->tag("store UPDATE").c_str(), "A: %s PBS %d,%d,%d PE:%s VE:%s DUP:%s FTS %s LTS %s NTS %s RES[D%s P%s]", rtr ? "RTR" : "NML", pbspec.on_count, pbspec// .interval, pbspec.timeout, publish_expired ? "Y" : "N", validity_expired ? "Y" : "N", isduplicate ? "Y" : "N", ftstime.c_str(), ltstime.c_str(), ntstime.c_str(),// result & cbf_updateresult::stu_DUPLICATE ? "Y" : "N", result & cbf_updateresult::stu_PUBLISH ? "Y" : "N");
|
// ESP_LOGI(this->tag("store UPDATE").c_str(), "A: %s PBS %d,%d,%d PE:%s VE:%s DUP:%s FTS %s LTS %s NTS %s RES[D%s P%s]", rtr ? "RTR" : "NML", pbspec.on_count, pbspec// .interval, pbspec.timeout, publish_expired ? "Y" : "N", validity_expired ? "Y" : "N", isduplicate ? "Y" : "N", ftstime.c_str(), ltstime.c_str(), ntstime.c_str(),// result & cbf_updateresult::stu_DUPLICATE ? "Y" : "N", result & cbf_updateresult::stu_PUBLISH ? "Y" : "N");
|
||||||
if(validity_expired) {
|
if(validity_expired) {
|
||||||
*this = newitem;
|
*this = newitem;
|
||||||
if(!isduplicate) {
|
if(!isduplicate) {
|
||||||
this->count = 1;
|
this->count = 1;
|
||||||
publish_expired = false;
|
publish_expired = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(isduplicate || publish_expired) {
|
if(isduplicate || publish_expired) {
|
||||||
this->count++;
|
this->count++;
|
||||||
if(this->count == pbspec.on_count)
|
if(this->count == pbspec.on_count)
|
||||||
this->setpublish(); // must be reset by caller after publish
|
this->setpublish(); // must be reset by caller after publish
|
||||||
if(this->getpublish())
|
if(this->getpublish())
|
||||||
result = static_cast<cbf_updateresult>(result | cbf_updateresult::stu_PUBLISH);
|
result = static_cast<cbf_updateresult>(result | cbf_updateresult::stu_PUBLISH);
|
||||||
this->last_timestamp = newtime;
|
this->last_timestamp = newtime;
|
||||||
if(reset_timer <= 0) {
|
if(reset_timer <= 0) {
|
||||||
this->first_timestamp = newtime;
|
this->first_timestamp = newtime;
|
||||||
this->count = 1;
|
this->count = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ftstime = ESPTime::from_epoch_local(this->first_timestamp).strftime("%H:%M:%S");
|
// ftstime = ESPTime::from_epoch_local(this->first_timestamp).strftime("%H:%M:%S");
|
||||||
// ltstime = ESPTime::from_epoch_local(this->last_timestamp).strftime("%H:%M:%S");
|
// ltstime = ESPTime::from_epoch_local(this->last_timestamp).strftime("%H:%M:%S");
|
||||||
// if(rtr)
|
// if(rtr)
|
||||||
// ESP_LOGI(this->tag("store UPDATE").c_str(), "B: %s PBS %d,%d,%d PE:%s VE:%s DUP:%s FTS %s LTS %s NTS %s RES[D%s P%s]", rtr ? "RTR" : "NML", pbspec.on_count, pbspec.interval, pbspec.timeout, publish_expired ? "Y" : "N", validity_expired ? "Y" : "N", isduplicate ? "Y" : "N", ftstime.c_str(), ltstime.c_str(), ntstime.c_str(), result & cbf_updateresult::stu_DUPLICATE ? "Y" : "N", result & cbf_updateresult::stu_PUBLISH ? "Y" : "N");
|
// ESP_LOGI(this->tag("store UPDATE").c_str(), "B: %s PBS %d,%d,%d PE:%s VE:%s DUP:%s FTS %s LTS %s NTS %s RES[D%s P%s]", rtr ? "RTR" : "NML", pbspec.on_count, pbspec.interval, pbspec.timeout, publish_expired ? "Y" : "N", validity_expired ? "Y" : "N", isduplicate ? "Y" : "N", ftstime.c_str(), ltstime.c_str(), ntstime.c_str(), result & cbf_updateresult::stu_DUPLICATE ? "Y" : "N", result & cbf_updateresult::stu_PUBLISH ? "Y" : "N");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int cbf_store::compare(const cbf_store& b, const int *comparecolumns) const
|
int cbf_store::compare(const cbf_store& b, const int *comparecolumns) const
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
bool stopcompare = false;
|
bool stopcompare = false;
|
||||||
bool isdefaultcompare = comparecolumns == nullptr || *comparecolumns == 0;
|
bool isdefaultcompare = comparecolumns == nullptr || *comparecolumns == 0;
|
||||||
int ncmpcols = 0;
|
int ncmpcols = 0;
|
||||||
while (comparecolumns[ncmpcols] != 0) ncmpcols++;
|
while (comparecolumns[ncmpcols] != 0) ncmpcols++;
|
||||||
if (isdefaultcompare) {
|
if (isdefaultcompare) {
|
||||||
for (int i = sisortNULL + 1; i < sisortEND && !stopcompare && result == 0; i++) {
|
for (int i = sisortNULL + 1; i < sisortEND && !stopcompare && result == 0; i++) {
|
||||||
bool validnextcol = i < sisortEND - 1;
|
bool validnextcol = i < sisortEND - 1;
|
||||||
result = compare(b, i, &stopcompare, validnextcol);
|
result = compare(b, i, &stopcompare, validnextcol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < sisortEND && !stopcompare && result == 0 && comparecolumns[i] != 0; i++) {
|
for (int i = 0; i < sisortEND && !stopcompare && result == 0 && comparecolumns[i] != 0; i++) {
|
||||||
int cmpcol = comparecolumns[i];
|
int cmpcol = comparecolumns[i];
|
||||||
bool validnextcol = cmpcol != 0 && comparecolumns[i + 1] != 0;
|
bool validnextcol = cmpcol != 0 && comparecolumns[i + 1] != 0;
|
||||||
result = compare(b, cmpcol, &stopcompare, validnextcol);
|
result = compare(b, cmpcol, &stopcompare, validnextcol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int cbf_store::compare(const cbf_store &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const
|
int cbf_store::compare(const cbf_store &b, int cmpflag, bool *stopcomparesignal, bool validnextcol) const
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int reverseorderflag = cmpflag & sisortreverse;
|
int reverseorderflag = cmpflag & sisortreverse;
|
||||||
int casesensitiveflag = cmpflag & sisortcase;
|
int casesensitiveflag = cmpflag & sisortcase;
|
||||||
int nulliswildcardflag = cmpflag & sisortwild;
|
int nulliswildcardflag = cmpflag & sisortwild;
|
||||||
int stopcompareflag = cmpflag & sistopcomparecol;
|
int stopcompareflag = cmpflag & sistopcomparecol;
|
||||||
cmpflag = cmpflag & ~FLAGBITS;
|
cmpflag = cmpflag & ~FLAGBITS;
|
||||||
if (cmpflag == 0)
|
if (cmpflag == 0)
|
||||||
return result;
|
return result;
|
||||||
bool casesensitive = casesensitiveflag != 0;
|
bool casesensitive = casesensitiveflag != 0;
|
||||||
bool sortwild = nulliswildcardflag != 0;
|
bool sortwild = nulliswildcardflag != 0;
|
||||||
bool stopcompare = stopcompareflag != 0;
|
bool stopcompare = stopcompareflag != 0;
|
||||||
*stopcomparesignal = false;
|
*stopcomparesignal = false;
|
||||||
switch (cmpflag) {
|
switch (cmpflag) {
|
||||||
case sisortNULL:
|
case sisortNULL:
|
||||||
return 0;
|
return 0;
|
||||||
case sisortcanid:
|
case sisortcanid:
|
||||||
{
|
{
|
||||||
bool bothvalid = this->can_id != 0 && b.can_id != 0;
|
bool bothvalid = this->can_id != 0 && b.can_id != 0;
|
||||||
if (bothvalid) {
|
if (bothvalid) {
|
||||||
result = num_compare(this->can_id, b.can_id);
|
result = num_compare(this->can_id, b.can_id);
|
||||||
*stopcomparesignal = stopcompare; // set flag only if both items are valid
|
*stopcomparesignal = stopcompare; // set flag only if both items are valid
|
||||||
} else {
|
} else {
|
||||||
if (sortwild)
|
if (sortwild)
|
||||||
result = 0;
|
result = 0;
|
||||||
else {
|
else {
|
||||||
bool bothinvalid = !(this->can_id || b.can_id);
|
bool bothinvalid = !(this->can_id || b.can_id);
|
||||||
if (validnextcol && bothinvalid)
|
if (validnextcol && bothinvalid)
|
||||||
result = 0; //if validnextcol then return 0 if both IDs are invalid
|
result = 0; //if validnextcol then return 0 if both IDs are invalid
|
||||||
result = this->can_id ? 1 : -1;
|
result = this->can_id ? 1 : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case sisortmsgid:
|
case sisortmsgid:
|
||||||
result = num_compare(this->msg_id, b.msg_id);
|
result = num_compare(this->msg_id, b.msg_id);
|
||||||
break;
|
break;
|
||||||
case sisortcount:
|
case sisortcount:
|
||||||
{
|
{
|
||||||
result = num_compare(this->count, b.count);
|
result = num_compare(this->count, b.count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case sisortfirst_timestamp:
|
case sisortfirst_timestamp:
|
||||||
{
|
{
|
||||||
auto a_ts = this->first_timestamp;
|
auto a_ts = this->first_timestamp;
|
||||||
auto b_ts = b.first_timestamp;
|
auto b_ts = b.first_timestamp;
|
||||||
result = num_compare(a_ts, b_ts);
|
result = num_compare(a_ts, b_ts);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case sisortlast_timestamp:
|
case sisortlast_timestamp:
|
||||||
{
|
{
|
||||||
auto a_ts = this->last_timestamp;
|
auto a_ts = this->last_timestamp;
|
||||||
auto b_ts = b.last_timestamp;
|
auto b_ts = b.last_timestamp;
|
||||||
result = num_compare(a_ts, b_ts);
|
result = num_compare(a_ts, b_ts);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case sisortrtr:
|
case sisortrtr:
|
||||||
result = bool_compare(this->rtr, b.rtr);
|
result = bool_compare(this->rtr, b.rtr);
|
||||||
break;
|
break;
|
||||||
case sisortframe:
|
case sisortframe:
|
||||||
{
|
{
|
||||||
result = this->compare_frame(b.frame);
|
result = this->compare_frame(b.frame);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
result = 0;
|
result = 0;
|
||||||
// ESP_LOGE("cbf_store::compare", "Unknown compare column %d", cmpflag);
|
// ESP_LOGE("cbf_store::compare", "Unknown compare column %d", cmpflag);
|
||||||
*stopcomparesignal = true; // stop compare as we don't know how to handle
|
*stopcomparesignal = true; // stop compare as we don't know how to handle
|
||||||
// should never reach here as all cases must be dealt with
|
// should never reach here as all cases must be dealt with
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (reverseorderflag != 0)
|
if (reverseorderflag != 0)
|
||||||
result = -result;
|
result = -result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
std::string cbf_store::tag(const std::string& prefix, int prefixlen) const
|
std::string cbf_store::tag(const std::string& prefix, int prefixlen) const
|
||||||
{
|
{
|
||||||
// Be extra careful with the placement of printf format specifiers and their corresponding arguments.
|
// Be extra careful with the placement of printf format specifiers and their corresponding arguments.
|
||||||
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
||||||
char tag[48];
|
char tag[48];
|
||||||
if(prefix.length() == 0)
|
if(prefix.length() == 0)
|
||||||
snprintf(tag, sizeof(tag), "#%04d s%d 0x%03X %s", msg_id, id, can_id, getpublish() ? "P" : " ");
|
snprintf(tag, sizeof(tag), "#%04d s%d 0x%03X %s", msg_id, id, can_id, getpublish() ? "P" : " ");
|
||||||
else
|
else
|
||||||
snprintf(tag, sizeof(tag), "%-*s #%04d s%d 0x%03X %s", prefixlen, prefix.c_str(), msg_id, id, can_id, getpublish() ? "P" : " ");
|
snprintf(tag, sizeof(tag), "%-*s #%04d s%d 0x%03X %s", prefixlen, prefix.c_str(), msg_id, id, can_id, getpublish() ? "P" : " ");
|
||||||
return std::string(tag);
|
return std::string(tag);
|
||||||
}
|
}
|
||||||
std::string cbf_store::to_string() const
|
std::string cbf_store::to_string() const
|
||||||
{
|
{
|
||||||
// Be extra careful with the placement of printf format specifiers and their corresponding arguments.
|
// Be extra careful with the placement of printf format specifiers and their corresponding arguments.
|
||||||
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
// Incorrect placement / misaligned arguments can lead to undefined behavior and processor crashes.
|
||||||
char buffer[80];
|
char buffer[80];
|
||||||
auto ftstime = is_valid() ? ESPTime::from_epoch_local(first_timestamp).strftime("%H:%M:%S") : "N/A";
|
auto ftstime = is_valid() ? ESPTime::from_epoch_local(first_timestamp).strftime("%H:%M:%S") : "N/A";
|
||||||
auto ltstime = is_valid() ? ESPTime::from_epoch_local(last_timestamp).strftime("%H:%M:%S") : "N/A";
|
auto ltstime = is_valid() ? ESPTime::from_epoch_local(last_timestamp).strftime("%H:%M:%S") : "N/A";
|
||||||
snprintf(buffer, sizeof(buffer), " Fts: %s Lts: %s Count: %2d %s", ftstime.c_str(), ltstime.c_str(), count, cb_frame::to_string().c_str());
|
snprintf(buffer, sizeof(buffer), " Fts: %s Lts: %s Count: %2d %s", ftstime.c_str(), ltstime.c_str(), count, cb_frame::to_string().c_str());
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
@ -1,71 +1,71 @@
|
|||||||
#include "cbf_store_pylon.h"
|
#include "cbf_store_pylon.h"
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
cbf_store_pylon::cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp)
|
cbf_store_pylon::cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp)
|
||||||
: cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_pylon(msg_id, can_id, frame, rtr)
|
: cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_pylon(msg_id, can_id, frame, rtr)
|
||||||
{
|
{
|
||||||
this->id = 0;
|
this->id = 0;
|
||||||
this->count = 0;
|
this->count = 0;
|
||||||
// ESP_LOGI(tag("store_pylon CTOR1").c_str(), "%-20s %s", "Created store_pylon", this->to_string().c_str());
|
// ESP_LOGI(tag("store_pylon CTOR1").c_str(), "%-20s %s", "Created store_pylon", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store_pylon::cbf_store_pylon(const cbf_store_pylon& b)
|
cbf_store_pylon::cbf_store_pylon(const cbf_store_pylon& b)
|
||||||
: cb_frame(b), cbf_store(b) // call base class copy constructor
|
: cb_frame(b), cbf_store(b) // call base class copy constructor
|
||||||
{
|
{
|
||||||
// ESP_LOGI(tag("store_pylon CCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str());
|
// ESP_LOGI(tag("store_pylon CCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store_pylon& cbf_store_pylon::operator=(const cbf_store_pylon& b)
|
cbf_store_pylon& cbf_store_pylon::operator=(const cbf_store_pylon& b)
|
||||||
{
|
{
|
||||||
if (this != &b) {
|
if (this != &b) {
|
||||||
cbf_store_pylon tmp(b);
|
cbf_store_pylon tmp(b);
|
||||||
swap(tmp);
|
swap(tmp);
|
||||||
// ESP_LOGI(tag("store_pylon AOPP").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
|
// ESP_LOGI(tag("store_pylon AOPP").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
cbf_store_pylon::cbf_store_pylon(cbf_store_pylon&& src)
|
cbf_store_pylon::cbf_store_pylon(cbf_store_pylon&& src)
|
||||||
: cb_frame(src), cbf_store(src)
|
: cb_frame(src), cbf_store(src)
|
||||||
{
|
{
|
||||||
src.clear();
|
src.clear();
|
||||||
// ESP_LOGI(tag("store_pylon MCCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str());
|
// ESP_LOGI(tag("store_pylon MCCTOR").c_str(), "%-20s %s", "Copied store_pylon", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store_pylon& cbf_store_pylon::operator=(cbf_store_pylon&& src)
|
cbf_store_pylon& cbf_store_pylon::operator=(cbf_store_pylon&& src)
|
||||||
{
|
{
|
||||||
if (this != &src) {
|
if (this != &src) {
|
||||||
cbf_store::operator=(std::move(src));
|
cbf_store::operator=(std::move(src));
|
||||||
cb_frame::operator=(std::move(src));
|
cb_frame::operator=(std::move(src));
|
||||||
src.clear();
|
src.clear();
|
||||||
// ESP_LOGI(tag("store_pylon MOASS").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
|
// ESP_LOGI(tag("store_pylon MOASS").c_str(), "%-20s %s", "Assigned store_pylon", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
std::shared_ptr<cbf_store_pylon> cbf_store_pylon::clone() const
|
std::shared_ptr<cbf_store_pylon> cbf_store_pylon::clone() const
|
||||||
{
|
{
|
||||||
return std::static_pointer_cast<cbf_store_pylon>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_pylon
|
return std::static_pointer_cast<cbf_store_pylon>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_pylon
|
||||||
}
|
}
|
||||||
std::shared_ptr<cbf_store> cbf_store_pylon::clone_impl() const
|
std::shared_ptr<cbf_store> cbf_store_pylon::clone_impl() const
|
||||||
{
|
{
|
||||||
//ESP_LOGI(tag("store_pylon CLONE").c_str(), "%-20s", "Cloning store_pylon");
|
//ESP_LOGI(tag("store_pylon CLONE").c_str(), "%-20s", "Cloning store_pylon");
|
||||||
return std::make_shared<cbf_store_pylon>(*this);
|
return std::make_shared<cbf_store_pylon>(*this);
|
||||||
}
|
}
|
||||||
void cbf_store_pylon::clear()
|
void cbf_store_pylon::clear()
|
||||||
{
|
{
|
||||||
cb_frame::clear();
|
cb_frame::clear();
|
||||||
cbf_store::clear();
|
cbf_store::clear();
|
||||||
}
|
}
|
||||||
void cbf_store_pylon::swap(cbf_store_pylon &s)
|
void cbf_store_pylon::swap(cbf_store_pylon &s)
|
||||||
{
|
{
|
||||||
cb_frame::swap(s);
|
cb_frame::swap(s);
|
||||||
cbf_store::swap(s);
|
cbf_store::swap(s);
|
||||||
}
|
}
|
||||||
cbf_store::cbf_updateresult cbf_store_pylon::update(const cbf_store& newitem, const int *comparecolumns)
|
cbf_store::cbf_updateresult cbf_store_pylon::update(const cbf_store& newitem, const int *comparecolumns)
|
||||||
{
|
{
|
||||||
//ESP_LOGI(tag("store_pylon UPDATE").c_str(), "%-20s %s", "Updating store_pylon", this->to_string().c_str());
|
//ESP_LOGI(tag("store_pylon UPDATE").c_str(), "%-20s %s", "Updating store_pylon", this->to_string().c_str());
|
||||||
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
|
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
|
||||||
}
|
}
|
||||||
std::string cbf_store_pylon::to_string() const
|
std::string cbf_store_pylon::to_string() const
|
||||||
{
|
{
|
||||||
return cb_frame::to_string() + " " + cbf_pylon::to_string();
|
return cb_frame::to_string() + " " + cbf_pylon::to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
@ -1,34 +1,34 @@
|
|||||||
#ifndef __SOLAR_CBF_STORE_PYLON // include GUARD
|
#ifndef __SOLAR_CBF_STORE_PYLON // include GUARD
|
||||||
#define __SOLAR_CBF_STORE_PYLON
|
#define __SOLAR_CBF_STORE_PYLON
|
||||||
#include "esphome.h"
|
#include "esphome.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cbf_store.h"
|
#include "cbf_store.h"
|
||||||
#include "cbf_pylon.h"
|
#include "cbf_pylon.h"
|
||||||
using namespace esphome;
|
using namespace esphome;
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
class cbf_store_pylon : public cbf_store, public cbf_pylon
|
class cbf_store_pylon : public cbf_store, public cbf_pylon
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
cbf_store_pylon() = delete; // default constructor not allowed
|
cbf_store_pylon() = delete; // default constructor not allowed
|
||||||
cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp);
|
cbf_store_pylon(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp);
|
||||||
virtual ~cbf_store_pylon() = default; // virtual destructor for base class
|
virtual ~cbf_store_pylon() = default; // virtual destructor for base class
|
||||||
std::shared_ptr<cbf_store_pylon> clone() const;
|
std::shared_ptr<cbf_store_pylon> clone() const;
|
||||||
void clear();
|
void clear();
|
||||||
void swap(cbf_store_pylon &s);
|
void swap(cbf_store_pylon &s);
|
||||||
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
|
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
|
||||||
virtual std::string to_string() const override;
|
virtual std::string to_string() const override;
|
||||||
|
|
||||||
cbf_store_pylon(const cbf_store_pylon& b);
|
cbf_store_pylon(const cbf_store_pylon& b);
|
||||||
cbf_store_pylon& operator=(const cbf_store_pylon& b);
|
cbf_store_pylon& operator=(const cbf_store_pylon& b);
|
||||||
cbf_store_pylon(cbf_store_pylon&& src);
|
cbf_store_pylon(cbf_store_pylon&& src);
|
||||||
cbf_store_pylon& operator=(cbf_store_pylon&& src);
|
cbf_store_pylon& operator=(cbf_store_pylon&& src);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual std::shared_ptr<cbf_store> clone_impl() const override;
|
virtual std::shared_ptr<cbf_store> clone_impl() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
#endif // __SOLAR_CBF_STORE_PYLON
|
#endif // __SOLAR_CBF_STORE_PYLON
|
||||||
@ -1,71 +1,71 @@
|
|||||||
#include "cbf_store_sthome.h"
|
#include "cbf_store_sthome.h"
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
cbf_store_sthome::cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp)
|
cbf_store_sthome::cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& frame, bool rtr, time_t timestamp)
|
||||||
: cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_sthome(msg_id, can_id, frame, rtr)
|
: cb_frame(msg_id, can_id, frame, rtr), cbf_store(msg_id, can_id, 0, timestamp, timestamp) //, cbf_sthome(msg_id, can_id, frame, rtr)
|
||||||
{
|
{
|
||||||
this->id = 0;
|
this->id = 0;
|
||||||
this->count = 0;
|
this->count = 0;
|
||||||
// ESP_LOGI(tag("store_sthome CTOR1").c_str(), "%-20s %s", "Created store_sthome", this->to_string().c_str());
|
// ESP_LOGI(tag("store_sthome CTOR1").c_str(), "%-20s %s", "Created store_sthome", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store_sthome::cbf_store_sthome(const cbf_store_sthome& b)
|
cbf_store_sthome::cbf_store_sthome(const cbf_store_sthome& b)
|
||||||
: cb_frame(b), cbf_store(b) // call base class copy constructor
|
: cb_frame(b), cbf_store(b) // call base class copy constructor
|
||||||
{
|
{
|
||||||
// ESP_LOGI(tag("store_sthome CCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str());
|
// ESP_LOGI(tag("store_sthome CCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store_sthome& cbf_store_sthome::operator=(const cbf_store_sthome& b)
|
cbf_store_sthome& cbf_store_sthome::operator=(const cbf_store_sthome& b)
|
||||||
{
|
{
|
||||||
if (this != &b) {
|
if (this != &b) {
|
||||||
cbf_store_sthome tmp(b);
|
cbf_store_sthome tmp(b);
|
||||||
swap(tmp);
|
swap(tmp);
|
||||||
// ESP_LOGI(tag("store_sthome AOPP").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
|
// ESP_LOGI(tag("store_sthome AOPP").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
cbf_store_sthome::cbf_store_sthome(cbf_store_sthome&& src)
|
cbf_store_sthome::cbf_store_sthome(cbf_store_sthome&& src)
|
||||||
: cb_frame(src), cbf_store(src)
|
: cb_frame(src), cbf_store(src)
|
||||||
{
|
{
|
||||||
src.clear();
|
src.clear();
|
||||||
// ESP_LOGI(tag("store_sthome MCCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str());
|
// ESP_LOGI(tag("store_sthome MCCTOR").c_str(), "%-20s %s", "Copied store_sthome", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
cbf_store_sthome& cbf_store_sthome::operator=(cbf_store_sthome&& src)
|
cbf_store_sthome& cbf_store_sthome::operator=(cbf_store_sthome&& src)
|
||||||
{
|
{
|
||||||
if (this != &src) {
|
if (this != &src) {
|
||||||
cbf_store::operator=(std::move(src));
|
cbf_store::operator=(std::move(src));
|
||||||
cb_frame::operator=(std::move(src));
|
cb_frame::operator=(std::move(src));
|
||||||
src.clear();
|
src.clear();
|
||||||
// ESP_LOGI(tag("store_sthome MOASS").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
|
// ESP_LOGI(tag("store_sthome MOASS").c_str(), "%-20s %s", "Assigned store_sthome", this->to_string().c_str());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
std::shared_ptr<cbf_store_sthome> cbf_store_sthome::clone() const
|
std::shared_ptr<cbf_store_sthome> cbf_store_sthome::clone() const
|
||||||
{
|
{
|
||||||
return std::static_pointer_cast<cbf_store_sthome>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_sthome
|
return std::static_pointer_cast<cbf_store_sthome>(clone_impl()); // static_pointer_cast is safe here because we know the underlying type is cbf_store_sthome
|
||||||
}
|
}
|
||||||
std::shared_ptr<cbf_store> cbf_store_sthome::clone_impl() const
|
std::shared_ptr<cbf_store> cbf_store_sthome::clone_impl() const
|
||||||
{
|
{
|
||||||
//ESP_LOGI(tag("store_sthome CLONE").c_str(), "%-20s", "Cloning store_sthome");
|
//ESP_LOGI(tag("store_sthome CLONE").c_str(), "%-20s", "Cloning store_sthome");
|
||||||
return std::make_shared<cbf_store_sthome>(*this);
|
return std::make_shared<cbf_store_sthome>(*this);
|
||||||
}
|
}
|
||||||
void cbf_store_sthome::clear()
|
void cbf_store_sthome::clear()
|
||||||
{
|
{
|
||||||
cb_frame::clear();
|
cb_frame::clear();
|
||||||
cbf_store::clear();
|
cbf_store::clear();
|
||||||
}
|
}
|
||||||
void cbf_store_sthome::swap(cbf_store_sthome &s)
|
void cbf_store_sthome::swap(cbf_store_sthome &s)
|
||||||
{
|
{
|
||||||
cb_frame::swap(s);
|
cb_frame::swap(s);
|
||||||
cbf_store::swap(s);
|
cbf_store::swap(s);
|
||||||
}
|
}
|
||||||
cbf_store::cbf_updateresult cbf_store_sthome::update(const cbf_store& newitem, const int *comparecolumns)
|
cbf_store::cbf_updateresult cbf_store_sthome::update(const cbf_store& newitem, const int *comparecolumns)
|
||||||
{
|
{
|
||||||
//ESP_LOGI(tag("store_sthome UPDATE").c_str(), "%-20s %s", "Updating store_sthome", this->to_string().c_str());
|
//ESP_LOGI(tag("store_sthome UPDATE").c_str(), "%-20s %s", "Updating store_sthome", this->to_string().c_str());
|
||||||
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
|
return cbf_store::update(newitem, publish_spec, rtr_publish_spec, comparecolumns);
|
||||||
}
|
}
|
||||||
std::string cbf_store_sthome::to_string() const
|
std::string cbf_store_sthome::to_string() const
|
||||||
{
|
{
|
||||||
return cb_frame::to_string() + " " + cbf_sthome::to_string();
|
return cb_frame::to_string() + " " + cbf_sthome::to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
@ -1,34 +1,34 @@
|
|||||||
#ifndef __SOLAR_CBF_STORE_STHOME_H // include GUARD
|
#ifndef __SOLAR_CBF_STORE_STHOME_H // include GUARD
|
||||||
#define __SOLAR_CBF_STORE_STHOME_H
|
#define __SOLAR_CBF_STORE_STHOME_H
|
||||||
#include "esphome.h"
|
#include "esphome.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "cbf_store.h"
|
#include "cbf_store.h"
|
||||||
#include "cbf_sthome.h"
|
#include "cbf_sthome.h"
|
||||||
using namespace esphome;
|
using namespace esphome;
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
class cbf_store_sthome : public cbf_store, public cbf_sthome
|
class cbf_store_sthome : public cbf_store, public cbf_sthome
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
cbf_store_sthome() = delete; // default constructor not allowed
|
cbf_store_sthome() = delete; // default constructor not allowed
|
||||||
cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp);
|
cbf_store_sthome(int msg_id, uint32_t can_id, const byte_vector& _frame, bool _rtr , time_t timestamp);
|
||||||
virtual ~cbf_store_sthome() = default; // virtual destructor for base class
|
virtual ~cbf_store_sthome() = default; // virtual destructor for base class
|
||||||
std::shared_ptr<cbf_store_sthome> clone() const;
|
std::shared_ptr<cbf_store_sthome> clone() const;
|
||||||
void clear();
|
void clear();
|
||||||
void swap(cbf_store_sthome &s);
|
void swap(cbf_store_sthome &s);
|
||||||
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
|
virtual cbf_store::cbf_updateresult update(const cbf_store& newitem, const int *comparecolumns) override;
|
||||||
virtual std::string to_string() const override;
|
virtual std::string to_string() const override;
|
||||||
|
|
||||||
cbf_store_sthome(const cbf_store_sthome& b);
|
cbf_store_sthome(const cbf_store_sthome& b);
|
||||||
cbf_store_sthome& operator=(const cbf_store_sthome& b);
|
cbf_store_sthome& operator=(const cbf_store_sthome& b);
|
||||||
cbf_store_sthome(cbf_store_sthome&& src);
|
cbf_store_sthome(cbf_store_sthome&& src);
|
||||||
cbf_store_sthome& operator=(cbf_store_sthome&& src);
|
cbf_store_sthome& operator=(cbf_store_sthome&& src);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual std::shared_ptr<cbf_store> clone_impl() const override;
|
virtual std::shared_ptr<cbf_store> clone_impl() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
#endif // __SOLAR_CBF_STORE_STHOME_H
|
#endif // __SOLAR_CBF_STORE_STHOME_H
|
||||||
@ -1,48 +1,48 @@
|
|||||||
#ifndef __SOLAR_COMMON // include GUARD
|
#ifndef __SOLAR_COMMON // include GUARD
|
||||||
#define __SOLAR_COMMON
|
#define __SOLAR_COMMON
|
||||||
|
|
||||||
namespace solar
|
namespace solar
|
||||||
{
|
{
|
||||||
|
|
||||||
#define FLAGBITS ~0x0FFF // bitwise one's complement (sort column info fits into lower three nibbles)
|
#define FLAGBITS ~0x0FFF // bitwise one's complement (sort column info fits into lower three nibbles)
|
||||||
#define SORTCASE 0x0001000 // case sensitive
|
#define SORTCASE 0x0001000 // case sensitive
|
||||||
#define SORTREVERSE 0x0002000 // reverse order
|
#define SORTREVERSE 0x0002000 // reverse order
|
||||||
#define WILDNULL 0x0004000 // zero denotes wildcard, i.e. anything is equal to zero
|
#define WILDNULL 0x0004000 // zero denotes wildcard, i.e. anything is equal to zero
|
||||||
#define STOPCOMPARE 0x0080000 // stop compare after this column if both items to be compared are valid and a compare was possible
|
#define STOPCOMPARE 0x0080000 // stop compare after this column if both items to be compared are valid and a compare was possible
|
||||||
#define SEARCHWILD 0x0100000 // do wildcard comparison (only searchkey should contain wildcards)
|
#define SEARCHWILD 0x0100000 // do wildcard comparison (only searchkey should contain wildcards)
|
||||||
#define num_compare(a, b) ((a) > (b)) ? +1 : ((a) < (b)) ? -1 : 0
|
#define num_compare(a, b) ((a) > (b)) ? +1 : ((a) < (b)) ? -1 : 0
|
||||||
#define bool_compare(a, b) ((!(a) && !(b)) || ((a) && (b))) ? 0 : (a) ? +1 : -1
|
#define bool_compare(a, b) ((!(a) && !(b)) || ((a) && (b))) ? 0 : (a) ? +1 : -1
|
||||||
#define max(a, b) ((a) > (b)) ? (a) : (b)
|
#define max(a, b) ((a) > (b)) ? (a) : (b)
|
||||||
#define min(a, b) ((a) < (b)) ? (a) : (b)
|
#define min(a, b) ((a) < (b)) ? (a) : (b)
|
||||||
|
|
||||||
typedef unsigned int uint;
|
typedef unsigned int uint;
|
||||||
typedef std::vector<uint8_t> byte_vector;
|
typedef std::vector<uint8_t> byte_vector;
|
||||||
|
|
||||||
struct publish_spec_t
|
struct publish_spec_t
|
||||||
{
|
{
|
||||||
int on_count; // how many times a duplicate of this frame was received; used to signal publish
|
int on_count; // how many times a duplicate of this frame was received; used to signal publish
|
||||||
int interval; // used for checking whether updateinterval has expired, i.e. to force a publish
|
int interval; // used for checking whether updateinterval has expired, i.e. to force a publish
|
||||||
int timeout; // used for checking whether timeout has expired, i.e. frame has become stale
|
int timeout; // used for checking whether timeout has expired, i.e. frame has become stale
|
||||||
publish_spec_t(int on_count, int interval, int timeout)
|
publish_spec_t(int on_count, int interval, int timeout)
|
||||||
: on_count(on_count), interval(interval), timeout(timeout) {}
|
: on_count(on_count), interval(interval), timeout(timeout) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::string ltrim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
|
inline std::string ltrim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
|
||||||
{
|
{
|
||||||
size_t start = str.find_first_not_of(chars);
|
size_t start = str.find_first_not_of(chars);
|
||||||
return (start == std::string::npos) ? "" : str.substr(start);
|
return (start == std::string::npos) ? "" : str.substr(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string rtrim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
|
inline std::string rtrim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
|
||||||
{
|
{
|
||||||
size_t end = str.find_last_not_of(chars);
|
size_t end = str.find_last_not_of(chars);
|
||||||
return (end == std::string::npos) ? "" : str.substr(0, end + 1);
|
return (end == std::string::npos) ? "" : str.substr(0, end + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string trim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
|
inline std::string trim(const std::string& str, const std::string& chars = " \t\n\r\f\v")
|
||||||
{
|
{
|
||||||
return rtrim(ltrim(str, chars), chars);
|
return rtrim(ltrim(str, chars), chars);
|
||||||
}
|
}
|
||||||
} // namespace solar
|
} // namespace solar
|
||||||
|
|
||||||
#endif // __SOLAR_COMMON
|
#endif // __SOLAR_COMMON
|
||||||
Loading…
Reference in New Issue
Block a user