diff --git a/include/ocpp/common/message_queue.hpp b/include/ocpp/common/message_queue.hpp index bafcdf4ac..b752df369 100644 --- a/include/ocpp/common/message_queue.hpp +++ b/include/ocpp/common/message_queue.hpp @@ -27,8 +27,6 @@ namespace ocpp { -const auto STANDARD_MESSAGE_TIMEOUT = std::chrono::seconds(30); - struct MessageQueueConfig { int transaction_message_attempts; int transaction_message_retry_interval; // seconds @@ -38,6 +36,8 @@ struct MessageQueueConfig { int queues_total_size_threshold; bool queue_all_messages; // cf. OCPP 2.0.1. "QueueAllMessages" in OCPPCommCtrlr + + int message_timeout_seconds = 30; }; /// \brief Contains a OCPP message in json form with additional information @@ -257,6 +257,12 @@ template class MessageQueue { } } + // Computes the current message timeout = interval * attempt + message timeout + std::chrono::seconds current_message_timeout(unsigned int attempt) { + return std::chrono::seconds(this->config.message_timeout_seconds + + (this->config.transaction_message_retry_interval * attempt)); + } + public: /// \brief Creates a new MessageQueue object with the provided \p configuration and \p send_callback MessageQueue(const std::function& send_callback, const MessageQueueConfig& config, @@ -392,7 +398,7 @@ template class MessageQueue { } else { EVLOG_debug << "Successfully sent message. UID: " << this->in_flight->uniqueId(); this->in_flight_timeout_timer.timeout([this]() { this->handle_timeout_or_callerror(std::nullopt); }, - STANDARD_MESSAGE_TIMEOUT); + this->current_message_timeout(message->message_attempts)); switch (queue_type) { case QueueType::Normal: this->normal_message_queue.pop(); @@ -612,11 +618,20 @@ template class MessageQueue { /// \brief Handles a message timeout or a CALLERROR. \p enhanced_message_opt is set only in case of CALLERROR void handle_timeout_or_callerror(const std::optional>& enhanced_message_opt) { std::lock_guard lk(this->message_mutex); - EVLOG_warning << "Message timeout or CALLERROR for: " << this->in_flight->messageType << " (" - << this->in_flight->uniqueId() << ")"; + // We got a timeout iff enhanced_message_opt is empty. Otherwise, enhanced_message_opt contains the CallError. + bool timeout = !enhanced_message_opt.has_value(); + if (timeout) { + EVLOG_warning << "Message timeout for: " << this->in_flight->messageType << " (" + << this->in_flight->uniqueId() << ")"; + } else { + EVLOG_warning << "CALLERROR for: " << this->in_flight->messageType << " (" << this->in_flight->uniqueId() + << ")"; + } + if (this->in_flight->isTransactionMessage()) { if (this->in_flight->message_attempts < this->config.transaction_message_attempts) { EVLOG_warning << "Message is transaction related and will therefore be sent again"; + // Generate a new message ID for the retry this->in_flight->message[MESSAGE_ID] = this->createMessageId(); if (this->config.transaction_message_retry_interval > 0) { // exponential backoff @@ -746,6 +761,11 @@ template class MessageQueue { this->config.transaction_message_retry_interval = transaction_message_retry_interval; } + /// \brief Set message_timeout to given \p timeout (in seconds) + void update_message_timeout(const int timeout) { + this->config.message_timeout_seconds = timeout; + } + /// \brief Creates a unique message ID /// \returns the unique message ID MessageId createMessageId() { diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index a368733c4..c5ae66f63 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -150,7 +150,8 @@ ChargePoint::ChargePoint(const std::map& evse_connector_struct this->device_model->get_optional_value(ControllerComponentVariables::MessageQueueSizeThreshold) .value_or(DEFAULT_MESSAGE_QUEUE_SIZE_THRESHOLD), this->device_model->get_optional_value(ControllerComponentVariables::QueueAllMessages) - .value_or(false)}, + .value_or(false), + this->device_model->get_value(ControllerComponentVariables::MessageTimeout)}, this->database_handler); } @@ -1369,6 +1370,27 @@ void ChargePoint::handle_variable_changed(const SetVariableData& set_variable_da this->websocket->set_connection_options(connection_options); } + if (component_variable == ControllerComponentVariables::MessageAttemptInterval) { + if (component_variable.variable.has_value()) { + this->message_queue->update_transaction_message_retry_interval( + this->device_model->get_value(ControllerComponentVariables::MessageAttemptInterval)); + } + } + + if (component_variable == ControllerComponentVariables::MessageAttempts) { + if (component_variable.variable.has_value()) { + this->message_queue->update_transaction_message_attempts( + this->device_model->get_value(ControllerComponentVariables::MessageAttempts)); + } + } + + if (component_variable == ControllerComponentVariables::MessageTimeout) { + if (component_variable.variable.has_value()) { + this->message_queue->update_message_timeout( + this->device_model->get_value(ControllerComponentVariables::MessageTimeout)); + } + } + // TODO(piet): other special handling of changed variables can be added here... }