diff --git a/lib/everest/ocpp/doc/networkconnectivity/README.md b/lib/everest/ocpp/doc/networkconnectivity/README.md index eaf643dc35..bc16cf0700 100644 --- a/lib/everest/ocpp/doc/networkconnectivity/README.md +++ b/lib/everest/ocpp/doc/networkconnectivity/README.md @@ -14,8 +14,8 @@ be activated before it is possible to connect to this interface. To do this, you In the implementation of this callback, you have to create a promise and return the future to the promise: ```cpp -std::promise promise(); -std::future future = promise.get_future(); +std::promise promise(); +std::future future = promise.get_future(); return future; ``` diff --git a/lib/everest/ocpp/include/ocpp/v2/connectivity_manager.hpp b/lib/everest/ocpp/include/ocpp/common/connectivity_manager.hpp similarity index 87% rename from lib/everest/ocpp/include/ocpp/v2/connectivity_manager.hpp rename to lib/everest/ocpp/include/ocpp/common/connectivity_manager.hpp index 80c6687dca..8152be374e 100644 --- a/lib/everest/ocpp/include/ocpp/v2/connectivity_manager.hpp +++ b/lib/everest/ocpp/include/ocpp/common/connectivity_manager.hpp @@ -12,8 +12,8 @@ #include namespace ocpp { namespace v2 { - class DeviceModel; +} /// \brief The result of a configuration of a network profile. struct ConfigNetworkResult { @@ -22,15 +22,24 @@ struct ConfigNetworkResult { }; using WebsocketConnectionCallback = - std::function; using WebsocketConnectionFailedCallback = std::function; using ConfigureNetworkConnectionProfileCallback = std::function( - const std::int32_t configuration_slot, const NetworkConnectionProfile& network_connection_profile)>; + const std::int32_t configuration_slot, const ocpp::v2::NetworkConnectionProfile& network_connection_profile)>; class ConnectivityManagerInterface { public: virtual ~ConnectivityManagerInterface() = default; + + /// \brief Set the \p callback that is called when a message is received from the websocket + /// + virtual void set_message_callback(const std::function& callback) = 0; + + /// \brief Set the logger \p logging + /// + virtual void set_logging(std::shared_ptr logging) = 0; + /// \brief Set the websocket \p authorization_key /// virtual void set_websocket_authorization_key(const std::string& authorization_key) = 0; @@ -63,7 +72,7 @@ class ConnectivityManagerInterface { /// \brief Gets the cached NetworkConnectionProfile based on the given \p configuration_slot. /// This returns the value from the cached network connection profiles. /// \return Returns a profile if the slot is found - virtual std::optional + virtual std::optional get_network_connection_profile(const std::int32_t configuration_slot) const = 0; /// \brief Get the priority of the given configuration slot. @@ -107,7 +116,7 @@ class ConnectivityManagerInterface { /// /// \param ocpp_interface The interface that is disconnected. /// - virtual void on_network_disconnected(OCPPInterfaceEnum ocpp_interface) = 0; + virtual void on_network_disconnected(ocpp::v2::OCPPInterfaceEnum ocpp_interface) = 0; /// \brief Called when the charging station certificate is changed /// @@ -120,7 +129,7 @@ class ConnectivityManagerInterface { class ConnectivityManager : public ConnectivityManagerInterface { private: /// \brief Reference to the device model - DeviceModel& device_model; + ocpp::v2::DeviceModel& device_model; /// \brief Pointer to the evse security class std::shared_ptr evse_security; /// \brief Pointer to the logger @@ -144,16 +153,16 @@ class ConnectivityManager : public ConnectivityManagerInterface { std::int32_t active_network_configuration_priority; int last_known_security_level; /// @brief Local cached network connection profiles - std::vector cached_network_connection_profiles; + std::vector cached_network_connection_profiles; /// @brief local cached network connection priorities std::vector network_connection_slots; OcppProtocolVersion connected_ocpp_version; public: - ConnectivityManager(DeviceModel& device_model, std::shared_ptr evse_security, - std::shared_ptr logging, - const std::function& message_callback); + ConnectivityManager(ocpp::v2::DeviceModel& device_model, std::shared_ptr evse_security); + void set_message_callback(const std::function& callback) override; + void set_logging(std::shared_ptr logging) override; void set_websocket_authorization_key(const std::string& authorization_key) override; void set_websocket_connection_options(const WebsocketConnectionOptions& connection_options) override; void set_websocket_connection_options_without_reconnect() override; @@ -161,15 +170,16 @@ class ConnectivityManager : public ConnectivityManagerInterface { void set_websocket_disconnected_callback(WebsocketConnectionCallback callback) override; void set_websocket_connection_failed_callback(WebsocketConnectionFailedCallback callback) override; void set_configure_network_connection_profile_callback(ConfigureNetworkConnectionProfileCallback callback) override; - std::optional + std::optional get_network_connection_profile(const std::int32_t configuration_slot) const override; - std::optional get_priority_from_configuration_slot(const int configuration_slot) const override; + std::optional + get_priority_from_configuration_slot(const std::int32_t configuration_slot) const override; const std::vector& get_network_connection_slots() const override; bool is_websocket_connected() override; void connect(std::optional network_profile_slot = std::nullopt) override; void disconnect() override; bool send_to_websocket(const std::string& message) override; - void on_network_disconnected(OCPPInterfaceEnum ocpp_interface) override; + void on_network_disconnected(ocpp::v2::OCPPInterfaceEnum ocpp_interface) override; void on_charging_station_certificate_changed() override; void confirm_successful_connection() override; @@ -190,7 +200,7 @@ class ConnectivityManager : public ConnectivityManagerInterface { /// \return The network configuration containing the network interface to use, nullptr if the request failed or the /// callback is not configured std::optional - handle_configure_network_connection_profile_callback(int slot, const NetworkConnectionProfile& profile); + handle_configure_network_connection_profile_callback(int slot, const ocpp::v2::NetworkConnectionProfile& profile); /// \brief Function invoked when the web socket connected with the \p security_profile /// @@ -237,5 +247,4 @@ class ConnectivityManager : public ConnectivityManagerInterface { void remove_network_connection_profiles_below_actual_security_profile(); }; -} // namespace v2 } // namespace ocpp diff --git a/lib/everest/ocpp/include/ocpp/v2/charge_point.hpp b/lib/everest/ocpp/include/ocpp/v2/charge_point.hpp index aba427dff0..1903b2aa9d 100644 --- a/lib/everest/ocpp/include/ocpp/v2/charge_point.hpp +++ b/lib/everest/ocpp/include/ocpp/v2/charge_point.hpp @@ -89,6 +89,28 @@ class ChargePointInterface { /// The handlers /// @{ + /// + /// \brief Shall be called when a websocket connection has been established in case the connectivity_handler is + /// provided exernally. + /// \param configuration_slot The network profile slot used for the connection. + /// \param network_connection_profile The network connection profile used for the connection. + virtual void on_websocket_connected(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile, + const OcppProtocolVersion ocpp_version) = 0; + + /// + /// \brief Shall be called when a websocket connection has been disconnected in case the connectivity_handler is + /// provided externally. + /// \param configuration_slot The network profile slot used for the connection. + /// \param network_connection_profile The network connection profile used for the connection. + virtual void on_websocket_disconnected(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile) = 0; + + /// \brief Shall be called when a websocket connection attempt has failed in case the connectivity_handler is + /// provided externally. + /// \param reason The reason why the connection failed. + virtual void on_websocket_connection_failed(ConnectionFailedReason reason) = 0; + /// /// \brief Can be called when a network is disconnected, for example when an ethernet cable is removed. /// @@ -345,7 +367,7 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa private: std::shared_ptr device_model; std::unique_ptr evse_manager; - std::unique_ptr connectivity_manager; + std::shared_ptr connectivity_manager; std::unique_ptr> message_dispatcher; @@ -412,12 +434,7 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa // internal helper functions void initialize(const std::map& evse_connector_structure, const std::string& message_log_path); - void websocket_connected_callback(const int configuration_slot, - const NetworkConnectionProfile& network_connection_profile, - const OcppProtocolVersion ocpp_version); - void websocket_disconnected_callback(const int configuration_slot, - const NetworkConnectionProfile& network_connection_profile); - void websocket_connection_failed(ConnectionFailedReason reason); + OcspUpdater make_ocsp_updater(); void update_dm_availability_state(const std::int32_t evse_id, const std::int32_t connector_id, const ConnectorStatusEnum status); @@ -463,6 +480,21 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa /// @name Constructors for 2.0.1 /// @{ + /// \brief Construct a new ChargePoint object + /// \param evse_connector_structure Map that defines the structure of EVSE and connectors of the chargepoint. The + /// key represents the id of the EVSE and the value represents the number of connectors for this EVSE. The ids of + /// the EVSEs have to increment starting with 1. + /// \param device_model device model instance + /// \param database_handler database handler instance + /// \param evse_security Pointer to evse_security that manages security related operations + /// \param connectivity_manager connectivity manager instance + /// \param message_log_path Path to where logfiles are written to + /// \param callbacks Callbacks that will be registered for ChargePoint + ChargePoint(const std::map& evse_connector_structure, std::shared_ptr device_model, + std::shared_ptr database_handler, const std::shared_ptr evse_security, + const std::shared_ptr connectivity_manager, + const std::string& message_log_path, const Callbacks& callbacks); + /// \brief Construct a new ChargePoint object /// \param evse_connector_structure Map that defines the structure of EVSE and connectors of the chargepoint. The /// key represents the id of the EVSE and the value represents the number of connectors for this EVSE. The ids of @@ -527,6 +559,12 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa void connect_websocket(std::optional network_profile_slot = std::nullopt) override; void disconnect_websocket() override; + void on_websocket_connected(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile, + const OcppProtocolVersion ocpp_version) override; + void on_websocket_disconnected(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile) override; + void on_websocket_connection_failed(ConnectionFailedReason reason) override; void on_network_disconnected(OCPPInterfaceEnum ocpp_interface) override; void on_firmware_update_status_notification(std::int32_t request_id, diff --git a/lib/everest/ocpp/include/ocpp/v2/charge_point_callbacks.hpp b/lib/everest/ocpp/include/ocpp/v2/charge_point_callbacks.hpp index ca2b9c0031..5cb21d8992 100644 --- a/lib/everest/ocpp/include/ocpp/v2/charge_point_callbacks.hpp +++ b/lib/everest/ocpp/include/ocpp/v2/charge_point_callbacks.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include diff --git a/lib/everest/ocpp/include/ocpp/v2/functional_blocks/functional_block_context.hpp b/lib/everest/ocpp/include/ocpp/v2/functional_blocks/functional_block_context.hpp index 119d32e1c2..4c2dea3ed8 100644 --- a/lib/everest/ocpp/include/ocpp/v2/functional_blocks/functional_block_context.hpp +++ b/lib/everest/ocpp/include/ocpp/v2/functional_blocks/functional_block_context.hpp @@ -8,10 +8,10 @@ namespace ocpp { class EvseSecurity; +class ConnectivityManagerInterface; namespace v2 { class DeviceModel; -class ConnectivityManagerInterface; class EvseManagerInterface; class DatabaseHandlerInterface; class ComponentStateManagerInterface; diff --git a/lib/everest/ocpp/include/ocpp/v2/message_dispatcher.hpp b/lib/everest/ocpp/include/ocpp/v2/message_dispatcher.hpp index 3ca1ed85d3..f60e53d6ed 100644 --- a/lib/everest/ocpp/include/ocpp/v2/message_dispatcher.hpp +++ b/lib/everest/ocpp/include/ocpp/v2/message_dispatcher.hpp @@ -3,8 +3,8 @@ #pragma once +#include #include -#include #include namespace ocpp { diff --git a/lib/everest/ocpp/lib/CMakeLists.txt b/lib/everest/ocpp/lib/CMakeLists.txt index 12f111075f..0254a30de1 100644 --- a/lib/everest/ocpp/lib/CMakeLists.txt +++ b/lib/everest/ocpp/lib/CMakeLists.txt @@ -21,6 +21,7 @@ target_sources(ocpp PRIVATE ocpp/common/call_types.cpp ocpp/common/charging_station_base.cpp + ocpp/common/connectivity_manager.cpp ocpp/common/ocpp_logging.cpp ocpp/common/schemas.cpp ocpp/common/types.cpp @@ -89,7 +90,6 @@ if(LIBOCPP_ENABLE_V2) ocpp/v2/types.cpp ocpp/v2/utils.cpp ocpp/v2/component_state_manager.cpp - ocpp/v2/connectivity_manager.cpp ocpp/v2/message_dispatcher.cpp ocpp/v2/functional_blocks/authorization.cpp ocpp/v2/functional_blocks/availability.cpp diff --git a/lib/everest/ocpp/lib/ocpp/v2/connectivity_manager.cpp b/lib/everest/ocpp/lib/ocpp/common/connectivity_manager.cpp similarity index 96% rename from lib/everest/ocpp/lib/ocpp/v2/connectivity_manager.cpp rename to lib/everest/ocpp/lib/ocpp/common/connectivity_manager.cpp index 21abd1eb0f..61efef8698 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/connectivity_manager.cpp +++ b/lib/everest/ocpp/lib/ocpp/common/connectivity_manager.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2020 - 2024 Pionix GmbH and Contributors to EVerest -#include +#include #include #include @@ -17,16 +17,21 @@ constexpr std::int32_t default_network_config_timeout_seconds = 60; } // namespace namespace ocpp { -namespace v2 { -ConnectivityManager::ConnectivityManager(DeviceModel& device_model, std::shared_ptr evse_security, - std::shared_ptr logging, - const std::function& message_callback) : +using NetworkConnectionProfile = ocpp::v2::NetworkConnectionProfile; +using AttributeEnum = ocpp::v2::AttributeEnum; +using DeviceModel = ocpp::v2::DeviceModel; +using OCPPInterfaceEnum = ocpp::v2::OCPPInterfaceEnum; +using SetNetworkProfileRequest = ocpp::v2::SetNetworkProfileRequest; +namespace ControllerComponentVariables = ocpp::v2::ControllerComponentVariables; + +ConnectivityManager::ConnectivityManager(DeviceModel& device_model, std::shared_ptr evse_security) : device_model{device_model}, evse_security{evse_security}, - logging{logging}, + message_callback([](const std::string& message) { + EVLOG_warning << "No message callback set in ConnectivityManager. Dropping message: " << message; + }), websocket{nullptr}, - message_callback{message_callback}, wants_to_be_connected{false}, active_network_configuration_priority{0}, last_known_security_level{0}, @@ -34,6 +39,14 @@ ConnectivityManager::ConnectivityManager(DeviceModel& device_model, std::shared_ cache_network_connection_profiles(); } +void ConnectivityManager::set_message_callback(const std::function& callback) { + this->message_callback = callback; +} + +void ConnectivityManager::set_logging(std::shared_ptr logging) { + this->logging = logging; +} + void ConnectivityManager::set_websocket_authorization_key(const std::string& authorization_key) { if (this->websocket != nullptr) { this->websocket->set_authorization_key(authorization_key); @@ -338,7 +351,7 @@ ConnectivityManager::get_ws_connection_options(const std::int32_t configuration_ this->device_model.get_value(ControllerComponentVariables::SecurityCtrlrIdentity), network_connection_profile.securityProfile); - const auto ocpp_versions = utils::get_ocpp_protocol_versions( + const auto ocpp_versions = ocpp::v2::utils::get_ocpp_protocol_versions( this->device_model.get_value(ControllerComponentVariables::SupportedOcppVersions)); WebsocketConnectionOptions connection_options{ @@ -525,5 +538,4 @@ void ConnectivityManager::remove_network_connection_profiles_below_actual_securi AttributeEnum::Actual, new_network_priority, VARIABLE_ATTRIBUTE_VALUE_SOURCE_INTERNAL); } -} // namespace v2 } // namespace ocpp diff --git a/lib/everest/ocpp/lib/ocpp/common/websocket/websocket.cpp b/lib/everest/ocpp/lib/ocpp/common/websocket/websocket.cpp index 0c4610ff4c..8ad34b84f8 100644 --- a/lib/everest/ocpp/lib/ocpp/common/websocket/websocket.cpp +++ b/lib/everest/ocpp/lib/ocpp/common/websocket/websocket.cpp @@ -16,6 +16,11 @@ namespace ocpp { Websocket::Websocket(const WebsocketConnectionOptions& connection_options, std::shared_ptr evse_security, std::shared_ptr logging) : logging(logging) { + + if (logging == nullptr) { + throw std::runtime_error("Websocket requires a valid MessageLogging instance"); + } + this->websocket = std::make_unique(connection_options, evse_security); } diff --git a/lib/everest/ocpp/lib/ocpp/v2/charge_point.cpp b/lib/everest/ocpp/lib/ocpp/v2/charge_point.cpp index a1bd17039e..145bbb5b04 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/charge_point.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/charge_point.cpp @@ -48,6 +48,24 @@ namespace v2 { const auto DEFAULT_MESSAGE_QUEUE_SIZE_THRESHOLD = 1000; ChargePoint::ChargePoint(const std::map& evse_connector_structure, + std::shared_ptr device_model, std::shared_ptr database_handler, + const std::shared_ptr evse_security, + const std::shared_ptr connectivity_manager, + const std::string& message_log_path, const Callbacks& callbacks) : + ocpp::ChargingStationBase(evse_security), + device_model(device_model), + database_handler(database_handler), + connectivity_manager(connectivity_manager), + registration_status(RegistrationStatusEnum::Rejected), + skip_invalid_csms_certificate_notifications(false), + upload_log_status(UploadLogStatusEnum::Idle), + bootreason(BootReasonEnum::PowerUp), + ocsp_updater(make_ocsp_updater()), + callbacks(callbacks) { + initialize(evse_connector_structure, message_log_path); +} + +ChargePoint::ChargePoint(const std::map& evse_connector_structure, std::shared_ptr device_model, std::shared_ptr database_handler, std::shared_ptr> message_queue, const std::string& message_log_path, const std::shared_ptr evse_security, @@ -60,33 +78,8 @@ ChargePoint::ChargePoint(const std::map& evse_connec skip_invalid_csms_certificate_notifications(false), upload_log_status(UploadLogStatusEnum::Idle), bootreason(BootReasonEnum::PowerUp), - ocsp_updater(this->evse_security, - [this](GetCertificateStatusRequest req) -> GetCertificateStatusResponse { - try { - return this->send_callback( - MessageType::GetCertificateStatusResponse)(req); - } catch (const UnexpectedMessageTypeFromCSMS& e) { - EVLOG_warning << e.what(); - } - GetCertificateStatusResponse response; - response.status = GetCertificateStatusEnum::Failed; - return response; - }), + ocsp_updater(make_ocsp_updater()), callbacks(callbacks) { - - if (!this->device_model) { - EVLOG_AND_THROW(std::invalid_argument("Device model should not be null")); - } - - // Make sure the received callback struct is completely filled early before we actually start running - if (!this->callbacks.all_callbacks_valid(this->device_model, evse_connector_structure)) { - EVLOG_AND_THROW(std::invalid_argument("All non-optional callbacks must be supplied")); - } - - if (!this->database_handler) { - EVLOG_AND_THROW(std::invalid_argument("Database handler should not be null")); - } - initialize(evse_connector_structure, message_log_path); } @@ -117,12 +110,11 @@ ChargePoint::ChargePoint(const std::map& evse_connec ChargePoint::~ChargePoint() = default; void ChargePoint::start(BootReasonEnum bootreason, bool start_connecting) { + this->connectivity_manager->set_message_callback( + std::bind(&ChargePoint::message_callback, this, std::placeholders::_1)); this->message_queue->start(); this->bootreason = bootreason; - // Trigger all initial status notifications and callbacks related to component state - // Should be done before sending the BootNotification.req so that the correct states can be reported - this->component_state_manager->trigger_all_effective_availability_changed_callbacks(); // get transaction messages from db (if there are any) so they can be sent again. this->message_queue->get_persisted_messages_from_db(); this->provisioning->boot_notification_req(bootreason); @@ -175,6 +167,82 @@ void ChargePoint::disconnect_websocket() { this->connectivity_manager->disconnect(); } +void ChargePoint::on_websocket_connected(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile, + const OcppProtocolVersion ocpp_version) { + this->message_queue->resume(this->message_queue_resume_delay); + this->ocpp_version = ocpp_version; + if (this->registration_status == RegistrationStatusEnum::Accepted) { + this->connectivity_manager->confirm_successful_connection(); + + if (this->time_disconnected.time_since_epoch() != 0s) { + // handle offline threshold + // Get the current time point using steady_clock + auto offline_duration = std::chrono::steady_clock::now() - this->time_disconnected; + + // B04.FR.01 + // If offline period exceeds offline threshold then send the status notification for all connectors + if (offline_duration > std::chrono::seconds(this->device_model->get_value( + ControllerComponentVariables::OfflineThreshold))) { + EVLOG_debug << "offline for more than offline threshold "; + this->component_state_manager->send_status_notification_all_connectors(); + } else { + // B04.FR.02 + // If offline period doesn't exceed offline threshold then send the status notification for all + // connectors that changed state + EVLOG_debug << "offline for less than offline threshold "; + this->component_state_manager->send_status_notification_changed_connectors(); + } + this->security->init_certificate_expiration_check_timers(); // re-init as timers are stopped on disconnect + } + } + this->time_disconnected = std::chrono::time_point(); + + // We have a connection again so next time it fails we should send the notification again + this->skip_invalid_csms_certificate_notifications = false; + + if (this->callbacks.connection_state_changed_callback.has_value()) { + this->callbacks.connection_state_changed_callback.value()(true, configuration_slot, network_connection_profile, + ocpp_version); + } +} + +void ChargePoint::on_websocket_disconnected(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile) { + this->message_queue->pause(); + + // check if offline threshold has been defined + if (this->device_model->get_value(ControllerComponentVariables::OfflineThreshold) != 0) { + // Get the current time point using steady_clock + this->time_disconnected = std::chrono::steady_clock::now(); + } + + this->security->stop_certificate_expiration_check_timers(); + if (this->callbacks.connection_state_changed_callback.has_value()) { + this->callbacks.connection_state_changed_callback.value()(false, configuration_slot, network_connection_profile, + this->ocpp_version); + } +} + +void ChargePoint::on_websocket_connection_failed(ConnectionFailedReason reason) { + switch (reason) { + case ConnectionFailedReason::InvalidCSMSCertificate: + if (!this->skip_invalid_csms_certificate_notifications) { + this->security->security_event_notification_req(CiString<50>(ocpp::security_events::INVALIDCSMSCERTIFICATE), + std::nullopt, true, true); + this->skip_invalid_csms_certificate_notifications = true; + } else { + EVLOG_debug << "Skipping InvalidCsmsCertificate SecurityEvent since it has been sent already"; + } + break; + case ConnectionFailedReason::FailedToAuthenticateAtCsms: + const auto& security_event = ocpp::security_events::FAILEDTOAUTHENTICATEATCSMS; + this->security->security_event_notification_req(CiString<50>(security_event), std::nullopt, true, + utils::is_critical(security_event)); + break; + } +} + void ChargePoint::on_network_disconnected(OCPPInterfaceEnum ocpp_interface) { this->connectivity_manager->on_network_disconnected(ocpp_interface); } @@ -420,6 +488,19 @@ void ChargePoint::on_ev_charging_needs(const NotifyEVChargingNeedsRequest& reque void ChargePoint::initialize(const std::map& evse_connector_structure, const std::string& message_log_path) { + if (!this->device_model) { + EVLOG_AND_THROW(std::invalid_argument("Device model should not be null")); + } + + // Make sure the received callback struct is completely filled early before we actually start running + if (!this->callbacks.all_callbacks_valid(this->device_model, evse_connector_structure)) { + EVLOG_AND_THROW(std::invalid_argument("All non-optional callbacks must be supplied")); + } + + if (!this->database_handler) { + EVLOG_AND_THROW(std::invalid_argument("Database handler should not be null")); + } + this->device_model->check_integrity(evse_connector_structure); this->database_handler->open_connection(); this->component_state_manager = std::make_shared( @@ -475,23 +556,29 @@ void ChargePoint::initialize(const std::map& evse_co this->evse_manager = std::make_unique( evse_connector_structure, *this->device_model, this->database_handler, component_state_manager, transaction_meter_value_callback, this->callbacks.pause_charging_callback); - this->configure_message_logging_format(message_log_path); - this->connectivity_manager = - std::make_unique(*this->device_model, this->evse_security, this->logging, - [this](const std::string& message) { this->message_callback(message); }); + // Trigger all initial status notifications and callbacks related to component state + // Should be done before sending the BootNotification.req so that the correct states can be reported + this->component_state_manager->trigger_all_effective_availability_changed_callbacks(); - this->connectivity_manager->set_websocket_connected_callback( - [this](int configuration_slot, const NetworkConnectionProfile& network_connection_profile, - const OcppProtocolVersion ocpp_version) { - this->websocket_connected_callback(configuration_slot, network_connection_profile, ocpp_version); - }); - this->connectivity_manager->set_websocket_disconnected_callback( - [this](int configuration_slot, const NetworkConnectionProfile& network_connection_profile, auto) { - this->websocket_disconnected_callback(configuration_slot, network_connection_profile); - }); - this->connectivity_manager->set_websocket_connection_failed_callback( - [this](ConnectionFailedReason reason) { this->websocket_connection_failed(reason); }); + this->configure_message_logging_format(message_log_path); + + if (this->connectivity_manager == nullptr) { + // connectivity manager was not provided in constructor. Create a new one and set the message callbacks + this->connectivity_manager = std::make_shared(*this->device_model, this->evse_security); + this->connectivity_manager->set_websocket_connected_callback( + [this](int configuration_slot, const NetworkConnectionProfile& network_connection_profile, + const OcppProtocolVersion ocpp_version) { + this->on_websocket_connected(configuration_slot, network_connection_profile, ocpp_version); + }); + this->connectivity_manager->set_websocket_disconnected_callback( + [this](int configuration_slot, const NetworkConnectionProfile& network_connection_profile, auto) { + this->on_websocket_disconnected(configuration_slot, network_connection_profile); + }); + this->connectivity_manager->set_websocket_connection_failed_callback( + std::bind(&ChargePoint::on_websocket_connection_failed, this, std::placeholders::_1)); + } + this->connectivity_manager->set_logging(this->logging); if (this->message_queue == nullptr) { std::set message_types_discard_for_queueing; @@ -642,6 +729,20 @@ void ChargePoint::initialize(const std::map& evse_co VARIABLE_ATTRIBUTE_VALUE_SOURCE_INTERNAL, true); } +OcspUpdater ChargePoint::make_ocsp_updater() { + return OcspUpdater(this->evse_security, [this](GetCertificateStatusRequest req) -> GetCertificateStatusResponse { + try { + return this->send_callback( + MessageType::GetCertificateStatusResponse)(req); + } catch (const UnexpectedMessageTypeFromCSMS& e) { + EVLOG_warning << e.what(); + } + GetCertificateStatusResponse response; + response.status = GetCertificateStatusEnum::Failed; + return response; + }); +} + void ChargePoint::handle_message(const EnhancedMessage& message) { const auto& json_message = message.message; try { @@ -1078,81 +1179,6 @@ std::optional ChargePoint::data_transfer_req(const DataTra return this->data_transfer->data_transfer_req(request); } -void ChargePoint::websocket_connected_callback(const int configuration_slot, - const NetworkConnectionProfile& network_connection_profile, - const OcppProtocolVersion ocpp_version) { - this->message_queue->resume(this->message_queue_resume_delay); - this->ocpp_version = ocpp_version; - if (this->registration_status == RegistrationStatusEnum::Accepted) { - this->connectivity_manager->confirm_successful_connection(); - - if (this->time_disconnected.time_since_epoch() != 0s) { - // handle offline threshold - // Get the current time point using steady_clock - auto offline_duration = std::chrono::steady_clock::now() - this->time_disconnected; - - // B04.FR.01 - // If offline period exceeds offline threshold then send the status notification for all connectors - if (offline_duration > std::chrono::seconds(this->device_model->get_value( - ControllerComponentVariables::OfflineThreshold))) { - EVLOG_debug << "offline for more than offline threshold "; - this->component_state_manager->send_status_notification_all_connectors(); - } else { - // B04.FR.02 - // If offline period doesn't exceed offline threshold then send the status notification for all - // connectors that changed state - EVLOG_debug << "offline for less than offline threshold "; - this->component_state_manager->send_status_notification_changed_connectors(); - } - this->security->init_certificate_expiration_check_timers(); // re-init as timers are stopped on disconnect - } - } - this->time_disconnected = std::chrono::time_point(); - - // We have a connection again so next time it fails we should send the notification again - this->skip_invalid_csms_certificate_notifications = false; - - if (this->callbacks.connection_state_changed_callback.has_value()) { - this->callbacks.connection_state_changed_callback.value()(true, configuration_slot, network_connection_profile, - ocpp_version); - } -} - -void ChargePoint::websocket_disconnected_callback(const int configuration_slot, - const NetworkConnectionProfile& network_connection_profile) { - this->message_queue->pause(); - - // check if offline threshold has been defined - if (this->device_model->get_value(ControllerComponentVariables::OfflineThreshold) != 0) { - // Get the current time point using steady_clock - this->time_disconnected = std::chrono::steady_clock::now(); - } - - this->security->stop_certificate_expiration_check_timers(); - if (this->callbacks.connection_state_changed_callback.has_value()) { - this->callbacks.connection_state_changed_callback.value()(false, configuration_slot, network_connection_profile, - this->ocpp_version); - } -} - -void ChargePoint::websocket_connection_failed(ConnectionFailedReason reason) { - switch (reason) { - case ConnectionFailedReason::InvalidCSMSCertificate: - if (!this->skip_invalid_csms_certificate_notifications) { - this->security->security_event_notification_req(CiString<50>(ocpp::security_events::INVALIDCSMSCERTIFICATE), - std::nullopt, true, true); - this->skip_invalid_csms_certificate_notifications = true; - } else { - EVLOG_debug << "Skipping InvalidCsmsCertificate SecurityEvent since it has been sent already"; - } - break; - case ConnectionFailedReason::FailedToAuthenticateAtCsms: - const auto& security_event = ocpp::security_events::FAILEDTOAUTHENTICATEATCSMS; - this->security->security_event_notification_req(CiString<50>(security_event), std::nullopt, true, - utils::is_critical(security_event)); - break; - } -} void ChargePoint::update_dm_availability_state(const std::int32_t evse_id, const std::int32_t connector_id, const ConnectorStatusEnum status) { RequiredComponentVariable charging_station = ControllerComponentVariables::ChargingStationAvailabilityState; diff --git a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/authorization.cpp b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/authorization.cpp index 4fe3bc6016..77c3e40010 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/authorization.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/authorization.cpp @@ -3,9 +3,9 @@ #include +#include #include #include -#include #include #include #include diff --git a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/diagnostics.cpp b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/diagnostics.cpp index 7c7b49df45..7083d49a60 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/diagnostics.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/diagnostics.cpp @@ -3,8 +3,8 @@ #include +#include #include -#include #include #include #include diff --git a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/provisioning.cpp b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/provisioning.cpp index c6f005d94f..83073adba6 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/provisioning.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/provisioning.cpp @@ -3,10 +3,10 @@ #include +#include #include #include #include -#include #include #include #include diff --git a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/remote_transaction_control.cpp b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/remote_transaction_control.cpp index 8a4081f489..85da1f227d 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/remote_transaction_control.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/remote_transaction_control.cpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include #include diff --git a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/security.cpp b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/security.cpp index 17ca23c2bc..ba14c1d8cd 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/security.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/security.cpp @@ -3,9 +3,9 @@ #include +#include #include #include -#include #include #include #include diff --git a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/smart_charging.cpp b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/smart_charging.cpp index 8ea3d09543..1456f758c1 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/smart_charging.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/smart_charging.cpp @@ -7,7 +7,7 @@ #include -#include +#include #include #include #include diff --git a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/transaction.cpp b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/transaction.cpp index ccebc19b53..e702d698a0 100644 --- a/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/transaction.cpp +++ b/lib/everest/ocpp/lib/ocpp/v2/functional_blocks/transaction.cpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include #include diff --git a/lib/everest/ocpp/lib/ocpp/v21/functional_blocks/bidirectional.cpp b/lib/everest/ocpp/lib/ocpp/v21/functional_blocks/bidirectional.cpp index a8f64f8b30..192612a7ed 100644 --- a/lib/everest/ocpp/lib/ocpp/v21/functional_blocks/bidirectional.cpp +++ b/lib/everest/ocpp/lib/ocpp/v21/functional_blocks/bidirectional.cpp @@ -5,9 +5,9 @@ #include +#include #include #include -#include #include #include #include diff --git a/lib/everest/ocpp/tests/lib/ocpp/v2/mocks/connectivity_manager_mock.hpp b/lib/everest/ocpp/tests/lib/ocpp/v2/mocks/connectivity_manager_mock.hpp index 1898482708..05828eae35 100644 --- a/lib/everest/ocpp/tests/lib/ocpp/v2/mocks/connectivity_manager_mock.hpp +++ b/lib/everest/ocpp/tests/lib/ocpp/v2/mocks/connectivity_manager_mock.hpp @@ -5,11 +5,13 @@ #include "gmock/gmock.h" -#include +#include namespace ocpp::v2 { -class ConnectivityManagerMock : public ConnectivityManagerInterface { +class ConnectivityManagerMock : public ocpp::ConnectivityManagerInterface { public: + MOCK_METHOD(void, set_message_callback, (const std::function& callback)); + MOCK_METHOD(void, set_logging, (std::shared_ptr logging)); MOCK_METHOD(void, set_websocket_authorization_key, (const std::string& authorization_key)); MOCK_METHOD(void, set_websocket_connection_options, (const WebsocketConnectionOptions& connection_options)); MOCK_METHOD(void, set_websocket_connection_options_without_reconnect, ()); diff --git a/modules/EVSE/OCPP201/OCPP201.cpp b/modules/EVSE/OCPP201/OCPP201.cpp index 8b8a9db6d9..708e020afa 100644 --- a/modules/EVSE/OCPP201/OCPP201.cpp +++ b/modules/EVSE/OCPP201/OCPP201.cpp @@ -712,9 +712,9 @@ void OCPP201::ready() { callbacks.configure_network_connection_profile_callback = [this](const int32_t configuration_slot, const ocpp::v2::NetworkConnectionProfile& network_connection_profile) { - std::promise promise; - std::future future = promise.get_future(); - ocpp::v2::ConfigNetworkResult result; + std::promise promise; + std::future future = promise.get_future(); + ocpp::ConfigNetworkResult result; result.success = true; promise.set_value(result); return future;