diff --git a/esphome/components/sml/sml.cpp b/esphome/components/sml/sml.cpp index bac13be923..9e52e05be9 100644 --- a/esphome/components/sml/sml.cpp +++ b/esphome/components/sml/sml.cpp @@ -51,10 +51,10 @@ void Sml::loop() { if (!valid) break; - // remove start/end sequence - this->sml_data_.erase(this->sml_data_.begin(), this->sml_data_.begin() + START_SEQ.size()); - this->sml_data_.resize(this->sml_data_.size() - 8); - this->process_sml_file_(this->sml_data_); + // discard start/end sequence + auto file_begin = this->sml_data_.begin() + START_SEQ.size(); + auto file_length = this->sml_data_.size() - START_SEQ.size() - 8; + this->process_sml_file_(byte_span(&*file_begin, file_length)); } break; }; @@ -66,32 +66,13 @@ void Sml::add_on_data_callback(std::function, bool)> & this->data_callbacks_.add(std::move(callback)); } -void Sml::process_sml_file_(const bytes &sml_data) { - SmlFile sml_file = SmlFile(sml_data); - std::vector obis_info = sml_file.get_obis_info(); - this->publish_obis_info_(obis_info); - - this->log_obis_info_(obis_info); -} - -void Sml::log_obis_info_(const std::vector &obis_info_vec) { - ESP_LOGD(TAG, "OBIS info:"); - for (auto const &obis_info : obis_info_vec) { - std::string info; - info += " (" + bytes_repr(obis_info.server_id) + ") "; - info += obis_info.code_repr(); - info += " [0x" + bytes_repr(obis_info.value) + "]"; - ESP_LOGD(TAG, "%s", info.c_str()); - } -} - -void Sml::publish_obis_info_(const std::vector &obis_info_vec) { - for (auto const &obis_info : obis_info_vec) { - this->publish_value_(obis_info); - } +void Sml::process_sml_file_(const byte_span &sml_data) { + SmlFile(sml_data).for_each_obis_info([this](const ObisInfo &obis_info) { this->publish_value_(obis_info); }); } void Sml::publish_value_(const ObisInfo &obis_info) { + ESP_LOGD(TAG, "OBIS (%s) %s [0x%s]", bytes_repr(obis_info.server_id).c_str(), obis_info.code_repr().c_str(), + bytes_repr(obis_info.value).c_str()); for (auto const &sml_listener : sml_listeners_) { if ((!sml_listener->server_id.empty()) && (bytes_repr(obis_info.server_id) != sml_listener->server_id)) continue; diff --git a/esphome/components/sml/sml.h b/esphome/components/sml/sml.h index b0c932ca95..5780564689 100644 --- a/esphome/components/sml/sml.h +++ b/esphome/components/sml/sml.h @@ -27,9 +27,7 @@ class Sml : public Component, public uart::UARTDevice { void add_on_data_callback(std::function, bool)> &&callback); protected: - void process_sml_file_(const bytes &sml_data); - void log_obis_info_(const std::vector &obis_info_vec); - void publish_obis_info_(const std::vector &obis_info_vec); + void process_sml_file_(const byte_span &sml_data); char check_start_end_bytes_(uint8_t byte); void publish_value_(const ObisInfo &obis_info); diff --git a/esphome/components/sml/sml_parser.cpp b/esphome/components/sml/sml_parser.cpp index 2cc71e87fa..78d89d1937 100644 --- a/esphome/components/sml/sml_parser.cpp +++ b/esphome/components/sml/sml_parser.cpp @@ -5,21 +5,16 @@ namespace esphome { namespace sml { -SmlFile::SmlFile(bytes buffer) : buffer_(std::move(buffer)) { +SmlFile::SmlFile(byte_span const &buffer) : buffer_(buffer) { // extract messages this->pos_ = 0; while (this->pos_ < this->buffer_.size()) { - if (this->buffer_[this->pos_] == 0x00) - break; // EndOfSmlMsg - - SmlNode message = SmlNode(); - if (!this->setup_node(&message)) + if (!this->setup_node(this->messages)) break; - this->messages.emplace_back(message); } } -bool SmlFile::setup_node(SmlNode *node) { +bool SmlFile::setup_node(std::vector &nodes) { // If the TL field is 0x00, this is the end of the message // (see 6.3.1 of SML protocol definition) if (this->buffer_[this->pos_] == 0x00) { @@ -61,49 +56,43 @@ bool SmlFile::setup_node(SmlNode *node) { if (this->pos_ + length > this->buffer_.size()) return false; - node->type = type; - node->nodes.clear(); - node->value_bytes.clear(); - if (type == SML_LIST) { - node->nodes.reserve(length); + std::vector child_nodes; + child_nodes.reserve(length); for (size_t i = 0; i != length; i++) { - SmlNode child_node = SmlNode(); - if (!this->setup_node(&child_node)) + if (!this->setup_node(child_nodes)) return false; - node->nodes.emplace_back(child_node); } + nodes.emplace_back(type, std::move(child_nodes)); } else { // Value starts at the current position // Value ends "length" bytes later, // (since the TL field is counted but already subtracted from length) - node->value_bytes = bytes(this->buffer_.begin() + this->pos_, this->buffer_.begin() + this->pos_ + length); + nodes.emplace_back(type, byte_span(this->buffer_.begin() + this->pos_, length)); // Increment the pointer past all consumed bytes this->pos_ += length; } return true; } -std::vector SmlFile::get_obis_info() { - std::vector obis_info; +void SmlFile::for_each_obis_info(const std::function &callback) { for (auto const &message : messages) { - SmlNode message_body = message.nodes[3]; - uint16_t message_type = bytes_to_uint(message_body.nodes[0].value_bytes); + auto message_body = message.nodes[3]; + auto message_type = bytes_to_uint(message_body.nodes[0].value_bytes); if (message_type != SML_GET_LIST_RES) continue; - SmlNode get_list_response = message_body.nodes[1]; - bytes server_id = get_list_response.nodes[1].value_bytes; - SmlNode val_list = get_list_response.nodes[4]; + auto get_list_response = message_body.nodes[1]; + auto server_id = get_list_response.nodes[1].value_bytes; + auto val_list = get_list_response.nodes[4]; for (auto const &val_list_entry : val_list.nodes) { - obis_info.emplace_back(server_id, val_list_entry); + callback(ObisInfo(server_id, val_list_entry)); } } - return obis_info; } -std::string bytes_repr(const bytes &buffer) { +std::string bytes_repr(const byte_span &buffer) { std::string repr; for (auto const value : buffer) { repr += str_sprintf("%02x", value & 0xff); @@ -111,7 +100,7 @@ std::string bytes_repr(const bytes &buffer) { return repr; } -uint64_t bytes_to_uint(const bytes &buffer) { +uint64_t bytes_to_uint(const byte_span &buffer) { uint64_t val = 0; for (auto const value : buffer) { val = (val << 8) + value; @@ -119,7 +108,7 @@ uint64_t bytes_to_uint(const bytes &buffer) { return val; } -int64_t bytes_to_int(const bytes &buffer) { +int64_t bytes_to_int(const byte_span &buffer) { uint64_t tmp = bytes_to_uint(buffer); int64_t val; @@ -135,14 +124,14 @@ int64_t bytes_to_int(const bytes &buffer) { return val; } -std::string bytes_to_string(const bytes &buffer) { return std::string(buffer.begin(), buffer.end()); } +std::string bytes_to_string(const byte_span &buffer) { return std::string(buffer.begin(), buffer.end()); } -ObisInfo::ObisInfo(bytes server_id, SmlNode val_list_entry) : server_id(std::move(server_id)) { +ObisInfo::ObisInfo(byte_span const &server_id, SmlNode const &val_list_entry) : server_id(server_id) { this->code = val_list_entry.nodes[0].value_bytes; this->status = val_list_entry.nodes[1].value_bytes; this->unit = bytes_to_uint(val_list_entry.nodes[3].value_bytes); this->scaler = bytes_to_int(val_list_entry.nodes[4].value_bytes); - SmlNode value_node = val_list_entry.nodes[5]; + auto value_node = val_list_entry.nodes[5]; this->value = value_node.value_bytes; this->value_type = value_node.type; } diff --git a/esphome/components/sml/sml_parser.h b/esphome/components/sml/sml_parser.h index fca859d4b8..9dd9222e2b 100644 --- a/esphome/components/sml/sml_parser.h +++ b/esphome/components/sml/sml_parser.h @@ -5,50 +5,55 @@ #include #include #include "constants.h" +#include "span.h" namespace esphome { namespace sml { using bytes = std::vector; +using byte_span = Span; class SmlNode { public: - uint8_t type; - bytes value_bytes; - std::vector nodes; + SmlNode(uint8_t type, byte_span const &bytes) : type(type), value_bytes(bytes) {} + SmlNode(uint8_t type, std::vector &&nodes) : type(type), nodes(nodes) {} + + const uint8_t type; + const byte_span value_bytes; + const std::vector nodes; }; class ObisInfo { public: - ObisInfo(bytes server_id, SmlNode val_list_entry); - bytes server_id; - bytes code; - bytes status; + ObisInfo(byte_span const &server_id, SmlNode const &val_list_entry); + byte_span server_id; + byte_span code; + byte_span status; char unit; char scaler; - bytes value; + byte_span value; uint16_t value_type; std::string code_repr() const; }; class SmlFile { public: - SmlFile(bytes buffer); - bool setup_node(SmlNode *node); + SmlFile(byte_span const &buffer); + bool setup_node(std::vector &nodes); std::vector messages; - std::vector get_obis_info(); + void for_each_obis_info(const std::function &callback); protected: - const bytes buffer_; + const byte_span buffer_; size_t pos_; }; -std::string bytes_repr(const bytes &buffer); +std::string bytes_repr(const byte_span &buffer); -uint64_t bytes_to_uint(const bytes &buffer); +uint64_t bytes_to_uint(const byte_span &buffer); -int64_t bytes_to_int(const bytes &buffer); +int64_t bytes_to_int(const byte_span &buffer); -std::string bytes_to_string(const bytes &buffer); +std::string bytes_to_string(const byte_span &buffer); } // namespace sml } // namespace esphome diff --git a/esphome/components/sml/span.h b/esphome/components/sml/span.h new file mode 100644 index 0000000000..9671e5186d --- /dev/null +++ b/esphome/components/sml/span.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace esphome { +namespace sml { + +template class Span { + public: + Span() : data_(nullptr), size_(0) {} + Span(T *data, size_t size) : data_(data), size_(size) {} + Span(const Span &) = default; + + Span &operator=(const Span &) = default; + size_t size() const { return size_; } + + const T &operator[](size_t index) const { return data_[index]; } + const T *begin() const { return data_; } + const T *end() const { return data_ + size_; } + + T &operator[](size_t index) { return data_[index]; } + T *begin() { return data_; } + T *end() { return data_ + size_; } + + private: + T *data_; + size_t size_; +}; + +} // namespace sml +} // namespace esphome