Skip to content

Commit

Permalink
Added retry mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
paveldn committed Oct 10, 2023
1 parent 1e919e4 commit a1a2a56
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 44 deletions.
19 changes: 13 additions & 6 deletions include/protocol/haier_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<OutgoingQueueItem>;
TransportLevelHandler transport_;
Expand All @@ -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_;

};


Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -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":
Expand Down
77 changes: 42 additions & 35 deletions src/protocol/haier_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_(),
Expand All @@ -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:
Expand Down Expand Up @@ -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<FrameType, TimeoutHandler>::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<FrameType, TimeoutHandler>::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;
Expand All @@ -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;
Expand All @@ -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)
Expand Down
23 changes: 21 additions & 2 deletions test/smartair2_test/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
{
Expand Down Expand Up @@ -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);
}

0 comments on commit a1a2a56

Please sign in to comment.