From a1a2a56258da1e8a24473d37464a4e11c9f71b0e Mon Sep 17 00:00:00 2001 From: Pavlo Dudnytskyi Date: Tue, 10 Oct 2023 21:24:46 +0200 Subject: [PATCH] Added retry mechanism --- include/protocol/haier_protocol.h | 19 +++++--- library.json | 2 +- src/protocol/haier_protocol.cpp | 77 +++++++++++++++++-------------- test/smartair2_test/main.cpp | 23 ++++++++- 4 files changed, 77 insertions(+), 44 deletions(-) diff --git a/include/protocol/haier_protocol.h b/include/protocol/haier_protocol.h index f82e527..8f1266d 100644 --- a/include/protocol/haier_protocol.h +++ b/include/protocol/haier_protocol.h @@ -56,9 +56,11 @@ class ProtocolHandler ProtocolHandler& operator=(const ProtocolHandler&) = delete; explicit ProtocolHandler(ProtocolStream&) noexcept; size_t get_outgoing_queue_size() const noexcept {return this->outgoing_messages_.size(); }; - void send_message(const HaierMessage& message, bool use_crc); - void send_message(const HaierMessage& message, bool use_crc, long long answer_timeout_miliseconds); - void send_message(const HaierMessage& message, bool use_crc, std::chrono::milliseconds answer_timeout); + void set_answer_timeout(long long answer_timeout_miliseconds); + void set_answer_timeout(std::chrono::milliseconds answer_timeout); + void set_cooldown_interval(long long answer_timeout_miliseconds); + void set_cooldown_interval(std::chrono::milliseconds answer_timeout); + void send_message(const HaierMessage& message, bool use_crc, uint8_t num_retries = 0, std::chrono::milliseconds interval = std::chrono::milliseconds::zero()); void send_answer(const HaierMessage& answer); void send_answer(const HaierMessage& answer, bool use_crc); void set_message_handler(FrameType message_type, MessageHandler handler); @@ -82,7 +84,8 @@ class ProtocolHandler { const HaierMessage message; bool use_crc; - const std::chrono::milliseconds answer_timeout; + int number_of_retries; + std::chrono::milliseconds retry_interval; }; using OutgoingQueue = std::queue; TransportLevelHandler transport_; @@ -98,8 +101,12 @@ class ProtocolHandler bool incoming_message_crc_status_; bool answer_sent_; FrameType last_message_type_; - std::chrono::steady_clock::time_point cooldown_timeout_; - std::chrono::steady_clock::time_point answer_timeout_; + std::chrono::milliseconds answer_timeout_interval_; + std::chrono::milliseconds cooldown_interval_; + std::chrono::steady_clock::time_point cooldown_time_point_; + std::chrono::steady_clock::time_point answer_time_point_; + std::chrono::steady_clock::time_point retry_time_point_; + }; diff --git a/library.json b/library.json index 47064e0..b4c3bb8 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "HaierProtocol", - "version": "0.9.22", + "version": "0.9.23", "description": "A library to control Haier AC using serial protocol", "keywords": "haier, air-conditioner, uart, serial", "repository": diff --git a/src/protocol/haier_protocol.cpp b/src/protocol/haier_protocol.cpp index 5cd75d9..4b88f3c 100644 --- a/src/protocol/haier_protocol.cpp +++ b/src/protocol/haier_protocol.cpp @@ -6,8 +6,9 @@ namespace haier_protocol { -constexpr std::chrono::milliseconds MESSAGE_COOLDOWN_INTERVAL = std::chrono::milliseconds(400); +constexpr uint8_t MAX_PACKET_RETRIES = 9; constexpr std::chrono::milliseconds DEFAULT_ANSWER_TIMEOUT = std::chrono::milliseconds(200); +constexpr std::chrono::milliseconds DEFAULT_COOLDOWN_INTERVAL = std::chrono::milliseconds(400); ProtocolHandler::ProtocolHandler(ProtocolStream &stream) noexcept : transport_(stream), message_handlers_map_(), @@ -21,15 +22,18 @@ ProtocolHandler::ProtocolHandler(ProtocolStream &stream) noexcept : transport_(s processing_message_(false), incoming_message_crc_status_(false), answer_sent_(false), - last_message_type_(FrameType::UNKNOWN_FRAME_TYPE) + last_message_type_(FrameType::UNKNOWN_FRAME_TYPE), + answer_timeout_interval_(DEFAULT_ANSWER_TIMEOUT), + cooldown_interval_(DEFAULT_COOLDOWN_INTERVAL) { - this->cooldown_timeout_ = std::chrono::steady_clock::time_point(); + this->cooldown_time_point_ = std::chrono::steady_clock::time_point(); } void ProtocolHandler::loop() { this->transport_.read_data(); this->transport_.process_data(); + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); switch (this->state_) { case ProtocolState::IDLE: @@ -68,36 +72,46 @@ void ProtocolHandler::loop() } } { - std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - bool messagesAvailable = !this->outgoing_messages_.empty(); - if ((messagesAvailable) && (now >= this->cooldown_timeout_)) + if ((!this->outgoing_messages_.empty()) && (now >= this->cooldown_time_point_) && (now >= this->retry_time_point_)) { // Ready to send next message OutgoingQueueItem &msg = this->outgoing_messages_.front(); - if (this->write_message_(msg.message, msg.use_crc)) - { - this->last_message_type_ = msg.message.get_frame_type(); - this->state_ = ProtocolState::WAITING_FOR_ANSWER; - this->answer_timeout_ = now + msg.answer_timeout; + if (msg.number_of_retries > 0) { + if (this->write_message_(msg.message, msg.use_crc)) + { + this->last_message_type_ = msg.message.get_frame_type(); + this->state_ = ProtocolState::WAITING_FOR_ANSWER; + this->answer_time_point_ = now + this->answer_timeout_interval_; + this->retry_time_point_ = now + msg.retry_interval; + } + msg.number_of_retries--; + } else { + this->outgoing_messages_.pop(); + this->retry_time_point_ = now; } - this->outgoing_messages_.pop(); } } } break; case ProtocolState::WAITING_FOR_ANSWER: // Check for timeout, move to idle after timeout - if ((std::chrono::steady_clock::now() > this->answer_timeout_)) + if (now > this->answer_time_point_) { - HandlerError hres; - std::map::const_iterator handler = this->timeout_handlers_map_.find(this->last_message_type_); - if (handler != this->timeout_handlers_map_.end()) - hres = handler->second(this->last_message_type_); - else - hres = this->default_timeout_handler_(this->last_message_type_); - if (hres != HandlerError::HANDLER_OK) - { - HAIER_LOGW("Timeout handler error, msg=%02X, err=%d", this->last_message_type_, hres); + // Answer timeout + OutgoingQueueItem& msg = this->outgoing_messages_.front(); + if (msg.number_of_retries == 0) { + // No more retries, remove message + this->outgoing_messages_.pop(); + this->retry_time_point_ = now; + HandlerError hres; + std::map::const_iterator handler = this->timeout_handlers_map_.find(this->last_message_type_); + if (handler != this->timeout_handlers_map_.end()) + hres = handler->second(this->last_message_type_); + else + hres = this->default_timeout_handler_(this->last_message_type_); + if (hres != HandlerError::HANDLER_OK) { + HAIER_LOGW("Timeout handler error, msg=%02X, err=%d", this->last_message_type_, hres); + } } state_ = ProtocolState::IDLE; break; @@ -117,6 +131,9 @@ void ProtocolHandler::loop() { HAIER_LOGW("Answer handler error, msg=%02X, answ=%02X, err=%d", this->last_message_type_, msg_type, hres); } + // Answer received, remove message + this->outgoing_messages_.pop(); + this->retry_time_point_ = now; state_ = ProtocolState::IDLE; } break; @@ -139,23 +156,13 @@ bool ProtocolHandler::write_message_(const HaierMessage &message, bool use_crc) { HAIER_LOGE("Error sending message: %02X", frame_type); } - this->cooldown_timeout_ = std::chrono::steady_clock::now() + MESSAGE_COOLDOWN_INTERVAL; + this->cooldown_time_point_ = std::chrono::steady_clock::now() + this->cooldown_interval_; return is_success; } -void ProtocolHandler::send_message(const HaierMessage &message, bool use_crc) -{ - send_message(message, use_crc, DEFAULT_ANSWER_TIMEOUT); -} - -void ProtocolHandler::send_message(const HaierMessage& message, bool use_crc, long long answer_timeout_miliseconds) -{ - send_message(message, use_crc, std::chrono::milliseconds(answer_timeout_miliseconds)); -} - -void ProtocolHandler::send_message(const HaierMessage& message, bool use_crc, std::chrono::milliseconds answer_timeout) +void ProtocolHandler::send_message(const HaierMessage& message, bool use_crc, uint8_t num_repeats, std::chrono::milliseconds interval) { - this->outgoing_messages_.push({ message, use_crc, answer_timeout }); + this->outgoing_messages_.push({ message, use_crc, std::min(num_repeats, MAX_PACKET_RETRIES) + 1, interval }); } void ProtocolHandler::send_answer(const HaierMessage &answer) diff --git a/test/smartair2_test/main.cpp b/test/smartair2_test/main.cpp index 402f905..725e8db 100644 --- a/test/smartair2_test/main.cpp +++ b/test/smartair2_test/main.cpp @@ -55,11 +55,30 @@ int main(int argc, char** argv) { { uint8_t module_capabilities[2] = { 0b00000000, 0b00000111 }; const haier_protocol::HaierMessage device_version_request_message(haier_protocol::FrameType::GET_DEVICE_VERSION, module_capabilities, sizeof(module_capabilities)); - smartair2_client.send_message(device_version_request_message, false, 100); + smartair2_client.send_message(device_version_request_message, false, 3); smartair2_client.loop(); smartair2_server.loop(); smartair2_client.loop(); smartair2_server.loop(); + for (int i = 0; i <= 2; i++) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + smartair2_client.loop(); + smartair2_server.loop(); + } + } + { + // Unsupported message + const haier_protocol::HaierMessage unsupported_request_message(haier_protocol::FrameType::DOWNLINK_TRANSPARENT_TRANSMISSION); + smartair2_client.send_message(unsupported_request_message, false, 3); + smartair2_client.loop(); + smartair2_server.loop(); + smartair2_client.loop(); + smartair2_server.loop(); + for (int i = 0; i <= 6; i++) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + smartair2_client.loop(); + smartair2_server.loop(); + } } std::this_thread::sleep_for(std::chrono::milliseconds(500)); { @@ -101,7 +120,7 @@ int main(int argc, char** argv) { unsigned int warn = get_warnings_count(); unsigned int errors = get_errors_count(); std::cout << "Test results, warning: " << warn << " errors: " << errors << std::endl; - if ((warn != 2) || (errors != 0)) + if ((warn != 11) || (errors != 0)) exit(1); }