diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index d8bfe579b..f8d2ddd9c 100644 --- a/include/ocpp/v201/charge_point.hpp +++ b/include/ocpp/v201/charge_point.hpp @@ -95,6 +95,7 @@ struct Callbacks { validate_network_profile_callback; std::optional> configure_network_connection_profile_callback; + std::optional> time_sync_callback; }; /// \brief Class implements OCPP2.0.1 Charging Station @@ -123,6 +124,9 @@ class ChargePoint : ocpp::ChargingStationBase { Everest::SteadyTimer boot_notification_timer; Everest::SteadyTimer aligned_meter_values_timer; + // time keeping + std::chrono::time_point heartbeat_request_time; + // states RegistrationStatusEnum registration_status; WebsocketConnectionStatusEnum websocket_connection_status; @@ -301,6 +305,7 @@ class ChargePoint : ocpp::ChargingStationBase { // Functional Block G: Availability void handle_change_availability_req(Call call); + void handle_heartbeat_response(CallResult call); // Functional Block L: Firmware management void handle_firmware_update_req(Call call); diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 3f8061b90..2710a6bae 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -22,7 +22,8 @@ bool Callbacks::all_callbacks_valid() const { (!this->validate_network_profile_callback.has_value() or this->validate_network_profile_callback.value() != nullptr) and (!this->configure_network_connection_profile_callback.has_value() or - this->configure_network_connection_profile_callback.value() != nullptr); + this->configure_network_connection_profile_callback.value() != nullptr) and + (!this->time_sync_callback.has_value() or this->time_sync_callback.value() != nullptr); } ChargePoint::ChargePoint(const std::map& evse_connector_structure, @@ -675,6 +676,9 @@ void ChargePoint::handle_message(const EnhancedMessage& messa case MessageType::TriggerMessage: this->handle_trigger_message(json_message); break; + case MessageType::HeartbeatResponse: + this->handle_heartbeat_response(json_message); + break; case MessageType::SendLocalList: this->handle_send_local_authorization_list_req(json_message); break; @@ -1154,6 +1158,7 @@ void ChargePoint::status_notification_req(const int32_t evse_id, const int32_t c void ChargePoint::heartbeat_req() { HeartbeatRequest req; + heartbeat_request_time = std::chrono::steady_clock::now(); ocpp::Call call(req, this->message_queue->createMessageId()); this->send(call); } @@ -1267,7 +1272,9 @@ void ChargePoint::handle_boot_notification_response(CallResultheartbeat_timer.interval([this]() { this->heartbeat_req(); }, std::chrono::seconds(msg.interval)); } this->update_aligned_data_interval(); - + if (this->callbacks.time_sync_callback.has_value()) { + this->callbacks.time_sync_callback.value()(msg.currentTime); + } for (auto const& [evse_id, evse] : this->evses) { evse->trigger_status_notification_callbacks(); } @@ -1926,6 +1933,16 @@ void ChargePoint::handle_change_availability_req(Call } } +void ChargePoint::handle_heartbeat_response(CallResult call) { + if (this->callbacks.time_sync_callback.has_value()) { + // the received currentTime was the time the CSMS received the heartbeat request + // to get a system time as accurate as possible keep the time-of-flight into account + auto timeOfFlight = (std::chrono::steady_clock::now() - this->heartbeat_request_time) / 2; + ocpp::DateTime currentTimeCompensated(call.msg.currentTime.to_time_point() + timeOfFlight); + this->callbacks.time_sync_callback.value()(currentTimeCompensated); + } +} + void ChargePoint::handle_firmware_update_req(Call call) { EVLOG_debug << "Received UpdateFirmwareRequest: " << call.msg << "\nwith messageId: " << call.uniqueId; UpdateFirmwareResponse response = callbacks.update_firmware_request_callback(call.msg);