diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e066c183..9e2471129 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ option(BUILD_TESTING "Build unit tests, used if standalone project" OFF) option(CMAKE_RUN_CLANG_TIDY "Run clang-tidy" OFF) option(LIBOCPP16_BUILD_EXAMPLES "Build charge_point binary" OFF) option(OCPP_INSTALL "Install the library (shared data might be installed anyway)" ${EVC_MAIN_PROJECT}) -option(LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP "Usage of deprecated websocket++ instead of libwebsockets" OFF) +option(LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP "Websocket++ has been removed from the project" OFF) option(LIBOCPP_ENABLE_V16 "Enable OCPP 1.6 in the ocpp library" ON) option(LIBOCPP_ENABLE_V201 "Enable OCPP 2.0.1 in the ocpp library" ON) @@ -25,6 +25,10 @@ if((NOT LIBOCPP_ENABLE_V16) AND (NOT LIBOCPP_ENABLE_V201)) message(FATAL_ERROR "At least one of LIBOCPP_ENABLE_V16 and LIBOCPP_ENABLE_V201 needs to be ON") endif() +if(LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP) + message(FATAL_ERROR "Websocket++ has been removed") +endif() + if((${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME} OR ${PROJECT_NAME}_BUILD_TESTING) AND BUILD_TESTING) set(LIBOCPP_BUILD_TESTING ON) endif() @@ -39,12 +43,6 @@ if(NOT DISABLE_EDM) # In EDM mode, we can't install exports (because the dependencies usually do not install their exports) set(OCPP_INSTALL OFF) - - if(LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP) - # FIXME (aw): websocketpp doesn't play well with EDM/CPM - add_library(websocketpp::websocketpp INTERFACE IMPORTED) - set_target_properties(websocketpp::websocketpp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${websocketpp_SOURCE_DIR}") - endif() else() find_package(date REQUIRED) find_package(nlohmann_json REQUIRED) @@ -54,10 +52,6 @@ else() find_package(everest-timer REQUIRED) find_package(everest-log REQUIRED) find_package(everest-evse_security REQUIRED) - - if(LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP) - find_package(websocketpp REQUIRED) - endif() endif() # config and auxillary files diff --git a/README.md b/README.md index c176b3fc0..29e75e48f 100644 --- a/README.md +++ b/README.md @@ -553,11 +553,7 @@ In order to use the TPM keys, it is mandatory to use the default libwebsocket im ## Support for websocket++ -The old websocket++ implementation has been deprecated. For enabling websocket++ support use the following cmake option: - -```bash - cmake .. -DLIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP=ON -``` +The old websocket++ implementation has been removed. Enabling websocket++ will prevent compilation from starting. ### Support for iface diff --git a/config/v201/component_config/standardized/InternalCtrlr.json b/config/v201/component_config/standardized/InternalCtrlr.json index f27668ad4..28ce3b60f 100644 --- a/config/v201/component_config/standardized/InternalCtrlr.json +++ b/config/v201/component_config/standardized/InternalCtrlr.json @@ -752,6 +752,24 @@ "description": "If enabled the transactions that were active before shutdown will be resumed, if possible", "default": false, "type": "boolean" + }, + "NetworkConfigTimeout": { + "variable_name": "NetworkConfigTimeout", + "characteristics": { + "supportsMonitoring": true, + "dataType": "integer", + "minLimit": 1 + }, + "attributes": [ + { + "type": "Actual", + "mutability": "ReadWrite" + } + ], + "description": "Timeout value in seconds to wait for a response from a network configuration request", + "minimum": 1, + "default": "60", + "type": "integer" } }, "required": [ diff --git a/dependencies.yaml b/dependencies.yaml index 181095c94..ae64eedf0 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -25,10 +25,6 @@ date: git: https://github.com/HowardHinnant/date.git git_tag: v3.0.1 options: ["BUILD_TZ_LIB ON", "HAS_REMOTE_API 0", "USE_AUTOLOAD 0", "USE_SYSTEM_TZ_DB ON"] -websocketpp: - git: https://github.com/zaphoyd/websocketpp.git - git_tag: 0.8.2 - cmake_condition: "LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP" libevse-security: git: https://github.com/EVerest/libevse-security.git git_tag: v0.8.0 diff --git a/doc/networkconnectivity/README.md b/doc/networkconnectivity/README.md new file mode 100644 index 000000000..45e9fb5c1 --- /dev/null +++ b/doc/networkconnectivity/README.md @@ -0,0 +1,103 @@ +# Network connection profile interface + +libocpp automatically tries to connect using the given network connection profiles. +However, if you want more control, you can use the callbacks provided for the network connection. + +libocpp will automatically connect to the network profile with the highest priority. +If this fails, it will network profile with the second highest priority, and so on. + +## Set up interface (optional) + +A callback can be implemented to set up the interface. For example, if the interface is a modem, it must first be +be activated before it is possible to connect to this interface. To do this, you can implement the callback +`std::future(configure_network_connection_profile_callback(configuration_slot, NetworkConnectionProfile))` + +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(); +return future; +``` + +If the network was setup successfully, you can set the values in the promise with +```cpp +promise.set_value(configNetworkResult); +``` +This way, libocpp knows that it can connect to the given interface and will try to do so. +A timeout can be configured using `NetworkConfigTimeout' to wait longer or shorter than the default 60 seconds. + +### Bind to a specific interface + +In some cases there are multiple network interfaces available and you may want to connect to a specific one. +In `ConfigNetworkResult` you can specify which interface you want the websocket to bind to. +Sometimes an interface has more than one IP address (in the case of a local/auto link for example). +In this case you want the websocket to bind to a specific IP address. The `interface_address` in ConfigNetworkResult supports both. +It will bind to the given network interface (a string containing the name of the interface) or the given ip address (a string containing the ip address in human readable format). + +## Connect to higher network connection profile priority (optional) + +Normally, when libocpp is connected with a network connection profile, it will not disconnect. +However, there may be a situation where libocpp is connected to a profile with priority 2 or lower, and you find out at system level that an interface (with a higher priority) has changed and is now up. +A call is added so that you can suggest that libocpp switch to this profile: `bool on_try_switch_network_connection_profile(const int32_t configuration_slot)`. +libocpp will inform the caller by the return value if it tries to switch to this profile. + +## Disconnected / connected callbacks + +libocpp provides two callbacks for when the websocket is connected and disconnected. It will provide the network slot +in these callbacks, so you can keep the network connection in use (e.g. not disable the modem), or disable the network connection (example again: disable the modem). + +## Sequence diagram + +'core' can be read as any application that implements libocpp + +For step 9, ping is one way to check if a CSMS is up, but you of course can implement a way to check this yourself. + +```mermaid +sequenceDiagram +participant csms +participant libocpp +participant core +autonumber + +note over csms,libocpp: libocpp wants to connect to network connection profile + +libocpp ->>+ core: std::future(configure_network_connection_profile_callback(
configuration_slot, NetworkConnectionProfile)) +note over core: ... possible delay ... +core ->> core: Setup network, e.g. setup modem +core ->>- libocpp: promise.set_value(status,
ip_address, etc) + +alt within timeout + + %% core ->> libocpp: on_network_update (ip address) + libocpp ->> csms: connect websocket (ip address) + csms ->> libocpp: ACK + libocpp ->> core: websocket_connected_callback(configuration_slot, NetworkConnectionProfile) + core ->> core: disable unneeded interfaces,
e.g. disable modem +else timeout reached, next network connection profile selected + libocpp -->> core: std::future(configure_network_connection_profile_callback(
configuration_slot, NetworkConnectionProfile)) (see 1) +end + + +note over libocpp: CSMS is connected via connection profile prio 2 (for example modem) but prio 1 (for example eth0) comes up + +loop until prio 1 csms is found + core ->> csms: ping +end + +core ->> libocpp: on_try_switch_networkconnectionprofile(configuration_slot) +libocpp -->> core: std::future(configure_network_connection_profile_callback(
configuration_slot, NetworkConnectionProfile)) (see 1) + + +note over csms,libocpp: Network is disconnected (for example networkcable removed) + +core ->> libocpp: disconnect csms (on_network_disconnected(configuration_slot, OCPPInterfaceEnum) +libocpp ->> core: websocket_disconnected_callback(configuration_slot, NetworkConnectionProfile) +libocpp -->> core: std::future(configure_network_connection_profile_callback(
configuration_slot, NetworkConnectionProfile)) (see 1) + + +note over csms,libocpp: Network is disconnected (websocket timeout) + +libocpp ->> libocpp: disconnect csms +libocpp ->> core: websocket_disconnected_callback(configuration_slot, NetworkConnectionProfile) +libocpp -->> core: std::future(configure_network_connection_profile_callback(
configuration_slot, NetworkConnectionProfile)) (see 1) +``` diff --git a/include/ocpp/common/websocket/websocket_libwebsockets.hpp b/include/ocpp/common/websocket/websocket_libwebsockets.hpp index 53a3f0bf0..747a0c5f6 100644 --- a/include/ocpp/common/websocket/websocket_libwebsockets.hpp +++ b/include/ocpp/common/websocket/websocket_libwebsockets.hpp @@ -21,13 +21,13 @@ struct ConnectionData; struct WebsocketMessage; /// \brief Experimental libwebsockets TLS connection -class WebsocketTlsTPM final : public WebsocketBase { +class WebsocketLibwebsockets final : public WebsocketBase { public: /// \brief Creates a new Websocket object with the providede \p connection_options - explicit WebsocketTlsTPM(const WebsocketConnectionOptions& connection_options, - std::shared_ptr evse_security); + explicit WebsocketLibwebsockets(const WebsocketConnectionOptions& connection_options, + std::shared_ptr evse_security); - ~WebsocketTlsTPM(); + ~WebsocketLibwebsockets(); void set_connection_options(const WebsocketConnectionOptions& connection_options) override; diff --git a/include/ocpp/common/websocket/websocket_plain.hpp b/include/ocpp/common/websocket/websocket_plain.hpp deleted file mode 100644 index d23618d52..000000000 --- a/include/ocpp/common/websocket/websocket_plain.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest -#ifndef OCPP_WEBSOCKET_PLAIN_HPP -#define OCPP_WEBSOCKET_PLAIN_HPP - -#include - -#include -#include - -#include - -namespace ocpp { - -typedef websocketpp::client client; - -using websocketpp::lib::bind; -using websocketpp::lib::placeholders::_1; -using websocketpp::lib::placeholders::_2; - -/// -/// \brief contains a websocket abstraction that can connect to plaintext websocket endpoints (ws://) -/// -class WebsocketPlain final : public WebsocketBase { -private: - client ws_client; - websocketpp::lib::shared_ptr websocket_thread; - - websocketpp::connection_hdl handle; - websocketpp::transport::timer_handler reconnect_callback; - - /// \brief Connect to a plain websocket - void connect_plain(); - - /// \brief Called when a plaintext websocket connection is established, calls the connected callback - void on_open_plain(client* c, websocketpp::connection_hdl hdl); - - /// \brief Called when a message is received over the plaintext websocket, calls the message callback - void on_message_plain(websocketpp::connection_hdl hdl, client::message_ptr msg); - - /// \brief Called when a plaintext websocket connection is closed - void on_close_plain(client* c, websocketpp::connection_hdl hdl); - - /// \brief Called when a plaintext websocket connection fails to be established - void on_fail_plain(client* c, websocketpp::connection_hdl hdl); - - void set_connection_options(const WebsocketConnectionOptions& connection_options) override; - -public: - /// \brief Creates a new WebsocketPlain object with the providede \p connection_options - explicit WebsocketPlain(const WebsocketConnectionOptions& connection_options); - - ~WebsocketPlain() { - this->websocket_thread->join(); - } - - /// \brief connect to a plaintext websocket - /// \returns true if the websocket is initialized and a connection attempt is made - bool connect() override; - - /// \brief Reconnects the websocket using the delay, a reason for this reconnect can be provided with the - /// \p reason parameter - void reconnect(long delay) override; - - /// \brief Closes a plaintext websocket connection - void close(const WebsocketCloseReason code, const std::string& reason) override; - - /// \brief send a \p message over the websocket - /// \returns true if the message was sent successfully - bool send(const std::string& message) override; - - /// \brief send a websocket ping - void ping() override; -}; - -} // namespace ocpp -#endif // OCPP_WEBSOCKET_PLAIN_HPP diff --git a/include/ocpp/common/websocket/websocket_tls.hpp b/include/ocpp/common/websocket/websocket_tls.hpp deleted file mode 100644 index 80855249e..000000000 --- a/include/ocpp/common/websocket/websocket_tls.hpp +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest -#ifndef OCPP_WEBSOCKET_TLS_HPP -#define OCPP_WEBSOCKET_TLS_HPP - -#include -#include - -#include -#include - -namespace ocpp { - -typedef websocketpp::client tls_client; -typedef websocketpp::lib::shared_ptr tls_context; - -using websocketpp::lib::bind; -using websocketpp::lib::placeholders::_1; -using websocketpp::lib::placeholders::_2; - -/// -/// \brief contains a websocket abstraction that can connect to TLS and non-TLS websocket endpoints -/// -class WebsocketTLS final : public WebsocketBase { -private: - tls_client wss_client; - std::shared_ptr evse_security; - websocketpp::lib::shared_ptr websocket_thread; - - websocketpp::connection_hdl handle; - websocketpp::transport::timer_handler reconnect_callback; - - /// \brief Called when a TLS websocket connection gets initialized, manages the supported TLS versions, cipher lists - /// and how verification of the server certificate is handled - tls_context on_tls_init(std::string hostname, websocketpp::connection_hdl hdl, int32_t security_profile); - - /// \brief Verify that the csms certificate's commonName matches the CSMS FQDN - bool verify_csms_cn(const std::string& hostname, bool preverified, boost::asio::ssl::verify_context& ctx); - - /// \brief Connect to a TLS websocket - void connect_tls(); - - /// \brief Called when a TLS websocket connection is established, calls the connected callback - void on_open_tls(tls_client* c, websocketpp::connection_hdl hdl); - - /// \brief Called when a message is received over the TLS websocket, calls the message callback - void on_message_tls(websocketpp::connection_hdl hdl, tls_client::message_ptr msg); - - /// \brief Called when a TLS websocket connection is closed - void on_close_tls(tls_client* c, websocketpp::connection_hdl hdl); - - /// \brief Called when a TLS websocket connection fails to be established - void on_fail_tls(tls_client* c, websocketpp::connection_hdl hdl); - - void set_connection_options(const WebsocketConnectionOptions& connection_options) override; - -public: - /// \brief Creates a new Websocket object with the providede \p connection_options - explicit WebsocketTLS(const WebsocketConnectionOptions& connection_options, - std::shared_ptr evse_security); - - ~WebsocketTLS() { - this->websocket_thread->join(); - } - - /// \brief connect to a TLS websocket - /// \returns true if the websocket is initialized and a connection attempt is made - bool connect() override; - - /// \brief Reconnects the websocket using the delay, a reason for this reconnect can be provided with the - /// \param reason parameter - /// \param delay delay of the reconnect attempt - void reconnect(long delay) override; - - /// \brief closes the websocket - void close(const WebsocketCloseReason code, const std::string& reason) override; - - /// \brief send a \p message over the websocket - /// \returns true if the message was sent successfully - bool send(const std::string& message) override; - - /// \brief send a websocket ping - void ping() override; -}; - -} // namespace ocpp -#endif // OCPP_WEBSOCKET_HPP diff --git a/include/ocpp/common/websocket/websocket_uri.hpp b/include/ocpp/common/websocket/websocket_uri.hpp index 5603899aa..171e05cd4 100644 --- a/include/ocpp/common/websocket/websocket_uri.hpp +++ b/include/ocpp/common/websocket/websocket_uri.hpp @@ -6,20 +6,11 @@ #include #include -#if LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP -#include -#else #include -#endif namespace ocpp { -#if LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP -typedef websocketpp::uri ev_uri; -#else typedef ocpp::uri ev_uri; -#endif - class Uri { public: Uri(){}; diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index 662af9348..2e5c65405 100644 --- a/include/ocpp/v201/charge_point.hpp +++ b/include/ocpp/v201/charge_point.hpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -111,6 +110,34 @@ class ChargePointInterface { /// \brief Disconnects the the websocket connection to the CSMS if it is connected virtual void disconnect_websocket() = 0; + /// + /// \brief Can be called when a network is disconnected, for example when an ethernet cable is removed. + /// + /// This is introduced because the websocket can take several minutes to timeout when a network interface becomes + /// unavailable, whereas the system can detect this sooner. + /// + /// \param configuration_slot The slot of the network connection profile that is disconnected. + /// + virtual void on_network_disconnected(int32_t configuration_slot) = 0; + + /// + /// \brief Can be called when a network is disconnected, for example when an ethernet cable is removed. + /// + /// This is introduced because the websocket can take several minutes to timeout when a network interface becomes + /// unavailable, whereas the system can detect this sooner. + /// + /// \param ocpp_interface The interface that is disconnected. + /// + virtual void on_network_disconnected(OCPPInterfaceEnum ocpp_interface) = 0; + + /// \brief Switch to a specific network connection profile given the configuration slot. + /// + /// Switch will only be done when the configuration slot has a higher priority. + /// + /// \param configuration_slot Slot in which the configuration is stored + /// \return true if the switch is possible. + virtual bool on_try_switch_network_connection_profile(const int32_t configuration_slot) = 0; + /// \brief Chargepoint notifies about new firmware update status firmware_update_status. This function should be /// called during a Firmware Update to indicate the current firmware_update_status. /// \param request_id The request_id. When it is -1, it will not be included in the request. @@ -328,6 +355,26 @@ class ChargePointInterface { /// \return vector of composite schedules, one for each evse_id including 0. virtual std::vector get_all_composite_schedules(const int32_t duration, const ChargingRateUnitEnum& unit) = 0; + + /// \brief Gets the configured NetworkConnectionProfile based on the given \p configuration_slot . The + /// central system uri of the connection options will not contain ws:// or wss:// because this method removes it if + /// present. This returns the value from the cached network connection profiles. \param + /// network_configuration_priority \return + virtual std::optional + get_network_connection_profile(const int32_t configuration_slot) = 0; + + /// \brief Get the priority of the given configuration slot. + /// \param configuration_slot The configuration slot to get the priority from. + /// \return The priority if the configuration slot exists. + /// + virtual std::optional get_configuration_slot_priority(const int configuration_slot) = 0; + + /// @brief Get the network connection priorities. + /// Each item in the vector contains the configured configuration slots, where the slot with index 0 has the highest + /// priority. + /// @return The network connection priorities + /// + virtual const std::vector& get_network_connection_priorities() const = 0; }; /// \brief Class implements OCPP2.0.1 Charging Station @@ -425,8 +472,10 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa void init_certificate_expiration_check_timers(); void scheduled_check_client_certificate_expiration(); void scheduled_check_v2g_certificate_expiration(); - void websocket_connected_callback(const int security_profile); - void websocket_disconnected_callback(); + void websocket_connected_callback(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile); + void websocket_disconnected_callback(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile); void websocket_connection_failed(ConnectionFailedReason reason); void update_dm_availability_state(const int32_t evse_id, const int32_t connector_id, const ConnectorStatusEnum status); @@ -836,6 +885,12 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa virtual void connect_websocket() override; virtual void disconnect_websocket() override; + void on_network_disconnected(int32_t configuration_slot) override; + + void on_network_disconnected(OCPPInterfaceEnum ocpp_interface) override; + + bool on_try_switch_network_connection_profile(const int32_t configuration_slot) override; + void on_firmware_update_status_notification(int32_t request_id, const FirmwareStatusEnum& firmware_update_status) override; @@ -924,6 +979,12 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa std::vector get_all_composite_schedules(const int32_t duration, const ChargingRateUnitEnum& unit) override; + std::optional get_network_connection_profile(const int32_t configuration_slot) override; + + std::optional get_configuration_slot_priority(const int configuration_slot) override; + + const std::vector& get_network_connection_priorities() const override; + /// \brief Requests a value of a VariableAttribute specified by combination of \p component_id and \p variable_id /// from the device model /// \tparam T datatype of the value that is requested diff --git a/include/ocpp/v201/charge_point_callbacks.hpp b/include/ocpp/v201/charge_point_callbacks.hpp index b25cf1b51..6a25a99bd 100644 --- a/include/ocpp/v201/charge_point_callbacks.hpp +++ b/include/ocpp/v201/charge_point_callbacks.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -84,8 +85,7 @@ struct Callbacks { std::optional> validate_network_profile_callback; - std::optional> - configure_network_connection_profile_callback; + std::optional configure_network_connection_profile_callback; std::optional> time_sync_callback; /// \brief callback to be called to congfigure ocpp message logging @@ -135,7 +135,9 @@ struct Callbacks { transaction_event_response_callback; /// \brief Callback function is called when the websocket connection status changes - std::optional> connection_state_changed_callback; + std::optional> + connection_state_changed_callback; /// \brief Callback functions called for get / set / clear display messages std::optional(const GetDisplayMessagesRequest& request)>> diff --git a/include/ocpp/v201/connectivity_manager.hpp b/include/ocpp/v201/connectivity_manager.hpp index 3b2c23ec1..e72360209 100644 --- a/include/ocpp/v201/connectivity_manager.hpp +++ b/include/ocpp/v201/connectivity_manager.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -14,11 +15,11 @@ namespace v201 { class DeviceModel; -using WebsocketConnectedCallback = std::function; -using WebsocketDisconnectedCallback = std::function; +using WebsocketConnectionCallback = + std::function; using WebsocketConnectionFailedCallback = std::function; -using ConfigureNetworkConnectionProfileCallback = - std::function; +using ConfigureNetworkConnectionProfileCallback = std::function( + const int32_t configuration_slot, const NetworkConnectionProfile& network_connection_profile)>; class ConnectivityManager { private: @@ -33,9 +34,9 @@ class ConnectivityManager { /// \brief The message callback std::function message_callback; /// \brief Callback that is called when the websocket is connected successfully - std::optional websocket_connected_callback; + std::optional websocket_connected_callback; /// \brief Callback that is called when the websocket connection is disconnected - std::optional websocket_disconnected_callback; + std::optional websocket_disconnected_callback; /// \brief Callback that is called when the websocket could not connect with a specific reason std::optional websocket_connection_failed_callback; /// \brief Callback that is called to configure a network connection profile when none is configured @@ -46,8 +47,8 @@ class ConnectivityManager { int network_configuration_priority; /// @brief Local cached network connection profiles std::vector network_connection_profiles; - /// @brief local cached network conenction priorities - std::vector network_connection_priorities; + /// @brief local cached network connection priorities + std::vector network_connection_priorities; WebsocketConnectionOptions current_connection_options{}; public: @@ -69,11 +70,11 @@ class ConnectivityManager { /// \brief Set the \p callback that is called when the websocket is connected. /// - void set_websocket_connected_callback(WebsocketConnectedCallback callback); + void set_websocket_connected_callback(WebsocketConnectionCallback callback); /// \brief Set the \p callback that is called when the websocket is disconnected. /// - void set_websocket_disconnected_callback(WebsocketDisconnectedCallback callback); + void set_websocket_disconnected_callback(WebsocketConnectionCallback callback); /// \brief Set the \p callback that is called when the websocket could not connect with a specific reason /// @@ -84,11 +85,24 @@ class ConnectivityManager { void set_configure_network_connection_profile_callback(ConfigureNetworkConnectionProfileCallback callback); /// \brief Gets the configured NetworkConnectionProfile based on the given \p configuration_slot . The - /// central system uri ofthe connection options will not contain ws:// or wss:// because this method removes it if + /// central system uri of the connection options will not contain ws:// or wss:// because this method removes it if /// present. This returns the value from the cached network connection profiles. \param /// network_configuration_priority \return std::optional get_network_connection_profile(const int32_t configuration_slot); + /// \brief Get the priority of the given configuration slot. + /// \param configuration_slot The configuration slot to get the priority from. + /// \return The priority if the configuration slot exists. + /// + std::optional get_configuration_slot_priority(const int configuration_slot); + + /// @brief Get the network connection priorities. + /// Each item in the vector contains the configured configuration slots, where the slot with index 0 has the highest + /// priority. + /// @return The network connection priorities + /// + const std::vector& get_network_connection_priorities() const; + /// \brief Check if the websocket is connected /// \return True is the websocket is connected, else false /// @@ -115,6 +129,34 @@ class ConnectivityManager { /// bool send_to_websocket(const std::string& message); + /// + /// \brief Can be called when a network is disconnected, for example when an ethernet cable is removed. + /// + /// This is introduced because the websocket can take several minutes to timeout when a network interface becomes + /// unavailable, whereas the system can detect this sooner. + /// + /// \param configuration_slot The slot of the network connection profile that is disconnected. + /// + void on_network_disconnected(int32_t configuration_slot); + + /// + /// \brief Can be called when a network is disconnected, for example when an ethernet cable is removed. + /// + /// This is introduced because the websocket can take several minutes to timeout when a network interface becomes + /// unavailable, whereas the system can detect this sooner. + /// + /// \param ocpp_interface The interface that is disconnected. + /// + void on_network_disconnected(OCPPInterfaceEnum ocpp_interface); + + /// \brief Switch to a specific network connection profile given the configuration slot. + /// + /// Switch will only be done when the configuration slot has a higher priority. + /// + /// \param configuration_slot Slot in which the configuration is stored + /// \return true if the switch is possible. + bool on_try_switch_network_connection_profile(const int32_t configuration_slot); + private: /// \brief Init the websocket /// @@ -125,6 +167,36 @@ class ConnectivityManager { /// WebsocketConnectionOptions get_ws_connection_options(const int32_t configuration_slot); + /// \brief Function invoked when the web socket connected with the \p security_profile + /// + void on_websocket_connected(const int security_profile); + + /// \brief Function invoked when the web socket disconnected + /// + void on_websocket_disconnected(); + + /// \brief Function invoked when the web socket closes + /// + void on_websocket_closed(ocpp::WebsocketCloseReason reason); + + /// \brief Reconnect with the give websocket \p reason + /// + void reconnect(WebsocketCloseReason reason, std::optional next_priority = std::nullopt); + + /// + /// \brief Returns true if the provided configuration slot is of higher priority compared to the one currently + /// in use. + /// \param new_configuration_slot The configuration slot to check. + /// \return True when given slot is of higher priority. + /// + bool is_higher_priority_profile(const int new_configuration_slot); + + /// + /// \brief Get the active network configuration slot in use. + /// \return The active slot the network is connected to or the pending slot. + /// + int get_active_network_configuration_slot(); + /// \brief Moves websocket network_configuration_priority to next profile /// void next_network_configuration_priority(); diff --git a/include/ocpp/v201/ctrlr_component_variables.hpp b/include/ocpp/v201/ctrlr_component_variables.hpp index 485bcf84b..8099f5aa7 100644 --- a/include/ocpp/v201/ctrlr_component_variables.hpp +++ b/include/ocpp/v201/ctrlr_component_variables.hpp @@ -64,6 +64,7 @@ extern const ComponentVariable& LogRotationMaximumFileCount; extern const ComponentVariable& SupportedChargingProfilePurposeTypes; extern const ComponentVariable& SupportedCriteria; extern const ComponentVariable& RoundClockAlignedTimestamps; +extern const ComponentVariable& NetworkConfigTimeout; extern const ComponentVariable& MaxCompositeScheduleDuration; extern const RequiredComponentVariable& NumberOfConnectors; extern const ComponentVariable& UseSslDefaultVerifyPaths; diff --git a/include/ocpp/v201/ocpp_types.hpp b/include/ocpp/v201/ocpp_types.hpp index 38ee2399b..5c76097e8 100644 --- a/include/ocpp/v201/ocpp_types.hpp +++ b/include/ocpp/v201/ocpp_types.hpp @@ -889,6 +889,13 @@ void from_json(const json& j, SetMonitoringResult& k); /// \returns an output stream with the SetMonitoringResult written to std::ostream& operator<<(std::ostream& os, const SetMonitoringResult& k); +/// @brief The result of a configuration of a network profile. +struct ConfigNetworkResult { + uint8_t network_profile_slot; ///< @brief Network profile slot. + std::optional interface_address; ///< ip address or interface string + bool success; ///< true if the configuration was successful +}; + struct SetVariableData { CiString<1000> attributeValue; Component component; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index c59c90513..246a53f4f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,7 @@ target_compile_options(ocpp PRIVATE #-Werror # turn warnings into errors -Wimplicit-fallthrough # avoid unintended fallthroughs + -pedantic-errors ) target_compile_definitions(ocpp @@ -158,18 +159,6 @@ target_link_libraries(ocpp date::date-tz ) -if(LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP) - target_link_libraries(ocpp - PUBLIC - websocketpp::websocketpp - ) - - target_compile_definitions(ocpp - PRIVATE - LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP - ) -endif() - if(LIBOCPP_USE_BOOST_FILESYSTEM) find_package(Boost REQUIRED COMPONENTS filesystem) target_link_libraries(ocpp diff --git a/lib/ocpp/common/websocket/CMakeLists.txt b/lib/ocpp/common/websocket/CMakeLists.txt index 4cd1e0f7e..8805c9d58 100644 --- a/lib/ocpp/common/websocket/CMakeLists.txt +++ b/lib/ocpp/common/websocket/CMakeLists.txt @@ -6,11 +6,3 @@ target_sources(ocpp websocket.cpp websocket_libwebsockets.cpp ) - -if(LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP) - target_sources(ocpp - PRIVATE - websocket_plain.cpp - websocket_tls.cpp - ) -endif() diff --git a/lib/ocpp/common/websocket/websocket.cpp b/lib/ocpp/common/websocket/websocket.cpp index 9feb843d7..f7e2689c1 100644 --- a/lib/ocpp/common/websocket/websocket.cpp +++ b/lib/ocpp/common/websocket/websocket.cpp @@ -5,12 +5,7 @@ #include #include -#ifdef LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP -#include -#include -#else #include -#endif #include @@ -21,16 +16,7 @@ namespace ocpp { Websocket::Websocket(const WebsocketConnectionOptions& connection_options, std::shared_ptr evse_security, std::shared_ptr logging) : logging(logging) { - -#ifdef LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP - if (connection_options.security_profile <= 1) { - this->websocket = std::make_unique(connection_options); - } else if (connection_options.security_profile >= 2) { - this->websocket = std::make_unique(connection_options, evse_security); - } -#else - this->websocket = std::make_unique(connection_options, evse_security); -#endif + this->websocket = std::make_unique(connection_options, evse_security); } Websocket::~Websocket() { diff --git a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp index b032685a3..33f32a4c1 100644 --- a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp +++ b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp @@ -121,11 +121,11 @@ struct ConnectionData { return wsi; } - WebsocketTlsTPM* get_owner() { + WebsocketLibwebsockets* get_owner() { return owner.load(); } - void set_owner(WebsocketTlsTPM* o) { + void set_owner(WebsocketLibwebsockets* o) { owner = o; } @@ -139,7 +139,7 @@ struct ConnectionData { lws* wsi; private: - std::atomic owner; + std::atomic owner; std::thread::id lws_thread_id; @@ -231,16 +231,16 @@ static bool verify_csms_cn(const std::string& hostname, bool preverified, const return preverified; } -WebsocketTlsTPM::WebsocketTlsTPM(const WebsocketConnectionOptions& connection_options, - std::shared_ptr evse_security) : +WebsocketLibwebsockets::WebsocketLibwebsockets(const WebsocketConnectionOptions& connection_options, + std::shared_ptr evse_security) : WebsocketBase(), evse_security(evse_security), stop_deferred_handler(false) { set_connection_options(connection_options); - EVLOG_debug << "Initialised WebsocketTlsTPM with URI: " << this->connection_options.csms_uri.string(); + EVLOG_debug << "Initialised WebsocketLibwebsockets with URI: " << this->connection_options.csms_uri.string(); } -WebsocketTlsTPM::~WebsocketTlsTPM() { +WebsocketLibwebsockets::~WebsocketLibwebsockets() { std::shared_ptr local_data = conn_data; if (local_data != nullptr) { local_data->do_interrupt(); @@ -264,7 +264,7 @@ WebsocketTlsTPM::~WebsocketTlsTPM() { } } -void WebsocketTlsTPM::set_connection_options(const WebsocketConnectionOptions& connection_options) { +void WebsocketLibwebsockets::set_connection_options(const WebsocketConnectionOptions& connection_options) { switch (connection_options.security_profile) { // `switch` used to lint on missing enum-values case security::SecurityProfile::OCPP_1_6_ONLY_UNSECURED_TRANSPORT_WITHOUT_BASIC_AUTHENTICATION: case security::SecurityProfile::UNSECURED_TRANSPORT_WITH_BASIC_AUTHENTICATION: @@ -317,8 +317,8 @@ constexpr auto local_protocol_name = "lws-everest-client"; static const struct lws_protocols protocols[] = {{local_protocol_name, callback_minimal, 0, 0, 0, NULL, 0}, LWS_PROTOCOL_LIST_TERM}; -bool WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, const std::string& path_key, - bool custom_key, std::optional& password) { +bool WebsocketLibwebsockets::tls_init(SSL_CTX* ctx, const std::string& path_chain, const std::string& path_key, + bool custom_key, std::optional& password) { auto rc = SSL_CTX_set_cipher_list(ctx, this->connection_options.supported_ciphers_12.c_str()); if (rc != 1) { EVLOG_debug << "SSL_CTX_set_cipher_list return value: " << rc; @@ -428,7 +428,7 @@ bool WebsocketTlsTPM::tls_init(SSL_CTX* ctx, const std::string& path_chain, cons return true; } -void WebsocketTlsTPM::recv_loop() { +void WebsocketLibwebsockets::recv_loop() { std::shared_ptr local_data = conn_data; if (local_data == nullptr) { @@ -468,7 +468,7 @@ void WebsocketTlsTPM::recv_loop() { EVLOG_debug << "Exit recv loop with ID: " << std::hex << std::this_thread::get_id(); } -void WebsocketTlsTPM::client_loop() { +void WebsocketLibwebsockets::client_loop() { std::shared_ptr local_data = conn_data; if (local_data == nullptr) { @@ -691,7 +691,7 @@ void WebsocketTlsTPM::client_loop() { } // Will be called from external threads as well -bool WebsocketTlsTPM::connect() { +bool WebsocketLibwebsockets::connect() { if (!this->initialized()) { return false; } @@ -728,7 +728,7 @@ bool WebsocketTlsTPM::connect() { if (this->deferred_callback_thread == nullptr) { this->deferred_callback_thread = - std::make_unique(&WebsocketTlsTPM::handle_deferred_callback_queue, this); + std::make_unique(&WebsocketLibwebsockets::handle_deferred_callback_queue, this); } // Stop any pending reconnect timer @@ -767,7 +767,7 @@ bool WebsocketTlsTPM::connect() { std::unique_lock lock(connection_mutex); // Release other threads - this->websocket_thread.reset(new std::thread(&WebsocketTlsTPM::client_loop, this)); + this->websocket_thread.reset(new std::thread(&WebsocketLibwebsockets::client_loop, this)); // TODO(ioan): remove this thread when the fix will be moved into 'MessageQueue' // The reason for having a received message processing thread is that because @@ -775,7 +775,7 @@ bool WebsocketTlsTPM::connect() { // will send back another message, and since we're waiting for that message to be // sent over the wire on the client_loop, not giving the opportunity to the loop to // advance we will have a dead-lock - this->recv_message_thread.reset(new std::thread(&WebsocketTlsTPM::recv_loop, this)); + this->recv_message_thread.reset(new std::thread(&WebsocketLibwebsockets::recv_loop, this)); // Wait until connect or timeout timeouted = !conn_cv.wait_for(lock, std::chrono::seconds(60), [&]() { @@ -810,7 +810,7 @@ bool WebsocketTlsTPM::connect() { return (connected); } -void WebsocketTlsTPM::reconnect(long delay) { +void WebsocketLibwebsockets::reconnect(long delay) { if (this->shutting_down) { EVLOG_info << "Not reconnecting because the websocket is being shutdown."; return; @@ -837,7 +837,7 @@ void WebsocketTlsTPM::reconnect(long delay) { } } -void WebsocketTlsTPM::close(const WebsocketCloseReason code, const std::string& reason) { +void WebsocketLibwebsockets::close(const WebsocketCloseReason code, const std::string& reason) { EVLOG_info << "Closing websocket: " << reason; { @@ -877,7 +877,7 @@ void WebsocketTlsTPM::close(const WebsocketCloseReason code, const std::string& }); } -void WebsocketTlsTPM::on_conn_connected() { +void WebsocketLibwebsockets::on_conn_connected() { EVLOG_info << "OCPP client successfully connected to server"; this->connection_attempts = 1; // reset connection attempts @@ -896,7 +896,7 @@ void WebsocketTlsTPM::on_conn_connected() { }); } -void WebsocketTlsTPM::on_conn_close() { +void WebsocketLibwebsockets::on_conn_close() { EVLOG_info << "OCPP client closed connection to server"; std::lock_guard lk(this->connection_mutex); @@ -924,7 +924,7 @@ void WebsocketTlsTPM::on_conn_close() { }); } -void WebsocketTlsTPM::on_conn_fail() { +void WebsocketLibwebsockets::on_conn_fail() { EVLOG_error << "OCPP client connection to server failed"; std::lock_guard lk(this->connection_mutex); @@ -953,7 +953,7 @@ void WebsocketTlsTPM::on_conn_fail() { } } -void WebsocketTlsTPM::on_message(std::string&& message) { +void WebsocketLibwebsockets::on_message(std::string&& message) { if (!this->initialized()) { EVLOG_error << "Message received but TLS websocket has not been correctly initialized. Discarding message."; return; @@ -1017,7 +1017,7 @@ static bool send_internal(lws* wsi, WebsocketMessage* msg) { return true; } -void WebsocketTlsTPM::on_writable() { +void WebsocketLibwebsockets::on_writable() { if (!this->initialized() || !this->m_is_connected) { EVLOG_error << "Message sending but TLS websocket has not been correctly initialized/connected."; return; @@ -1113,7 +1113,7 @@ void WebsocketTlsTPM::on_writable() { } } -void WebsocketTlsTPM::request_write() { +void WebsocketLibwebsockets::request_write() { std::shared_ptr local_data = conn_data; if (this->m_is_connected) { if (local_data != nullptr) { @@ -1128,7 +1128,7 @@ void WebsocketTlsTPM::request_write() { } } -void WebsocketTlsTPM::poll_message(const std::shared_ptr& msg) { +void WebsocketLibwebsockets::poll_message(const std::shared_ptr& msg) { if (this->m_is_connected == false) { EVLOG_debug << "Trying to poll message without being connected!"; return; @@ -1171,7 +1171,7 @@ void WebsocketTlsTPM::poll_message(const std::shared_ptr& msg) } // Will be called from external threads -bool WebsocketTlsTPM::send(const std::string& message) { +bool WebsocketLibwebsockets::send(const std::string& message) { if (!this->initialized()) { EVLOG_error << "Could not send message because websocket is not properly initialized."; return false; @@ -1186,7 +1186,7 @@ bool WebsocketTlsTPM::send(const std::string& message) { return msg->message_sent; } -void WebsocketTlsTPM::ping() { +void WebsocketLibwebsockets::ping() { if (!this->initialized()) { EVLOG_error << "Could not send ping because websocket is not properly initialized."; } @@ -1198,12 +1198,12 @@ void WebsocketTlsTPM::ping() { poll_message(msg); } -int WebsocketTlsTPM::process_callback(void* wsi_ptr, int callback_reason, void* user, void* in, size_t len) { +int WebsocketLibwebsockets::process_callback(void* wsi_ptr, int callback_reason, void* user, void* in, size_t len) { enum lws_callback_reasons reason = static_cast(callback_reason); lws* wsi = reinterpret_cast(wsi_ptr); - // The ConnectionData is thread bound, so that if we clear it in the 'WebsocketTlsTPM' + // The ConnectionData is thread bound, so that if we clear it in the 'WebsocketLibwebsockets' // we still have a chance to close the connection here ConnectionData* data = reinterpret_cast(lws_wsi_user(wsi)); @@ -1439,7 +1439,7 @@ int WebsocketTlsTPM::process_callback(void* wsi_ptr, int callback_reason, void* return 0; } -void WebsocketTlsTPM::push_deferred_callback(const std::function& callback) { +void WebsocketLibwebsockets::push_deferred_callback(const std::function& callback) { if (!callback) { EVLOG_error << "Attempting to push stale callback in deferred queue!"; return; @@ -1450,7 +1450,7 @@ void WebsocketTlsTPM::push_deferred_callback(const std::function& callba this->deferred_callback_cv.notify_one(); } -void WebsocketTlsTPM::handle_deferred_callback_queue() { +void WebsocketLibwebsockets::handle_deferred_callback_queue() { while (true) { std::function callback; { diff --git a/lib/ocpp/common/websocket/websocket_plain.cpp b/lib/ocpp/common/websocket/websocket_plain.cpp deleted file mode 100644 index fc9fde003..000000000 --- a/lib/ocpp/common/websocket/websocket_plain.cpp +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest -#include -#include - -#include - -#include - -#include -#include - -namespace ocpp { - -websocketpp::close::status::value close_reason_to_value(WebsocketCloseReason reason) { - switch (reason) { - case WebsocketCloseReason::Normal: - return websocketpp::close::status::normal; - case WebsocketCloseReason::ForceTcpDrop: - return websocketpp::close::status::force_tcp_drop; - case WebsocketCloseReason::GoingAway: - return websocketpp::close::status::going_away; - case WebsocketCloseReason::AbnormalClose: - return websocketpp::close::status::abnormal_close; - case WebsocketCloseReason::ServiceRestart: - return websocketpp::close::status::service_restart; - } - - throw EnumToStringException{e, "WebsocketCloseReason"}; -} - -WebsocketCloseReason value_to_close_reason(websocketpp::close::status::value value) { - switch (value) { - case websocketpp::close::status::normal: - return WebsocketCloseReason::Normal; - case websocketpp::close::status::force_tcp_drop: - return WebsocketCloseReason::ForceTcpDrop; - case websocketpp::close::status::going_away: - return WebsocketCloseReason::GoingAway; - case websocketpp::close::status::abnormal_close: - return WebsocketCloseReason::AbnormalClose; - case websocketpp::close::status::service_restart: - return WebsocketCloseReason::ServiceRestart; - } - - throw std::out_of_range("No known conversion for provided enum of type websocketpp::close::status::value"); -} - -WebsocketPlain::WebsocketPlain(const WebsocketConnectionOptions& connection_options) : WebsocketBase() { - set_connection_options(connection_options); - - EVLOG_debug << "Initialised WebsocketPlain with URI: " << this->connection_options.csms_uri.string(); -} - -void WebsocketPlain::set_connection_options(const WebsocketConnectionOptions& connection_options) { - switch (connection_options.security_profile) { // `switch` used to lint on missing enum-values - case security::SecurityProfile::OCPP_1_6_ONLY_UNSECURED_TRANSPORT_WITHOUT_BASIC_AUTHENTICATION: - case security::SecurityProfile::UNSECURED_TRANSPORT_WITH_BASIC_AUTHENTICATION: - break; - case security::SecurityProfile::TLS_WITH_BASIC_AUTHENTICATION: - case security::SecurityProfile::TLS_WITH_CLIENT_SIDE_CERTIFICATES: - throw std::invalid_argument("`security_profile` is not a plain, unsecured one."); - default: - throw std::invalid_argument("unknown `security_profile`, value = " + - std::to_string(connection_options.security_profile)); - } - - set_connection_options_base(connection_options); - this->connection_options.csms_uri.set_secure(false); -} - -bool WebsocketPlain::connect() { - if (!this->initialized()) { - return false; - } - - EVLOG_info << "Connecting to plain websocket at uri: " << this->connection_options.csms_uri.string() - << " with security profile: " << this->connection_options.security_profile; - - this->ws_client.clear_access_channels(websocketpp::log::alevel::all); - this->ws_client.clear_error_channels(websocketpp::log::elevel::all); - this->ws_client.init_asio(); - this->ws_client.start_perpetual(); - - websocket_thread.reset(new websocketpp::lib::thread(&client::run, &this->ws_client)); - - this->reconnect_callback = [this](const websocketpp::lib::error_code& ec) { - if (!this->shutting_down) { - EVLOG_info << "Reconnecting to plain websocket at uri: " << this->connection_options.csms_uri.string() - << " with security profile: " << this->connection_options.security_profile; - - // close connection before reconnecting - if (this->m_is_connected) { - try { - EVLOG_info << "Closing websocket connection before reconnecting"; - this->ws_client.close(this->handle, websocketpp::close::status::normal, ""); - } catch (std::exception& e) { - EVLOG_error << "Error on plain close: " << e.what(); - } - } - - this->cancel_reconnect_timer(); - this->connect_plain(); - } - }; - - this->connect_plain(); - return true; -} - -bool WebsocketPlain::send(const std::string& message) { - if (!this->initialized()) { - EVLOG_error << "Could not send message because websocket is not properly initialized."; - return false; - } - - websocketpp::lib::error_code ec; - - this->ws_client.send(this->handle, message, websocketpp::frame::opcode::text, ec); - if (ec) { - EVLOG_error << "Error sending message over plain websocket: " << ec.message(); - - this->reconnect(this->get_reconnect_interval()); - EVLOG_info << "(plain) Called reconnect()"; - return false; - } - - EVLOG_debug << "Sent message over plain websocket: " << message; - - return true; -} - -void WebsocketPlain::reconnect(long delay) { - if (this->shutting_down) { - EVLOG_info << "Not reconnecting because the websocket is being shutdown."; - return; - } - - // TODO(kai): notify message queue that connection is down and a reconnect is imminent? - { - std::lock_guard lk(this->reconnect_mutex); - - if (this->m_is_connected) { - try { - EVLOG_info << "Closing websocket connection before reconnecting"; - this->ws_client.close(this->handle, websocketpp::close::status::normal, ""); - } catch (std::exception& e) { - EVLOG_error << "Error on plain close: " << e.what(); - } - } - - if (!this->reconnect_timer) { - EVLOG_info << "Reconnecting in: " << delay << "ms" - << ", attempt: " << this->connection_attempts; - this->reconnect_timer = this->ws_client.set_timer(delay, this->reconnect_callback); - } else { - EVLOG_info << "Reconnect timer already running"; - } - } - - // TODO: spec-conform reconnect, refer to status codes from: - // https://github.com/zaphoyd/websocketpp/blob/master/websocketpp/close.hpp -} - -void WebsocketPlain::connect_plain() { - - websocketpp::lib::error_code ec; - - const client::connection_ptr con = this->ws_client.get_connection( - std::make_shared(this->connection_options.csms_uri.get_websocketpp_uri()), ec); - - if (ec) { - EVLOG_error << "Connection initialization error for plain websocket: " << ec.message(); - } - - if (this->connection_options.hostName.has_value()) { - EVLOG_info << "User-Host is set to " << this->connection_options.hostName.value(); - con->append_header("User-Host", this->connection_options.hostName.value()); - } - - if (this->connection_options.security_profile == 0) { - EVLOG_debug << "Connecting with security profile: 0"; - } else if (this->connection_options.security_profile == 1) { - EVLOG_debug << "Connecting with security profile: 1"; - std::optional authorization_header = this->getAuthorizationHeader(); - if (authorization_header) { - con->append_header("Authorization", authorization_header.value()); - } else { - throw std::runtime_error("No authorization key provided when connecting with security profile: 1"); - } - } else { - throw std::runtime_error("Cannot connect with plain websocket with security profile > 1"); - } - - this->handle = con->get_handle(); - - con->set_open_handler(websocketpp::lib::bind(&WebsocketPlain::on_open_plain, this, &this->ws_client, - websocketpp::lib::placeholders::_1)); - con->set_fail_handler(websocketpp::lib::bind(&WebsocketPlain::on_fail_plain, this, &this->ws_client, - websocketpp::lib::placeholders::_1)); - con->set_close_handler(websocketpp::lib::bind(&WebsocketPlain::on_close_plain, this, &this->ws_client, - websocketpp::lib::placeholders::_1)); - con->set_message_handler(websocketpp::lib::bind(&WebsocketPlain::on_message_plain, this, - websocketpp::lib::placeholders::_1, - websocketpp::lib::placeholders::_2)); - con->set_pong_timeout(this->connection_options.pong_timeout_s * 1000); // pong timeout in ms - con->set_pong_timeout_handler( - websocketpp::lib::bind(&WebsocketPlain::on_pong_timeout, this, websocketpp::lib::placeholders::_2)); - - con->add_subprotocol(conversions::ocpp_protocol_version_to_string(this->connection_options.ocpp_version)); - std::lock_guard lk(this->connection_mutex); - this->ws_client.connect(con); -} - -void WebsocketPlain::on_open_plain(client* c, websocketpp::connection_hdl hdl) { - std::lock_guard lk(this->connection_mutex); - (void)c; // client is not used in this function - EVLOG_info << "OCPP client successfully connected to plain websocket server"; - this->connection_attempts = 1; // reset connection attempts - this->m_is_connected = true; - this->reconnecting = false; - this->set_websocket_ping_interval(this->connection_options.ping_interval_s); - this->connected_callback(this->connection_options.security_profile); -} - -void WebsocketPlain::on_message_plain(websocketpp::connection_hdl hdl, client::message_ptr msg) { - if (!this->initialized()) { - EVLOG_error << "Message received but plain websocket has not been correctly initialized. Discarding message."; - return; - } - try { - auto message = msg->get_payload(); - this->message_callback(message); - } catch (websocketpp::exception const& e) { - EVLOG_error << "Plain websocket exception on receiving message: " << e.what(); - } -} - -void WebsocketPlain::on_close_plain(client* c, websocketpp::connection_hdl hdl) { - std::lock_guard lk(this->connection_mutex); - this->m_is_connected = false; - this->disconnected_callback(); - this->cancel_reconnect_timer(); - client::connection_ptr con = c->get_con_from_hdl(hdl); - auto error_code = con->get_ec(); - EVLOG_info << "Closed plain websocket connection with code: " << error_code << " (" - << websocketpp::close::status::get_string(con->get_remote_close_code()) - << "), reason: " << con->get_remote_close_reason(); - // dont reconnect on normal code - if (con->get_remote_close_code() != websocketpp::close::status::normal) { - this->reconnect(this->get_reconnect_interval()); - } else { - this->closed_callback(value_to_close_reason(con->get_remote_close_code())); - } -} - -void WebsocketPlain::on_fail_plain(client* c, websocketpp::connection_hdl hdl) { - std::lock_guard lk(this->connection_mutex); - if (this->m_is_connected) { - this->disconnected_callback(); - } - this->m_is_connected = false; - this->connection_attempts += 1; - client::connection_ptr con = c->get_con_from_hdl(hdl); - const auto ec = con->get_ec(); - this->log_on_fail(ec, con->get_transport_ec(), con->get_response_code()); - - // -1 indicates to always attempt to reconnect - if (this->connection_options.max_connection_attempts == -1 or - this->connection_attempts <= this->connection_options.max_connection_attempts) { - this->reconnect(this->get_reconnect_interval()); - } else { - this->close(WebsocketCloseReason::Normal, "Connection failed"); - } -} - -void WebsocketPlain::close(const WebsocketCloseReason code, const std::string& reason) { - EVLOG_info << "Closing plain websocket."; - websocketpp::lib::error_code ec; - this->cancel_reconnect_timer(); - - this->ws_client.stop_perpetual(); - this->ws_client.close(this->handle, close_reason_to_value(code), reason, ec); - if (ec) { - EVLOG_error << "Error initiating close of plain websocket: " << ec.message(); - // on_close_plain won't be called here so we have to call the closed_callback manually - this->closed_callback(WebsocketCloseReason::AbnormalClose); - } else { - EVLOG_info << "Closed plain websocket successfully."; - } -} - -void WebsocketPlain::ping() { - if (this->m_is_connected) { - auto con = this->ws_client.get_con_from_hdl(this->handle); - websocketpp::lib::error_code error_code; - con->ping(this->connection_options.ping_payload, error_code); - } -} - -} // namespace ocpp diff --git a/lib/ocpp/common/websocket/websocket_tls.cpp b/lib/ocpp/common/websocket/websocket_tls.cpp deleted file mode 100644 index cfd5023d4..000000000 --- a/lib/ocpp/common/websocket/websocket_tls.cpp +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -namespace ocpp { - -extern websocketpp::close::status::value close_reason_to_value(WebsocketCloseReason reason); -extern WebsocketCloseReason value_to_close_reason(websocketpp::close::status::value value); - -WebsocketTLS::WebsocketTLS(const WebsocketConnectionOptions& connection_options, - std::shared_ptr evse_security) : - WebsocketBase(), evse_security(evse_security) { - - set_connection_options(connection_options); - - EVLOG_debug << "Initialised WebsocketTLS with URI: " << this->connection_options.csms_uri.string(); -} - -void WebsocketTLS::set_connection_options(const WebsocketConnectionOptions& connection_options) { - switch (connection_options.security_profile) { // `switch` used to lint on missing enum-values - case security::SecurityProfile::OCPP_1_6_ONLY_UNSECURED_TRANSPORT_WITHOUT_BASIC_AUTHENTICATION: - case security::SecurityProfile::UNSECURED_TRANSPORT_WITH_BASIC_AUTHENTICATION: - throw std::invalid_argument("`security_profile` is not a TLS-profile"); - case security::SecurityProfile::TLS_WITH_BASIC_AUTHENTICATION: - case security::SecurityProfile::TLS_WITH_CLIENT_SIDE_CERTIFICATES: - break; - default: - throw std::invalid_argument("unknown `security_profile`, value = " + - std::to_string(connection_options.security_profile)); - } - - set_connection_options_base(connection_options); - - this->connection_options.csms_uri.set_secure(true); -} - -bool WebsocketTLS::connect() { - if (!this->initialized()) { - return false; - } - - EVLOG_info << "Connecting TLS websocket to uri: " << this->connection_options.csms_uri.string() - << " with security-profile " << this->connection_options.security_profile; - - this->wss_client.clear_access_channels(websocketpp::log::alevel::all); - this->wss_client.clear_error_channels(websocketpp::log::elevel::all); - this->wss_client.init_asio(); - this->wss_client.start_perpetual(); - websocket_thread.reset(new websocketpp::lib::thread(&tls_client::run, &this->wss_client)); - - this->wss_client.set_tls_init_handler( - websocketpp::lib::bind(&WebsocketTLS::on_tls_init, this, this->connection_options.csms_uri.get_hostname(), - websocketpp::lib::placeholders::_1, this->connection_options.security_profile)); - - this->reconnect_callback = [this](const websocketpp::lib::error_code& ec) { - if (!this->shutting_down) { - EVLOG_info << "Reconnecting to TLS websocket at uri: " << this->connection_options.csms_uri.string() - << " with security profile: " << this->connection_options.security_profile; - - // close connection before reconnecting - if (this->m_is_connected) { - try { - EVLOG_info << "Closing websocket connection before reconnecting"; - this->wss_client.close(this->handle, websocketpp::close::status::normal, ""); - } catch (std::exception& e) { - EVLOG_error << "Error on TLS close: " << e.what(); - } - } - - this->cancel_reconnect_timer(); - this->connect_tls(); - } - }; - - this->connect_tls(); - return true; -} - -bool WebsocketTLS::send(const std::string& message) { - if (!this->initialized()) { - EVLOG_error << "Could not send message because websocket is not properly initialized."; - return false; - } - - websocketpp::lib::error_code ec; - - this->wss_client.send(this->handle, message, websocketpp::frame::opcode::text, ec); - if (ec) { - EVLOG_error << "Error sending message over TLS websocket: " << ec.message(); - - this->reconnect(this->get_reconnect_interval()); - EVLOG_info << "(TLS) Called reconnect()"; - return false; - } - - EVLOG_debug << "Sent message over TLS websocket: " << message; - - return true; -} - -void WebsocketTLS::reconnect(long delay) { - if (this->shutting_down) { - EVLOG_info << "Not reconnecting because the websocket is being shutdown."; - return; - } - - // TODO(kai): notify message queue that connection is down and a reconnect is imminent? - { - std::lock_guard lk(this->reconnect_mutex); - if (this->m_is_connected) { - try { - EVLOG_info << "Closing websocket connection before reconnecting"; - this->wss_client.close(this->handle, websocketpp::close::status::normal, ""); - } catch (std::exception& e) { - EVLOG_error << "Error on plain close: " << e.what(); - } - } - - if (!this->reconnect_timer) { - EVLOG_info << "Reconnecting in: " << delay << "ms" - << ", attempt: " << this->connection_attempts; - this->reconnect_timer = this->wss_client.set_timer(delay, this->reconnect_callback); - } else { - EVLOG_info << "Reconnect timer already running"; - } - } - - // TODO: spec-conform reconnect, refer to status codes from: - // https://github.com/zaphoyd/websocketpp/blob/master/websocketpp/close.hpp -} - -tls_context WebsocketTLS::on_tls_init(std::string hostname, websocketpp::connection_hdl hdl, int32_t security_profile) { - tls_context context = websocketpp::lib::make_shared(boost::asio::ssl::context::sslv23); - - try { - // FIXME(kai): choose reasonable defaults, they can probably be stricter than this set of options! - // it is recommended to only accept TLSv1.2+ - context->set_options(boost::asio::ssl::context::default_workarounds | // - boost::asio::ssl::context::no_sslv2 | // - boost::asio::ssl::context::no_sslv3 | // - boost::asio::ssl::context::no_tlsv1 | // - boost::asio::ssl::context::no_tlsv1_1 | boost::asio::ssl::context::no_compression | // - boost::asio::ssl::context::single_dh_use); - - EVLOG_debug << "List of ciphers that will be accepted by this TLS connection: " - << this->connection_options.supported_ciphers_12 << ":" - << this->connection_options.supported_ciphers_13; - - auto rc = - SSL_CTX_set_cipher_list(context->native_handle(), this->connection_options.supported_ciphers_12.c_str()); - if (rc != 1) { - EVLOG_debug << "SSL_CTX_set_cipher_list return value: " << rc; - EVLOG_AND_THROW(std::runtime_error("Could not set TLSv1.2 cipher list")); - } - - rc = SSL_CTX_set_ciphersuites(context->native_handle(), this->connection_options.supported_ciphers_13.c_str()); - if (rc != 1) { - EVLOG_debug << "SSL_CTX_set_cipher_list return value: " << rc; - } - - if (security_profile == 3) { - const auto certificate_result = - this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - - if (certificate_result.status != GetCertificateInfoStatus::Accepted || - !certificate_result.info.has_value()) { - EVLOG_AND_THROW(std::runtime_error( - "Connecting with security profile 3 but no client side certificate is present or valid")); - } - - const auto& certificate_info = certificate_result.info.value(); - - if (certificate_info.password.has_value()) { - std::string passwd = certificate_info.password.value(); - context->set_password_callback( - [passwd](auto max_len, auto purpose) { return passwd.substr(0, max_len); }); - } - - // certificate_path contains the chain if not empty. Use certificate chain if available, else use - // certificate_single_path - fs::path certificate_path; - - if (certificate_info.certificate_path.has_value()) { - certificate_path = certificate_info.certificate_path.value(); - } else if (certificate_info.certificate_single_path.has_value()) { - certificate_path = certificate_info.certificate_single_path.value(); - } else { - EVLOG_AND_THROW(std::runtime_error( - "Connecting with security profile 3 but no client side certificate is present or valid")); - } - - EVLOG_info << "Using certificate: " << certificate_path; - if (SSL_CTX_use_certificate_chain_file(context->native_handle(), certificate_path.c_str()) != 1) { - EVLOG_AND_THROW(std::runtime_error("Could not use client certificate file within SSL context")); - } - EVLOG_info << "Using key file: " << certificate_info.key_path; - if (SSL_CTX_use_PrivateKey_file(context->native_handle(), certificate_info.key_path.c_str(), - SSL_FILETYPE_PEM) != 1) { - EVLOG_AND_THROW(std::runtime_error("Could not set private key file within SSL context")); - } - } - - context->set_verify_mode(boost::asio::ssl::verify_peer); - - if (this->connection_options.verify_csms_common_name) { - - // Verify hostname - X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new(); - - if (this->connection_options.verify_csms_allow_wildcards) { - X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - } else { - X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_WILDCARDS); - } - - // Set the host and parameter check - X509_VERIFY_PARAM_set1_host(param, hostname.c_str(), hostname.length()); - SSL_CTX_set1_param(context->native_handle(), param); - - X509_VERIFY_PARAM_free(param); - } else { - EVLOG_warning << "Not verifying the CSMS certificates commonName with the Fully Qualified Domain Name " - "(FQDN) of the server because it has been explicitly turned off via the configuration!"; - } - - if (this->evse_security->is_ca_certificate_installed(ocpp::CaCertificateType::CSMS)) { - EVLOG_info << "Loading ca csms bundle to verify server certificate: " - << this->evse_security->get_verify_file(ocpp::CaCertificateType::CSMS); - rc = SSL_CTX_load_verify_locations( - context->native_handle(), this->evse_security->get_verify_file(ocpp::CaCertificateType::CSMS).c_str(), - NULL); - } - - if (rc != 1) { - EVLOG_error << "Could not load CA verify locations, error: " << ERR_error_string(ERR_get_error(), NULL); - EVLOG_AND_THROW(std::runtime_error("Could not load CA verify locations")); - } - - if (this->connection_options.use_ssl_default_verify_paths) { - rc = SSL_CTX_set_default_verify_paths(context->native_handle()); - if (rc != 1) { - EVLOG_error << "Could not load default CA verify path, error: " - << ERR_error_string(ERR_get_error(), NULL); - EVLOG_AND_THROW(std::runtime_error("Could not load CA verify locations")); - } - } - - } catch (std::exception& e) { - EVLOG_error << "Error on TLS init: " << e.what(); - EVLOG_AND_THROW(std::runtime_error("Could not properly initialize TLS connection.")); - } - return context; -} -void WebsocketTLS::connect_tls() { - websocketpp::lib::error_code ec; - - const tls_client::connection_ptr con = this->wss_client.get_connection( - std::make_shared(this->connection_options.csms_uri.get_websocketpp_uri()), ec); - - if (ec) { - EVLOG_error << "Connection initialization error for TLS websocket: " << ec.message(); - } - - if (this->connection_options.hostName.has_value()) { - EVLOG_info << "User-Host is set to " << this->connection_options.hostName.value(); - con->append_header("User-Host", this->connection_options.hostName.value()); - } - - if (this->connection_options.security_profile == 2) { - EVLOG_debug << "Connecting with security profile: 2"; - std::optional authorization_header = this->getAuthorizationHeader(); - if (authorization_header != std::nullopt) { - con->append_header("Authorization", authorization_header.value()); - } else { - EVLOG_AND_THROW( - std::runtime_error("No authorization key provided when connecting with security profile 2 or 3.")); - } - } else if (this->connection_options.security_profile == 3) { - EVLOG_debug << "Connecting with security profile: 3"; - } else { - EVLOG_AND_THROW( - std::runtime_error("Can not connect with TLS websocket with security profile not being 2 or 3.")); - } - - this->handle = con->get_handle(); - - con->set_open_handler(websocketpp::lib::bind(&WebsocketTLS::on_open_tls, this, &this->wss_client, - websocketpp::lib::placeholders::_1)); - con->set_fail_handler(websocketpp::lib::bind(&WebsocketTLS::on_fail_tls, this, &this->wss_client, - websocketpp::lib::placeholders::_1)); - con->set_close_handler(websocketpp::lib::bind(&WebsocketTLS::on_close_tls, this, &this->wss_client, - websocketpp::lib::placeholders::_1)); - con->set_message_handler(websocketpp::lib::bind( - &WebsocketTLS::on_message_tls, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2)); - con->set_pong_timeout(this->connection_options.pong_timeout_s * 1000); // pong timeout in ms - con->set_pong_timeout_handler( - websocketpp::lib::bind(&WebsocketTLS::on_pong_timeout, this, websocketpp::lib::placeholders::_2)); - - con->add_subprotocol(conversions::ocpp_protocol_version_to_string(this->connection_options.ocpp_version)); - - this->wss_client.connect(con); -} -void WebsocketTLS::on_open_tls(tls_client* c, websocketpp::connection_hdl hdl) { - (void)c; // tls_client is not used in this function - EVLOG_info << "OCPP client successfully connected to TLS websocket server"; - this->connection_attempts = 1; // reset connection attempts - this->m_is_connected = true; - this->reconnecting = false; - this->set_websocket_ping_interval(this->connection_options.ping_interval_s); - this->connected_callback(this->connection_options.security_profile); -} -void WebsocketTLS::on_message_tls(websocketpp::connection_hdl hdl, tls_client::message_ptr msg) { - (void)hdl; // connection_hdl is not used in this function - if (!this->initialized()) { - EVLOG_error << "Message received but TLS websocket has not been correctly initialized. Discarding message."; - return; - } - try { - auto message = msg->get_payload(); - this->message_callback(message); - } catch (websocketpp::exception const& e) { - EVLOG_error << "TLS websocket exception on receiving message: " << e.what(); - } -} -void WebsocketTLS::on_close_tls(tls_client* c, websocketpp::connection_hdl hdl) { - std::lock_guard lk(this->connection_mutex); - this->m_is_connected = false; - this->disconnected_callback(); - this->cancel_reconnect_timer(); - tls_client::connection_ptr con = c->get_con_from_hdl(hdl); - auto error_code = con->get_ec(); - - EVLOG_info << "Closed TLS websocket connection with code: " << error_code << " (" - << websocketpp::close::status::get_string(con->get_remote_close_code()) - << "), reason: " << con->get_remote_close_reason(); - // dont reconnect on normal close - if (con->get_remote_close_code() != websocketpp::close::status::normal) { - this->reconnect(this->get_reconnect_interval()); - } else { - this->closed_callback(value_to_close_reason(con->get_remote_close_code())); - } -} -void WebsocketTLS::on_fail_tls(tls_client* c, websocketpp::connection_hdl hdl) { - std::lock_guard lk(this->connection_mutex); - if (this->m_is_connected) { - this->disconnected_callback(); - } - this->m_is_connected = false; - this->connection_attempts += 1; - tls_client::connection_ptr con = c->get_con_from_hdl(hdl); - const auto ec = con->get_ec(); - this->log_on_fail(ec, con->get_transport_ec(), con->get_response_code()); - - // -1 indicates to always attempt to reconnect - if (this->connection_options.max_connection_attempts == -1 or - this->connection_attempts <= this->connection_options.max_connection_attempts) { - this->reconnect(this->get_reconnect_interval()); - } else { - this->close(WebsocketCloseReason::Normal, "Connection failed"); - } -} - -void WebsocketTLS::close(const WebsocketCloseReason code, const std::string& reason) { - - EVLOG_info << "Closing TLS websocket."; - - websocketpp::lib::error_code ec; - this->cancel_reconnect_timer(); - - this->wss_client.stop_perpetual(); - this->wss_client.close(this->handle, close_reason_to_value(code), reason, ec); - - if (ec) { - EVLOG_error << "Error initiating close of TLS websocket: " << ec.message(); - // on_close_tls wont be called here so we have to call the closed_callback manually - this->closed_callback(WebsocketCloseReason::AbnormalClose); - } else { - EVLOG_info << "Closed TLS websocket successfully."; - } -} - -void WebsocketTLS::ping() { - if (this->m_is_connected) { - auto con = this->wss_client.get_con_from_hdl(this->handle); - websocketpp::lib::error_code error_code; - con->ping(this->connection_options.ping_payload, error_code); - } -} - -} // namespace ocpp diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index d0f0c8856..a79d4c2c4 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -202,6 +202,18 @@ void ChargePoint::disconnect_websocket() { this->connectivity_manager->disconnect_websocket(); } +void ChargePoint::on_network_disconnected(int32_t configuration_slot) { + this->connectivity_manager->on_network_disconnected(configuration_slot); +} + +void ChargePoint::on_network_disconnected(OCPPInterfaceEnum ocpp_interface) { + this->connectivity_manager->on_network_disconnected(ocpp_interface); +} + +bool ChargePoint::on_try_switch_network_connection_profile(const int32_t configuration_slot) { + return this->connectivity_manager->on_try_switch_network_connection_profile(configuration_slot); +} + void ChargePoint::on_firmware_update_status_notification(int32_t request_id, const FirmwareStatusEnum& firmware_update_status) { if (this->firmware_status == firmware_update_status) { @@ -1166,9 +1178,9 @@ void ChargePoint::initialize(const std::map& evse_connector_st std::bind(&ChargePoint::message_callback, this, std::placeholders::_1)); this->connectivity_manager->set_websocket_connected_callback( - std::bind(&ChargePoint::websocket_connected_callback, this, std::placeholders::_1)); + std::bind(&ChargePoint::websocket_connected_callback, this, std::placeholders::_1, std::placeholders::_2)); this->connectivity_manager->set_websocket_disconnected_callback( - std::bind(&ChargePoint::websocket_disconnected_callback, this)); + std::bind(&ChargePoint::websocket_disconnected_callback, this, std::placeholders::_1, std::placeholders::_2)); this->connectivity_manager->set_websocket_connection_failed_callback( std::bind(&ChargePoint::websocket_connection_failed, this, std::placeholders::_1)); @@ -4428,14 +4440,15 @@ void ChargePoint::scheduled_check_v2g_certificate_expiration() { .value_or(12 * 60 * 60))); } -void ChargePoint::websocket_connected_callback(const int security_profile) { +void ChargePoint::websocket_connected_callback(const int configuration_slot, + const NetworkConnectionProfile& network_connection_profile) { this->message_queue->resume(this->message_queue_resume_delay); const auto& security_profile_cv = ControllerComponentVariables::SecurityProfile; if (security_profile_cv.variable.has_value()) { - this->device_model->set_read_only_value(security_profile_cv.component, security_profile_cv.variable.value(), - AttributeEnum::Actual, std::to_string(security_profile), - VARIABLE_ATTRIBUTE_VALUE_SOURCE_INTERNAL); + this->device_model->set_read_only_value( + security_profile_cv.component, security_profile_cv.variable.value(), AttributeEnum::Actual, + std::to_string(network_connection_profile.securityProfile), VARIABLE_ATTRIBUTE_VALUE_SOURCE_INTERNAL); } if (this->registration_status == RegistrationStatusEnum::Accepted and @@ -4465,11 +4478,12 @@ void ChargePoint::websocket_connected_callback(const int security_profile) { this->skip_invalid_csms_certificate_notifications = false; if (this->callbacks.connection_state_changed_callback.has_value()) { - this->callbacks.connection_state_changed_callback.value()(true); + this->callbacks.connection_state_changed_callback.value()(true, configuration_slot, network_connection_profile); } } -void ChargePoint::websocket_disconnected_callback() { +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 @@ -4481,7 +4495,8 @@ void ChargePoint::websocket_disconnected_callback() { this->client_certificate_expiration_check_timer.stop(); this->v2g_certificate_expiration_check_timer.stop(); if (this->callbacks.connection_state_changed_callback.has_value()) { - this->callbacks.connection_state_changed_callback.value()(false); + this->callbacks.connection_state_changed_callback.value()(false, configuration_slot, + network_connection_profile); } } @@ -4755,6 +4770,18 @@ std::vector ChargePoint::get_all_composite_schedules(const in return composite_schedules; } +std::optional ChargePoint::get_network_connection_profile(const int32_t configuration_slot) { + return this->connectivity_manager->get_network_connection_profile(configuration_slot); +} + +std::optional ChargePoint::get_configuration_slot_priority(const int configuration_slot) { + return this->connectivity_manager->get_configuration_slot_priority(configuration_slot); +} + +const std::vector& ChargePoint::get_network_connection_priorities() const { + return this->connectivity_manager->get_network_connection_priorities(); +} + // Static functions /// diff --git a/lib/ocpp/v201/connectivity_manager.cpp b/lib/ocpp/v201/connectivity_manager.cpp index 491a21921..8dd4a1b5c 100644 --- a/lib/ocpp/v201/connectivity_manager.cpp +++ b/lib/ocpp/v201/connectivity_manager.cpp @@ -10,6 +10,9 @@ namespace { const auto WEBSOCKET_INIT_DELAY = std::chrono::seconds(2); const std::string VARIABLE_ATTRIBUTE_VALUE_SOURCE_INTERNAL = "internal"; +/// \brief Default timeout for the return value (future) of the `configure_network_connection_profile_callback` +/// function. +constexpr int32_t default_network_config_timeout_seconds = 60; } // namespace namespace ocpp { @@ -41,16 +44,16 @@ void ConnectivityManager::set_websocket_connection_options(const WebsocketConnec } void ConnectivityManager::set_websocket_connection_options_without_reconnect() { - const auto configuration_slot = this->network_connection_priorities.at(this->network_configuration_priority); - const auto connection_options = this->get_ws_connection_options(std::stoi(configuration_slot)); + const int configuration_slot = get_active_network_configuration_slot(); + const auto connection_options = this->get_ws_connection_options(configuration_slot); this->set_websocket_connection_options(connection_options); } -void ConnectivityManager::set_websocket_connected_callback(WebsocketConnectedCallback callback) { +void ConnectivityManager::set_websocket_connected_callback(WebsocketConnectionCallback callback) { this->websocket_connected_callback = callback; } -void ConnectivityManager::set_websocket_disconnected_callback(WebsocketDisconnectedCallback callback) { +void ConnectivityManager::set_websocket_disconnected_callback(WebsocketConnectionCallback callback) { this->websocket_disconnected_callback = callback; } @@ -82,6 +85,20 @@ ConnectivityManager::get_network_connection_profile(const int32_t configuration_ return std::nullopt; } +std::optional ConnectivityManager::get_configuration_slot_priority(const int configuration_slot) { + auto it = std::find(this->network_connection_priorities.begin(), this->network_connection_priorities.end(), + configuration_slot); + if (it != network_connection_priorities.end()) { + // Index is iterator - begin iterator + return it - network_connection_priorities.begin(); + } + return std::nullopt; +} + +const std::vector& ConnectivityManager::get_network_connection_priorities() const { + return this->network_connection_priorities; +} + bool ConnectivityManager::is_websocket_connected() { return this->websocket != nullptr && this->websocket->is_connected(); } @@ -89,6 +106,7 @@ bool ConnectivityManager::is_websocket_connected() { void ConnectivityManager::start() { init_websocket(); if (websocket != nullptr) { + this->disable_automatic_websocket_reconnects = false; websocket->connect(); } } @@ -123,6 +141,57 @@ bool ConnectivityManager::send_to_websocket(const std::string& message) { return this->websocket->send(message); } +void ConnectivityManager::on_network_disconnected(int32_t configuration_slot) { + const int actual_configuration_slot = get_active_network_configuration_slot(); + std::optional network_connection_profile = + this->get_network_connection_profile(actual_configuration_slot); + + if (!network_connection_profile.has_value()) { + EVLOG_warning << "Network disconnected. No network connection profile configured"; + } else if (configuration_slot == actual_configuration_slot) { + // Since there is no connection anymore: disconnect the websocket, the manager will try to connect with the next + // available network connection profile as we enable reconnects. + this->disconnect_websocket(ocpp::WebsocketCloseReason::GoingAway); + this->disable_automatic_websocket_reconnects = false; + } +} + +void ConnectivityManager::on_network_disconnected(OCPPInterfaceEnum ocpp_interface) { + + const int actual_configuration_slot = get_active_network_configuration_slot(); + std::optional network_connection_profile = + this->get_network_connection_profile(actual_configuration_slot); + + if (!network_connection_profile.has_value()) { + EVLOG_warning << "Network disconnected. No network connection profile configured"; + } else if (ocpp_interface == network_connection_profile.value().ocppInterface) { + // Since there is no connection anymore: disconnect the websocket, the manager will try to connect with the next + // available network connection profile as we enable reconnects. + this->disconnect_websocket(ocpp::WebsocketCloseReason::GoingAway); + this->disable_automatic_websocket_reconnects = false; + } +} + +bool ConnectivityManager::on_try_switch_network_connection_profile(const int32_t configuration_slot) { + if (!is_higher_priority_profile(configuration_slot)) { + return false; + } + + EVLOG_info << "Trying to connect with higher priority network connection profile (configuration slots: " + << this->get_active_network_configuration_slot() << " --> " << configuration_slot << ")."; + + const std::optional network_connection_profile_opt = + this->get_network_connection_profile(configuration_slot); + if (!network_connection_profile_opt.has_value()) { + EVLOG_warning << "Could not find network connection profile belonging to configuration slot " + << configuration_slot; + return false; + } + this->disconnect_websocket(WebsocketCloseReason::Normal); + reconnect(WebsocketCloseReason::Normal, get_configuration_slot_priority(configuration_slot)); + return true; +} + void ConnectivityManager::init_websocket() { if (this->device_model.get_value(ControllerComponentVariables::ChargePointId).find(':') != std::string::npos) { @@ -132,16 +201,50 @@ void ConnectivityManager::init_websocket() { // cache the network profiles on initialization cache_network_connection_profiles(); - const auto configuration_slot = this->network_connection_priorities.at(this->network_configuration_priority); - const auto connection_options = this->get_ws_connection_options(std::stoi(configuration_slot)); - - const auto network_connection_profile = this->get_network_connection_profile(std::stoi(configuration_slot)); + const int config_slot_int = this->network_connection_priorities.at(this->network_configuration_priority); + + const auto network_connection_profile = this->get_network_connection_profile(config_slot_int); + // Not const as the iface member can be set by the configure network connection profile callback + auto connection_options = this->get_ws_connection_options(config_slot_int); + bool can_use_connection_profile = true; + + if (!network_connection_profile.has_value()) { + EVLOG_warning << "No network connection profile configured for " << config_slot_int; + can_use_connection_profile = false; + } else if (this->configure_network_connection_profile_callback.has_value()) { + EVLOG_debug << "Request to configure network connection profile " << config_slot_int; + + std::future config_status = this->configure_network_connection_profile_callback.value()( + config_slot_int, network_connection_profile.value()); + const int32_t config_timeout = + this->device_model.get_optional_value(ControllerComponentVariables::NetworkConfigTimeout) + .value_or(default_network_config_timeout_seconds); + + std::future_status status = config_status.wait_for(std::chrono::seconds(config_timeout)); + + switch (status) { + case std::future_status::deferred: + case std::future_status::timeout: { + EVLOG_warning << "Timeout configuring config slot: " << config_slot_int; + can_use_connection_profile = false; + break; + } + case std::future_status::ready: { + ConfigNetworkResult result = config_status.get(); + if (result.success and result.network_profile_slot == config_slot_int) { + EVLOG_debug << "Config slot " << config_slot_int << " is configured"; + // Set interface or ip to connection options. + connection_options.iface = result.interface_address; + } else { + EVLOG_warning << "Could not configure config slot " << config_slot_int; + can_use_connection_profile = false; + } + break; + } + } + } - if (!network_connection_profile.has_value() or - (this->configure_network_connection_profile_callback.has_value() and - !this->configure_network_connection_profile_callback.value()(network_connection_profile.value()))) { - EVLOG_warning << "NetworkConnectionProfile could not be retrieved or configuration of network with the given " - "profile failed"; + if (!can_use_connection_profile) { this->websocket_timer.timeout( [this]() { this->next_network_configuration_priority(); @@ -152,13 +255,13 @@ void ConnectivityManager::init_websocket() { } EVLOG_info << "Open websocket with NetworkConfigurationPriority: " << this->network_configuration_priority + 1 - << " which is configurationSlot " << configuration_slot; + << " which is configurationSlot " << config_slot_int; if (const auto& active_network_profile_cv = ControllerComponentVariables::ActiveNetworkProfile; active_network_profile_cv.variable.has_value()) { - this->device_model.set_read_only_value(active_network_profile_cv.component, - active_network_profile_cv.variable.value(), AttributeEnum::Actual, - configuration_slot, VARIABLE_ATTRIBUTE_VALUE_SOURCE_INTERNAL); + this->device_model.set_read_only_value( + active_network_profile_cv.component, active_network_profile_cv.variable.value(), AttributeEnum::Actual, + std::to_string(config_slot_int), VARIABLE_ATTRIBUTE_VALUE_SOURCE_INTERNAL); } if (const auto& security_profile_cv = ControllerComponentVariables::SecurityProfile; @@ -171,31 +274,11 @@ void ConnectivityManager::init_websocket() { this->websocket = std::make_unique(connection_options, this->evse_security, this->logging); - if (this->websocket_connected_callback.has_value()) { - this->websocket->register_connected_callback(websocket_connected_callback.value()); - } - - if (this->websocket_disconnected_callback.has_value()) { - this->websocket->register_disconnected_callback(websocket_disconnected_callback.value()); - } - + this->websocket->register_connected_callback( + std::bind(&ConnectivityManager::on_websocket_connected, this, std::placeholders::_1)); + this->websocket->register_disconnected_callback(std::bind(&ConnectivityManager::on_websocket_disconnected, this)); this->websocket->register_closed_callback( - [this, connection_options, configuration_slot](const WebsocketCloseReason reason) { - EVLOG_warning << "Closed websocket of NetworkConfigurationPriority: " - << this->network_configuration_priority + 1 << " which is configurationSlot " - << configuration_slot; - - if (!this->disable_automatic_websocket_reconnects) { - this->websocket_timer.timeout( - [this, reason]() { - if (reason != WebsocketCloseReason::ServiceRestart) { - this->next_network_configuration_priority(); - } - this->start(); - }, - WEBSOCKET_INIT_DELAY); - } - }); + std::bind(&ConnectivityManager::on_websocket_closed, this, std::placeholders::_1)); if (websocket_connection_failed_callback.has_value()) { this->websocket->register_connection_failed_callback(websocket_connection_failed_callback.value()); @@ -250,6 +333,87 @@ WebsocketConnectionOptions ConnectivityManager::get_ws_connection_options(const return connection_options; } +void ConnectivityManager::on_websocket_connected([[maybe_unused]] int security_profile) { + const int actual_configuration_slot = get_active_network_configuration_slot(); + std::optional network_connection_profile = + this->get_network_connection_profile(actual_configuration_slot); + + if (this->websocket_connected_callback.has_value() and network_connection_profile.has_value()) { + this->websocket_connected_callback.value()(actual_configuration_slot, network_connection_profile.value()); + } +} + +void ConnectivityManager::on_websocket_disconnected() { + std::optional network_connection_profile = + this->get_network_connection_profile(this->get_active_network_configuration_slot()); + + if (this->websocket_disconnected_callback.has_value() and network_connection_profile.has_value()) { + this->websocket_disconnected_callback.value()(this->get_active_network_configuration_slot(), + network_connection_profile.value()); + } +} + +void ConnectivityManager::on_websocket_closed(ocpp::WebsocketCloseReason reason) { + EVLOG_warning << "Closed websocket of NetworkConfigurationPriority: " << this->network_configuration_priority + 1 + << " which is configurationSlot " << this->get_active_network_configuration_slot(); + + if (!this->disable_automatic_websocket_reconnects) { + reconnect(reason); + } +} + +void ConnectivityManager::reconnect(WebsocketCloseReason reason, std::optional next_priority) { + this->websocket_timer.timeout( + [this, reason, next_priority]() { + if (reason != WebsocketCloseReason::ServiceRestart) { + if (!next_priority.has_value()) { + this->next_network_configuration_priority(); + } else { + this->network_configuration_priority = next_priority.value(); + } + } + this->start(); + }, + WEBSOCKET_INIT_DELAY); +} + +bool ConnectivityManager::is_higher_priority_profile(const int new_configuration_slot) { + + const int current_slot = get_active_network_configuration_slot(); + if (current_slot == 0) { + // No slot in use, new is always higher priority. + return true; + } + + if (current_slot == new_configuration_slot) { + // Slot is the same, probably already connected + return false; + } + + const std::optional new_priority = get_configuration_slot_priority(new_configuration_slot); + if (!new_priority.has_value()) { + // Slot not found. + return false; + } + + const std::optional current_priority = get_configuration_slot_priority(current_slot); + if (!current_priority.has_value()) { + // Slot not found. + return false; + } + + if (new_priority.value() < current_priority.value()) { + // Priority is indeed higher (lower index means higher priority) + return true; + } + + return false; +} + +int ConnectivityManager::get_active_network_configuration_slot() { + return this->network_connection_priorities.at(this->network_configuration_priority); +} + void ConnectivityManager::next_network_configuration_priority() { // retrieve priorities from cache @@ -271,8 +435,12 @@ void ConnectivityManager::cache_network_connection_profiles() { this->network_connection_profiles = json::parse(this->device_model.get_value(ControllerComponentVariables::NetworkConnectionProfiles)); - this->network_connection_priorities = ocpp::split_string( - this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), ','); + for (const std::string& str : ocpp::split_string( + this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), + ',')) { + int num = std::stoi(str); + this->network_connection_priorities.push_back(num); + } if (this->network_connection_priorities.empty()) { EVLOG_AND_THROW(std::runtime_error("NetworkConfigurationPriority must not be empty")); diff --git a/lib/ocpp/v201/ctrlr_component_variables.cpp b/lib/ocpp/v201/ctrlr_component_variables.cpp index 93cba64f1..a4fcbc308 100644 --- a/lib/ocpp/v201/ctrlr_component_variables.cpp +++ b/lib/ocpp/v201/ctrlr_component_variables.cpp @@ -198,6 +198,13 @@ const ComponentVariable& RoundClockAlignedTimestamps = { "RoundClockAlignedTimestamps", }), }; +const ComponentVariable& NetworkConfigTimeout = { + ControllerComponents::InternalCtrlr, + std::nullopt, + std::optional({ + "NetworkConfigTimeout", + }), +}; const ComponentVariable& SupportedChargingProfilePurposeTypes = { ControllerComponents::InternalCtrlr, std::nullopt, diff --git a/lib/ocpp/v201/profile.cpp b/lib/ocpp/v201/profile.cpp index 48cb1d68d..9656d0692 100644 --- a/lib/ocpp/v201/profile.cpp +++ b/lib/ocpp/v201/profile.cpp @@ -359,13 +359,12 @@ CompositeSchedule calculate_composite_schedule(std::vector& in_c const auto now = floor_seconds(in_now); const auto end = floor_seconds(in_end); - CompositeSchedule composite{ - .chargingSchedulePeriod = {}, - .evseId = EVSEID_NOT_SET, - .duration = elapsed_seconds(end, now), - .scheduleStart = now, - .chargingRateUnit = selected_unit, - }; + CompositeSchedule composite; + composite.chargingSchedulePeriod = {}; + composite.evseId = EVSEID_NOT_SET; + composite.duration = elapsed_seconds(end, now); + composite.scheduleStart = now; + composite.chargingRateUnit = selected_unit; // sort the combined_schedules in stack priority order struct { @@ -476,14 +475,12 @@ CompositeSchedule calculate_composite_schedule(const CompositeSchedule& charging const CompositeScheduleDefaultLimits& default_limits, int32_t supply_voltage) { - CompositeSchedule combined{ - .chargingSchedulePeriod = {}, - .evseId = EVSEID_NOT_SET, - .duration = tx_default.duration, - .scheduleStart = tx_default.scheduleStart, - .chargingRateUnit = tx_default.chargingRateUnit, - - }; + CompositeSchedule combined; + combined.chargingSchedulePeriod = {}; + combined.evseId = EVSEID_NOT_SET; + combined.duration = tx_default.duration; + combined.scheduleStart = tx_default.scheduleStart; + combined.chargingRateUnit = tx_default.chargingRateUnit; const float default_limit = (tx_default.chargingRateUnit == ChargingRateUnitEnum::A) ? static_cast(default_limits.amps) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index de47e1e34..c5e188c69 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,6 +37,13 @@ target_compile_definitions(libocpp_unit_tests TEST_PROFILES_LOCATION_V201="${TEST_PROFILES_LOCATION_V201}" ) +target_compile_options(libocpp_unit_tests + PRIVATE + -pedantic-errors +) + +target_compile_features(libocpp_unit_tests PUBLIC cxx_std_17) + add_custom_command(TARGET libocpp_unit_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/resources/unittest_device_model.db ${CMAKE_CURRENT_BINARY_DIR}/resources/unittest_device_model.db COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/resources/unittest_device_model_missing_required.db ${CMAKE_CURRENT_BINARY_DIR}/resources/unittest_device_model_missing_required.db diff --git a/tests/lib/ocpp/common/test_message_queue.cpp b/tests/lib/ocpp/common/test_message_queue.cpp index 4fca812bb..dbf7f5f21 100644 --- a/tests/lib/ocpp/common/test_message_queue.cpp +++ b/tests/lib/ocpp/common/test_message_queue.cpp @@ -181,7 +181,7 @@ class MessageQueueTest : public ::testing::Test { this->call_marker_cond_var.notify_one(); return value; }); - }; + } void wait_for_calls(int expected_calls = 1) { std::unique_lock lock(call_marker_mutex); diff --git a/tests/lib/ocpp/v201/test_charge_point.cpp b/tests/lib/ocpp/v201/test_charge_point.cpp index eff0d757a..0fa45be8c 100644 --- a/tests/lib/ocpp/v201/test_charge_point.cpp +++ b/tests/lib/ocpp/v201/test_charge_point.cpp @@ -76,9 +76,8 @@ class ChargePointCommonTestFixtureV201 : public DatabaseTestingUtils { std::vector create_charging_schedule_periods(std::vector start_periods) { auto charging_schedule_periods = std::vector(); for (auto start_period : start_periods) { - auto charging_schedule_period = ChargingSchedulePeriod{ - .startPeriod = start_period, - }; + ChargingSchedulePeriod charging_schedule_period; + charging_schedule_period.startPeriod = start_period; charging_schedule_periods.push_back(charging_schedule_period); } @@ -114,16 +113,18 @@ class ChargePointCommonTestFixtureV201 : public DatabaseTestingUtils { std::optional validTo = {}) { auto recurrency_kind = RecurrencyKindEnum::Daily; std::vector charging_schedules = {charging_schedule}; - return ChargingProfile{.id = charging_profile_id, - .stackLevel = stack_level, - .chargingProfilePurpose = charging_profile_purpose, - .chargingProfileKind = charging_profile_kind, - .chargingSchedule = charging_schedules, - .customData = {}, - .recurrencyKind = recurrency_kind, - .validFrom = validFrom, - .validTo = validTo, - .transactionId = transaction_id}; + ChargingProfile charging_profile; + charging_profile.id = charging_profile_id; + charging_profile.stackLevel = stack_level; + charging_profile.chargingProfilePurpose = charging_profile_purpose; + charging_profile.chargingProfileKind = charging_profile_kind; + charging_profile.chargingSchedule = charging_schedules; + charging_profile.customData = {}; + charging_profile.recurrencyKind = recurrency_kind; + charging_profile.validFrom = validFrom; + charging_profile.validTo = validTo; + charging_profile.transactionId = transaction_id; + return charging_profile; } std::shared_ptr create_database_handler() { @@ -342,8 +343,7 @@ TEST_F(ChargePointCommonTestFixtureV201, callbacks.configure_network_connection_profile_callback = nullptr; EXPECT_FALSE(callbacks.all_callbacks_valid(device_model)); - testing::MockFunction - configure_network_connection_profile_callback_mock; + testing::MockFunction configure_network_connection_profile_callback_mock; callbacks.configure_network_connection_profile_callback = configure_network_connection_profile_callback_mock.AsStdFunction(); EXPECT_TRUE(callbacks.all_callbacks_valid(device_model)); @@ -626,11 +626,10 @@ class ChargePointFunctionalityTestFixtureV201 : public ChargePointCommonTestFixt template EnhancedMessage request_to_enhanced_message(const T& req) { auto message_id = uuid(); ocpp::Call call(req, message_id); - EnhancedMessage enhanced_message{ - .uniqueId = message_id, - .messageType = M, - .messageTypeId = MessageTypeId::CALL, - }; + EnhancedMessage enhanced_message; + enhanced_message.uniqueId = message_id; + enhanced_message.messageType = M; + enhanced_message.messageTypeId = MessageTypeId::CALL; call_to_json(enhanced_message.message, call); @@ -783,7 +782,8 @@ TEST_F(ChargePointFunctionalityTestFixtureV201, RequestStartTransactionRequest req; req.evseId = DEFAULT_EVSE_ID; - req.idToken = IdToken{.idToken = "Local", .type = IdTokenEnum::Local}; + req.idToken.idToken = "Local"; + req.idToken.type = IdTokenEnum::Local; req.chargingProfile = profile; auto start_transaction_req = @@ -909,7 +909,8 @@ TEST_F(ChargePointFunctionalityTestFixtureV201, RequestStartTransactionRequest req; req.evseId = DEFAULT_EVSE_ID; - req.idToken = IdToken{.idToken = "Local", .type = IdTokenEnum::Local}; + req.idToken.idToken = "Local"; + req.idToken.type = IdTokenEnum::Local; req.chargingProfile = profile; auto start_transaction_req = diff --git a/tests/lib/ocpp/v201/test_composite_schedule.cpp b/tests/lib/ocpp/v201/test_composite_schedule.cpp index 632bfb221..654f3032f 100644 --- a/tests/lib/ocpp/v201/test_composite_schedule.cpp +++ b/tests/lib/ocpp/v201/test_composite_schedule.cpp @@ -114,11 +114,10 @@ class CompositeScheduleTestFixtureV201 : public DatabaseTestingUtils { std::vector create_charging_schedule_periods(int32_t start_period, std::optional number_phases = std::nullopt, std::optional phase_to_use = std::nullopt) { - auto charging_schedule_period = ChargingSchedulePeriod{ - .startPeriod = start_period, - .numberPhases = number_phases, - .phaseToUse = phase_to_use, - }; + ChargingSchedulePeriod charging_schedule_period; + charging_schedule_period.startPeriod = start_period; + charging_schedule_period.numberPhases = number_phases; + charging_schedule_period.phaseToUse = phase_to_use; return {charging_schedule_period}; } @@ -126,9 +125,8 @@ class CompositeScheduleTestFixtureV201 : public DatabaseTestingUtils { std::vector create_charging_schedule_periods(std::vector start_periods) { auto charging_schedule_periods = std::vector(); for (auto start_period : start_periods) { - auto charging_schedule_period = ChargingSchedulePeriod{ - .startPeriod = start_period, - }; + ChargingSchedulePeriod charging_schedule_period; + charging_schedule_period.startPeriod = start_period; charging_schedule_periods.push_back(charging_schedule_period); } @@ -137,8 +135,10 @@ class CompositeScheduleTestFixtureV201 : public DatabaseTestingUtils { std::vector create_charging_schedule_periods_with_phases(int32_t start_period, int32_t numberPhases, int32_t phaseToUse) { - auto charging_schedule_period = - ChargingSchedulePeriod{.startPeriod = start_period, .numberPhases = numberPhases, .phaseToUse = phaseToUse}; + ChargingSchedulePeriod charging_schedule_period; + charging_schedule_period.startPeriod = start_period; + charging_schedule_period.numberPhases = numberPhases; + charging_schedule_period.phaseToUse = phaseToUse; return {charging_schedule_period}; } @@ -151,16 +151,18 @@ class CompositeScheduleTestFixtureV201 : public DatabaseTestingUtils { std::optional validTo = {}) { auto recurrency_kind = RecurrencyKindEnum::Daily; std::vector charging_schedules = {charging_schedule}; - return ChargingProfile{.id = charging_profile_id, - .stackLevel = stack_level, - .chargingProfilePurpose = charging_profile_purpose, - .chargingProfileKind = charging_profile_kind, - .chargingSchedule = charging_schedules, - .customData = {}, - .recurrencyKind = recurrency_kind, - .validFrom = validFrom, - .validTo = validTo, - .transactionId = transaction_id}; + ChargingProfile charging_profile; + charging_profile.id = charging_profile_id; + charging_profile.stackLevel = stack_level; + charging_profile.chargingProfilePurpose = charging_profile_purpose; + charging_profile.chargingProfileKind = charging_profile_kind; + charging_profile.chargingSchedule = charging_schedules; + charging_profile.customData = {}; + charging_profile.recurrencyKind = recurrency_kind; + charging_profile.validFrom = validFrom; + charging_profile.validTo = validTo; + charging_profile.transactionId = transaction_id; + return charging_profile; } void create_device_model_db(const std::string& path) { @@ -211,132 +213,110 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_Foundati SmartChargingTestUtils::get_charging_profiles_from_directory(BASE_JSON_PATH + "/grid/"); const DateTime start_time = ocpp::DateTime("2024-01-17T00:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-18T00:00:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 1.0, - .numberPhases = 1, - }, - { - .startPeriod = 3600, - .limit = 2.0, - .numberPhases = 1, - }, - { - .startPeriod = 7200, - .limit = 3.0, - .numberPhases = 1, - }, - { - .startPeriod = 10800, - .limit = 4.0, - .numberPhases = 1, - }, - { - .startPeriod = 14400, - .limit = 5.0, - .numberPhases = 1, - }, - { - .startPeriod = 18000, - .limit = 6.0, - .numberPhases = 1, - }, - { - .startPeriod = 21600, - .limit = 7.0, - .numberPhases = 1, - }, - { - .startPeriod = 25200, - .limit = 8.0, - .numberPhases = 1, - }, - { - .startPeriod = 28800, - .limit = 9.0, - .numberPhases = 1, - }, - { - .startPeriod = 32400, - .limit = 10.0, - .numberPhases = 1, - }, - { - .startPeriod = 36000, - .limit = 11.0, - .numberPhases = 1, - }, - { - .startPeriod = 39600, - .limit = 12.0, - .numberPhases = 1, - }, - { - .startPeriod = 43200, - .limit = 13.0, - .numberPhases = 1, - }, - { - .startPeriod = 46800, - .limit = 14.0, - .numberPhases = 1, - }, - { - .startPeriod = 50400, - .limit = 15.0, - .numberPhases = 1, - }, - { - .startPeriod = 54000, - .limit = 16.0, - .numberPhases = 1, - }, - { - .startPeriod = 57600, - .limit = 17.0, - .numberPhases = 1, - }, - { - .startPeriod = 61200, - .limit = 18.0, - .numberPhases = 1, - }, - { - .startPeriod = 64800, - .limit = 19.0, - .numberPhases = 1, - }, - { - .startPeriod = 68400, - .limit = 20.0, - .numberPhases = 1, - }, - { - .startPeriod = 72000, - .limit = 21.0, - .numberPhases = 1, - }, - { - .startPeriod = 75600, - .limit = 22.0, - .numberPhases = 1, - }, - { - .startPeriod = 79200, - .limit = 23.0, - .numberPhases = 1, - }, - { - .startPeriod = 82800, - .limit = 24.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 86400, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 1.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 3600; + period2.limit = 2.0; + period2.numberPhases = 1; + ChargingSchedulePeriod period3; + period3.startPeriod = 7200; + period3.limit = 3.0; + period3.numberPhases = 1; + ChargingSchedulePeriod period4; + period4.startPeriod = 10800; + period4.limit = 4.0; + period4.numberPhases = 1; + ChargingSchedulePeriod period5; + period5.startPeriod = 14400; + period5.limit = 5.0; + period5.numberPhases = 1; + ChargingSchedulePeriod period6; + period6.startPeriod = 18000; + period6.limit = 6.0; + period6.numberPhases = 1; + ChargingSchedulePeriod period7; + period7.startPeriod = 21600; + period7.limit = 7.0; + period7.numberPhases = 1; + ChargingSchedulePeriod period8; + period8.startPeriod = 25200; + period8.limit = 8.0; + period8.numberPhases = 1; + ChargingSchedulePeriod period9; + period9.startPeriod = 28800; + period9.limit = 9.0; + period9.numberPhases = 1; + ChargingSchedulePeriod period10; + period10.startPeriod = 32400; + period10.limit = 10.0; + period10.numberPhases = 1; + ChargingSchedulePeriod period11; + period11.startPeriod = 36000; + period11.limit = 11.0; + period11.numberPhases = 1; + ChargingSchedulePeriod period12; + period12.startPeriod = 39600; + period12.limit = 12.0; + period12.numberPhases = 1; + ChargingSchedulePeriod period13; + period13.startPeriod = 43200; + period13.limit = 13.0; + period13.numberPhases = 1; + ChargingSchedulePeriod period14; + period14.startPeriod = 46800; + period14.limit = 14.0; + period14.numberPhases = 1; + ChargingSchedulePeriod period15; + period15.startPeriod = 50400; + period15.limit = 15.0; + period15.numberPhases = 1; + ChargingSchedulePeriod period16; + period16.startPeriod = 54000; + period16.limit = 16.0; + period16.numberPhases = 1; + ChargingSchedulePeriod period17; + period17.startPeriod = 57600; + period17.limit = 17.0; + period17.numberPhases = 1; + ChargingSchedulePeriod period18; + period18.startPeriod = 61200; + period18.limit = 18.0; + period18.numberPhases = 1; + ChargingSchedulePeriod period19; + period19.startPeriod = 64800; + period19.limit = 19.0; + period19.numberPhases = 1; + ChargingSchedulePeriod period20; + period20.startPeriod = 68400; + period20.limit = 20.0; + period20.numberPhases = 1; + ChargingSchedulePeriod period21; + period21.startPeriod = 72000; + period21.limit = 21.0; + period21.numberPhases = 1; + ChargingSchedulePeriod period22; + period22.startPeriod = 75600; + period22.limit = 22.0; + period22.numberPhases = 1; + ChargingSchedulePeriod period23; + period23.startPeriod = 79200; + period23.limit = 23.0; + period23.numberPhases = 1; + ChargingSchedulePeriod period24; + period24.startPeriod = 82800; + period24.limit = 24.0; + period24.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3, period4, period5, period6, period7, period8, + period9, period10, period11, period12, period13, period14, period15, period16, + period17, period18, period19, period20, period21, period22, period23, period24}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 86400; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -353,17 +333,16 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_LayeredT const DateTime start_time = ocpp::DateTime("2024-01-18T18:04:00"); const DateTime end_time = ocpp::DateTime("2024-01-18T18:22:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 19.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 1080, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = 19.0; + period.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 1080; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -376,22 +355,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_LayeredT const DateTime start_time = ocpp::DateTime("2024-01-17T18:04:00"); const DateTime end_time = ocpp::DateTime("2024-01-17T18:33:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 2000.0, - .numberPhases = 1, - }, - { - .startPeriod = 1080, - .limit = 19.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 1740, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 2000.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 1080; + period2.limit = 19.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 1740; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -404,27 +381,25 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_LayeredT const DateTime start_time = ocpp::DateTime("2024-01-17T18:04:00"); const DateTime end_time = ocpp::DateTime("2024-01-17T19:04:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 2000.0, - .numberPhases = 1, - }, - { - .startPeriod = 1080, - .limit = 19.0, - .numberPhases = 1, - }, - { - .startPeriod = 3360, - .limit = 20.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 3600, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 2000.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 1080; + period2.limit = 19.0; + period2.numberPhases = 1; + ChargingSchedulePeriod period3; + period3.startPeriod = 3360; + period3.limit = 20.0; + period3.numberPhases = 1; + + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 3600; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -440,17 +415,16 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_LayeredR const DateTime start_time = ocpp::DateTime("2024-02-17T18:04:00"); const DateTime end_time = ocpp::DateTime("2024-02-17T18:05:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 2000.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 60, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = 2000.0; + period.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 60; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -463,23 +437,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_LayeredT SmartChargingTestUtils::get_charging_profiles_from_file("singles/TXProfile_Absolute_Start18-04.json"); const DateTime start_time = ocpp::DateTime("2024-01-17T18:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-17T18:05:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = DEFAULT_LIMIT_WATTS, - .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES, - }, - { - .startPeriod = 240, - .limit = - profiles.at(0).chargingSchedule.front().chargingSchedulePeriod.front().limit, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 300, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = DEFAULT_LIMIT_WATTS; + period1.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + ChargingSchedulePeriod period2; + period2.startPeriod = 240; + period2.limit = profiles.at(0).chargingSchedule.front().chargingSchedulePeriod.front().limit; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 300; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -492,33 +463,28 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_LayeredR SmartChargingTestUtils::get_charging_profiles_from_directory(BASE_JSON_PATH + "/layered_recurring/"); const DateTime start_time = ocpp::DateTime("2024-02-19T18:00:00"); const DateTime end_time = ocpp::DateTime("2024-02-19T19:04:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = - {{ - .startPeriod = 0, - .limit = 19.0, - .numberPhases = 1, - }, - { - .startPeriod = 240, - .limit = profiles.back().chargingSchedule.front().chargingSchedulePeriod.front().limit, - .numberPhases = 1, - }, - { - .startPeriod = 1320, - .limit = 19.0, - .numberPhases = 1, - }, - { - .startPeriod = 3600, - .limit = 20.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 3840, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 19.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 240; + period2.limit = profiles.back().chargingSchedule.front().chargingSchedulePeriod.front().limit; + period2.numberPhases = 1; + ChargingSchedulePeriod period3; + period3.startPeriod = 1320; + period3.limit = 19.0; + period3.numberPhases = 1; + ChargingSchedulePeriod period4; + period4.startPeriod = 3600; + period4.limit = 20.0; + period4.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3, period4}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 3840; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -533,27 +499,24 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_Validate const DateTime start_time = ocpp::DateTime("2024-01-17T18:01:00"); const DateTime end_time = ocpp::DateTime("2024-01-18T06:00:00"); std::vector profiles = SmartChargingTestUtils::get_baseline_profile_vector(); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 2000.0, - .numberPhases = 1, - }, - { - .startPeriod = 1020, - .limit = 11000.0, - .numberPhases = 3, - }, - { - .startPeriod = 25140, - .limit = 6000.0, - .numberPhases = 3, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 43140, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 2000.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 1020; + period2.limit = 11000.0; + period2.numberPhases = 3; + ChargingSchedulePeriod period3; + period3.startPeriod = 25140; + period3.limit = 6000.0; + period3.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 43140; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -572,17 +535,16 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_Relative // Doing this in order to avoid mocking system_clock::now() auto transaction = std::move(this->evse_manager->get_evse(DEFAULT_EVSE_ID).get_transaction()); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 2000.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 3600, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = 2000.0; + period.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 3600; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -601,22 +563,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_Relative const DateTime start_time = ocpp::DateTime("2024-05-17T05:00:00"); const DateTime end_time = ocpp::DateTime("2024-05-17T06:01:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 2000.0, - .numberPhases = 1, - }, - { - .startPeriod = 3601, - .limit = 7.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 3660, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 2000.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 3601; + period2.limit = 7.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 3660; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -633,27 +593,24 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_DemoCase const DateTime start_time = ocpp::DateTime("2024-01-17T18:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-18T06:00:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 2000.0, - .numberPhases = 1, - }, - { - .startPeriod = 1080, - .limit = 11000.0, - .numberPhases = 1, - }, - { - .startPeriod = 25200, - .limit = 6000.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 43200, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 2000.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 1080; + period2.limit = 11000.0; + period2.numberPhases = 1; + ChargingSchedulePeriod period3; + period3.startPeriod = 25200; + period3.limit = 6000.0; + period3.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 43200; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -670,22 +627,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_DemoCase const DateTime start_time = ocpp::DateTime("2024-01-19T18:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-20T06:00:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 11000.0, - .numberPhases = 1, - }, - { - .startPeriod = 25200, - .limit = 6000.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 43200, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 11000.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 25200; + period2.limit = 6000.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 43200; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -704,22 +659,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_MaxOverr const DateTime start_time = ocpp::DateTime("2024-01-17T00:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-17T02:00:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 10.0, - .numberPhases = 1, - }, - { - .startPeriod = 3600, - .limit = 20.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 7200, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 10.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 3600; + period2.limit = 20.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 7200; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -733,22 +686,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_MaxOverr const DateTime start_time = ocpp::DateTime("2024-01-17T22:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-18T00:00:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 230.0, - .numberPhases = 1, - }, - { - .startPeriod = 3600, - .limit = 10.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 7200, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 230.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 3600; + period2.limit = 10.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 7200; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -762,22 +713,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_External const DateTime start_time = ocpp::DateTime("2024-01-17T00:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-17T02:00:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 10.0, - .numberPhases = 1, - }, - { - .startPeriod = 3600, - .limit = 20.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 7200, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 10.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 3600; + period2.limit = 20.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 7200; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -791,22 +740,20 @@ TEST_F(CompositeScheduleTestFixtureV201, K08_CalculateCompositeSchedule_External const DateTime start_time = ocpp::DateTime("2024-01-17T22:00:00"); const DateTime end_time = ocpp::DateTime("2024-01-18T00:00:00"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 230.0, - .numberPhases = 1, - }, - { - .startPeriod = 3600, - .limit = 10.0, - .numberPhases = 1, - }}, - .evseId = DEFAULT_EVSE_ID, - .duration = 7200, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::W, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 230.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 3600; + period2.limit = 10.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 7200; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::W; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W); @@ -820,22 +767,36 @@ TEST_F(CompositeScheduleTestFixtureV201, OCTT_TC_K_41_CS) { const DateTime start_time = ocpp::DateTime("2024-08-21T12:24:40"); const DateTime end_time = ocpp::DateTime("2024-08-21T12:31:20"); - CompositeSchedule expected = { - .chargingSchedulePeriod = {{ - .startPeriod = 0, - .limit = 8.0, - .numberPhases = 3, - }, - {.startPeriod = 46, .limit = 10.0, .numberPhases = 3}, - {.startPeriod = 196, .limit = 6.0, .numberPhases = 3}, - {.startPeriod = 236, .limit = 10.0, .numberPhases = 3}, - {.startPeriod = 260, .limit = 8.0, .numberPhases = 3}, - {.startPeriod = 300, .limit = 10.0, .numberPhases = 3}}, - .evseId = DEFAULT_EVSE_ID, - .duration = 400, - .scheduleStart = start_time, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 8.0; + period1.numberPhases = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 46; + period2.limit = 10.0; + period2.numberPhases = 3; + ChargingSchedulePeriod period3; + period3.startPeriod = 196; + period3.limit = 6.0; + period3.numberPhases = 3; + ChargingSchedulePeriod period4; + period4.startPeriod = 236; + period4.limit = 10.0; + period4.numberPhases = 3; + ChargingSchedulePeriod period5; + period5.startPeriod = 260; + period5.limit = 8.0; + period5.numberPhases = 3; + ChargingSchedulePeriod period6; + period6.startPeriod = 300; + period6.limit = 10.0; + period6.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3, period4, period5, period6}; + expected.evseId = DEFAULT_EVSE_ID; + expected.duration = 400; + expected.scheduleStart = start_time; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = handler.calculate_composite_schedule(profiles, start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::A); diff --git a/tests/lib/ocpp/v201/test_database_handler.cpp b/tests/lib/ocpp/v201/test_database_handler.cpp index 9d1530821..5a0fd8795 100644 --- a/tests/lib/ocpp/v201/test_database_handler.cpp +++ b/tests/lib/ocpp/v201/test_database_handler.cpp @@ -168,9 +168,12 @@ TEST_F(DatabaseHandlerTest, TransactionDeleteNotFound) { } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithNoData_InsertProfile) { - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); + ChargingProfile profile; + profile.id = 1; + profile.stackLevel = 1; + profile.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile); auto sut = this->database_handler.get_all_charging_profiles_group_by_evse(); @@ -181,12 +184,18 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithNoData_InsertProfile) { } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithProfileData_UpdateProfile) { - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 2, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 2, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); + ChargingProfile profile1; + profile1.id = 2; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile1); + ChargingProfile profile2; + profile2.id = 2; + profile2.stackLevel = 2; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile2); std::string sql = "SELECT COUNT(*) FROM CHARGING_PROFILES"; auto select_stmt = this->database->new_statement(sql); @@ -198,12 +207,18 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithProfileData_UpdateProfile) { } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithProfileData_InsertNewProfile) { - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 2, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); + ChargingProfile profile1; + profile1.id = 1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile1); + ChargingProfile profile2; + profile2.id = 2; + profile2.stackLevel = 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile2); std::string sql = "SELECT COUNT(*) FROM CHARGING_PROFILES"; auto select_stmt = this->database->new_statement(sql); @@ -215,12 +230,18 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithProfileData_InsertNewProfile) { } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithProfileData_DeleteRemovesSpecifiedProfiles) { - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 2, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); + ChargingProfile profile1; + profile1.id = 1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile1); + ChargingProfile profile2; + profile2.id = 2; + profile2.stackLevel = 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile2); auto sql = "SELECT COUNT(*) FROM CHARGING_PROFILES"; @@ -243,12 +264,18 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithProfileData_DeleteRemovesSpecif } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithProfileData_DeleteAllRemovesAllProfiles) { - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); - this->database_handler.insert_or_update_charging_profile( - 1, ChargingProfile{ - .id = 2, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}); + ChargingProfile profile1; + profile1.id = 1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile1); + ChargingProfile profile2; + profile2.id = 2; + profile2.stackLevel = 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(1, profile2); auto sql = "SELECT COUNT(*) FROM CHARGING_PROFILES"; @@ -289,8 +316,11 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithNoProfileData_DeleteAllDoesNotF } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithSingleProfileData_LoadsChargingProfile) { - auto profile = ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile profile; + profile.id = 1; + profile.stackLevel = 1; + profile.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(1, profile); auto sut = this->database_handler.get_all_charging_profiles_group_by_evse(); @@ -307,17 +337,26 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithSingleProfileData_LoadsCharging } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithMultipleProfileSameEvse_LoadsChargingProfile) { - auto p1 = ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p1; + p1.id = 1; + p1.stackLevel = 1; + p1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(1, p1); - auto p2 = ChargingProfile{ - .id = 2, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p2; + p2.id = 2; + p2.stackLevel = 2; + p2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p2.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(1, p2); - auto p3 = ChargingProfile{ - .id = 3, .stackLevel = 3, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p3; + p3.id = 3; + p3.stackLevel = 3; + p3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p3.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(1, p3); auto sut = this->database_handler.get_all_charging_profiles_group_by_evse(); @@ -336,27 +375,46 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithMultipleProfileSameEvse_LoadsCh } TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithMultipleProfileDiffEvse_LoadsChargingProfile) { - auto p1 = ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p1; + p1.id = 1; + p1.stackLevel = 1; + p1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(1, p1); - auto p2 = - ChargingProfile{.id = 2, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; + ChargingProfile p2; + p2.id = 2; + p2.stackLevel = 2; + p2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + p2.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(1, p2); - auto p3 = ChargingProfile{ - .id = 3, .stackLevel = 3, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p3; + p3.id = 3; + p3.stackLevel = 3; + p3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p3.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(2, p3); - auto p4 = - ChargingProfile{.id = 4, .stackLevel = 4, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; + + ChargingProfile p4; + p4.id = 4; + p4.stackLevel = 4; + p4.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + p4.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(2, p4); - auto p5 = ChargingProfile{ - .id = 5, .stackLevel = 5, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p5; + p5.id = 5; + p5.stackLevel = 5; + p5.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p5.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(3, p5); - auto p6 = - ChargingProfile{.id = 6, .stackLevel = 6, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; + ChargingProfile p6; + p6.id = 6; + p6.stackLevel = 6; + p6.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + p6.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(3, p6); auto sut = this->database_handler.get_all_charging_profiles_group_by_evse(); @@ -385,10 +443,17 @@ TEST_F(DatabaseHandlerTest, KO1_FR27_DatabaseWithMultipleProfileDiffEvse_LoadsCh } TEST_F(DatabaseHandlerTest, GetAllChargingProfiles_GetsAllProfiles) { - auto profile1 = ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - auto profile2 = - ChargingProfile{.id = 2, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; + ChargingProfile profile1; + profile1.id = 1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + + ChargingProfile profile2; + profile2.id = 2; + profile2.stackLevel = 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID + 1, profile2); @@ -401,10 +466,17 @@ TEST_F(DatabaseHandlerTest, GetAllChargingProfiles_GetsAllProfiles) { } TEST_F(DatabaseHandlerTest, GetChargingProfilesForEvse_GetsProfilesForEVSE) { - auto profile1 = ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - auto profile2 = - ChargingProfile{.id = 2, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; + ChargingProfile profile1; + profile1.id = 1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + + ChargingProfile profile2; + profile2.id = 2; + profile2.stackLevel = 2; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); @@ -417,10 +489,17 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesForEvse_GetsProfilesForEVSE) { } TEST_F(DatabaseHandlerTest, GetChargingProfilesForEvse_DoesNotGetProfilesOnOtherEVSE) { - auto profile1 = ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - auto profile2 = - ChargingProfile{.id = 2, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; + ChargingProfile profile1; + profile1.id = 1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + + ChargingProfile profile2; + profile2.id = 2; + profile2.stackLevel = 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID + 1, profile2); @@ -435,12 +514,18 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesForEvse_DoesNotGetProfilesOnOther TEST_F(DatabaseHandlerTest, DeleteChargingProfileByTransactionId_DeletesByTransactionId) { const auto profile_id = 1; const auto transaction_id = uuid(); - auto profile1 = ChargingProfile{ - .id = profile_id, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - auto profile2 = ChargingProfile{.id = profile_id + 1, - .stackLevel = 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile, - .transactionId = transaction_id}; + ChargingProfile profile1; + profile1.id = profile_id; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + + ChargingProfile profile2; + profile2.id = profile_id + 1; + profile2.stackLevel = 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + profile2.transactionId = transaction_id; this->database_handler.insert_or_update_charging_profile(1, profile1); this->database_handler.insert_or_update_charging_profile(1, profile2); @@ -462,8 +547,11 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_WhenGivenProfi const ClearChargingProfile clear_criteria; const auto profile_id = 1; - auto p1 = ChargingProfile{ - .id = profile_id, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p1; + p1.id = profile_id; + p1.stackLevel = 1; + p1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, p1); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -480,8 +568,11 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_WhenNotGivenPr const ClearChargingProfile clear_criteria; const auto profile_id = 1; - auto p1 = ChargingProfile{ - .id = profile_id, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p1; + p1.id = profile_id; + p1.stackLevel = 1; + p1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, p1); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -497,10 +588,16 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_WhenNotGivenPr TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_WhenNotGivenCriteria_DeletesAllProfiles) { const auto profile_id = 1; - auto p1 = ChargingProfile{ - .id = profile_id, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - auto p2 = ChargingProfile{ - .id = profile_id + 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; + ChargingProfile p1; + p1.id = profile_id; + p1.stackLevel = 1; + p1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + ChargingProfile p2; + p2.id = profile_id + 1; + p2.stackLevel = 1; + p2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + p2.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, p1); this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID + 1, p2); @@ -518,13 +615,16 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_WhenAllCriteri const auto purpose = ChargingProfilePurposeEnum::TxDefaultProfile; const auto stack_level = 1; - const ClearChargingProfile clear_criteria = { - .evseId = DEFAULT_EVSE_ID, - .chargingProfilePurpose = purpose, - .stackLevel = stack_level, - }; + ClearChargingProfile clear_criteria; + clear_criteria.evseId = DEFAULT_EVSE_ID; + clear_criteria.chargingProfilePurpose = purpose; + clear_criteria.stackLevel = stack_level; - auto p1 = ChargingProfile{.id = 1, .stackLevel = stack_level, .chargingProfilePurpose = purpose}; + ChargingProfile p1; + p1.id = 1; + p1.stackLevel = stack_level; + p1.chargingProfilePurpose = purpose; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, p1); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -541,14 +641,16 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_UnknownPurpose const auto different_purpose = ChargingProfilePurposeEnum::TxProfile; const auto stack_level = 1; - const ClearChargingProfile clear_criteria = { - .evseId = DEFAULT_EVSE_ID, - .chargingProfilePurpose = different_purpose, - .stackLevel = stack_level, - }; + ClearChargingProfile clear_criteria; + clear_criteria.evseId = DEFAULT_EVSE_ID; + clear_criteria.chargingProfilePurpose = different_purpose; + clear_criteria.stackLevel = stack_level; - auto p1 = ChargingProfile{ - .id = 1, .stackLevel = stack_level, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile p1; + p1.id = 1; + p1.stackLevel = stack_level; + p1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, p1); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -567,13 +669,16 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_UnknownStackLe const auto purpose = ChargingProfilePurposeEnum::TxDefaultProfile; const auto different_stack_level = 2; - const ClearChargingProfile clear_criteria = { - .evseId = DEFAULT_EVSE_ID, - .chargingProfilePurpose = purpose, - .stackLevel = different_stack_level, - }; + ClearChargingProfile clear_criteria; + clear_criteria.evseId = DEFAULT_EVSE_ID; + clear_criteria.chargingProfilePurpose = purpose; + clear_criteria.stackLevel = different_stack_level; - auto p1 = ChargingProfile{.id = 1, .stackLevel = 1, .chargingProfilePurpose = purpose}; + ChargingProfile p1; + p1.id = 1; + p1.stackLevel = 1; + p1.chargingProfilePurpose = purpose; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, p1); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -593,13 +698,16 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_UnknownEvseId_ const auto stack_level = 1; const auto different_evse_id = DEFAULT_EVSE_ID + 1; - const ClearChargingProfile clear_criteria = { - .evseId = different_evse_id, - .chargingProfilePurpose = purpose, - .stackLevel = stack_level, - }; + ClearChargingProfile clear_criteria; + clear_criteria.evseId = different_evse_id; + clear_criteria.chargingProfilePurpose = purpose; + clear_criteria.stackLevel = stack_level; - auto p1 = ChargingProfile{.id = 1, .stackLevel = stack_level, .chargingProfilePurpose = purpose}; + ChargingProfile p1; + p1.id = 1; + p1.stackLevel = stack_level; + p1.chargingProfilePurpose = purpose; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, p1); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -618,13 +726,16 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_DoesNotDeleteE const auto purpose = ChargingProfilePurposeEnum::ChargingStationExternalConstraints; const auto stack_level = 1; - const ClearChargingProfile clear_criteria = { - .evseId = STATION_WIDE_ID, - .chargingProfilePurpose = purpose, - .stackLevel = stack_level, - }; + ClearChargingProfile clear_criteria; + clear_criteria.evseId = STATION_WIDE_ID; + clear_criteria.chargingProfilePurpose = purpose; + clear_criteria.stackLevel = stack_level; - auto p1 = ChargingProfile{.id = 1, .stackLevel = stack_level, .chargingProfilePurpose = purpose}; + ChargingProfile p1; + p1.id = 1; + p1.stackLevel = stack_level; + p1.chargingProfilePurpose = purpose; + p1.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, p1); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -639,8 +750,11 @@ TEST_F(DatabaseHandlerTest, ClearChargingProfilesMatchingCriteria_DoesNotDeleteE } TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_NoCriteriaReturnsAll) { - auto profile = ChargingProfile{ - .id = 1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile profile; + profile.id = 1; + profile.stackLevel = 1; + profile.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile); auto profiles = this->database_handler.get_all_charging_profiles(); @@ -653,14 +767,18 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_NoCriteriaReturn TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_NoMatchingIdsReturnsEmpty) { auto profile_id = 1; - auto profile = ChargingProfile{ - .id = profile_id, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfile profile; + profile.id = profile_id; + profile.stackLevel = 1; + profile.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile.chargingProfileKind = ChargingProfileKindEnum::Absolute; this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 1); - ChargingProfileCriterion criteria = {.chargingProfileId = std::vector{2}}; + ChargingProfileCriterion criteria; + criteria.chargingProfileId = std::vector{2}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -670,19 +788,28 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_NoMatchingIdsRet TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingIdReturnsSingleProfile) { auto profile_id_1 = 1; - auto profile_1 = ChargingProfile{ - .id = profile_id_1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{ - .id = profile_id_2, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = 2; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 2); - ChargingProfileCriterion criteria = {.chargingProfileId = std::vector{profile_id_1}}; + ChargingProfileCriterion criteria; + criteria.chargingProfileId = std::vector{profile_id_1}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -692,19 +819,26 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingIdReturn TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingIdsReturnsMultipleProfiles) { auto profile_id_1 = 1; - auto profile_1 = ChargingProfile{ - .id = profile_id_1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{ - .id = profile_id_2, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = 2; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 2); - ChargingProfileCriterion criteria = {.chargingProfileId = std::vector{profile_id_1, profile_id_2}}; + ChargingProfileCriterion criteria; + criteria.chargingProfileId = std::vector{profile_id_1, profile_id_2}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -714,24 +848,34 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingIdsRetur TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingChargingProfilePurposeReturnsProfiles) { auto profile_id_1 = 1; - auto profile_1 = ChargingProfile{ - .id = profile_id_1, .stackLevel = 1, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = 1; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{ - .id = profile_id_2, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = 2; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{ - .id = profile_id_3, .stackLevel = 2, .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = 2; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = {.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; + ChargingProfileCriterion criteria; + criteria.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -742,27 +886,34 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingCharging TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingStackLevelReturnsProfiles) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{.id = profile_id_3, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = stack_level + 1; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = {.stackLevel = 2}; + ChargingProfileCriterion criteria; + criteria.stackLevel = 2; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -773,27 +924,34 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingStackLev TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingProfileSourceReturnsProfiles) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{.id = profile_id_3, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3, ChargingLimitSourceEnum::EMS); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = stack_level + 1; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3, ChargingLimitSourceEnum::EMS); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = {.chargingLimitSource = {{ChargingLimitSourceEnum::CSO}}}; + ChargingProfileCriterion criteria; + criteria.chargingLimitSource = {{ChargingLimitSourceEnum::CSO}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -804,28 +962,34 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingProfileS TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingProfileSourcesReturnsProfiles) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{.id = profile_id_3, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3, ChargingLimitSourceEnum::EMS); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = stack_level + 1; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3, ChargingLimitSourceEnum::EMS); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = { - .chargingLimitSource = {{ChargingLimitSourceEnum::CSO, ChargingLimitSourceEnum::EMS}}}; + ChargingProfileCriterion criteria; + criteria.chargingLimitSource = {{ChargingLimitSourceEnum::CSO, ChargingLimitSourceEnum::EMS}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -836,29 +1000,36 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingProfileS TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_AllCriteriaSetReturnsOne) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{.id = profile_id_3, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3, ChargingLimitSourceEnum::CSO); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = stack_level + 1; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3, ChargingLimitSourceEnum::CSO); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = {.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile, - .stackLevel = 2, - .chargingLimitSource = {{ChargingLimitSourceEnum::CSO}}}; + ChargingProfileCriterion criteria; + criteria.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + criteria.stackLevel = 2; + criteria.chargingLimitSource = {{ChargingLimitSourceEnum::CSO}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -869,29 +1040,36 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_AllCriteriaSetRe TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_AllCriteriaSetReturnsNone) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{.id = profile_id_3, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3, ChargingLimitSourceEnum::CSO); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = stack_level + 1; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3, ChargingLimitSourceEnum::CSO); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = {.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile, - .stackLevel = 2, - .chargingLimitSource = {{ChargingLimitSourceEnum::EMS}}}; + ChargingProfileCriterion criteria; + criteria.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + criteria.stackLevel = 2; + criteria.chargingLimitSource = {{ChargingLimitSourceEnum::EMS}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -902,29 +1080,36 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_AllCriteriaSetRe TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_AllCriteriaSetReturnsNothing) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{.id = profile_id_3, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3, ChargingLimitSourceEnum::CSO); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = stack_level + 1; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3, ChargingLimitSourceEnum::CSO); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = {.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile, - .stackLevel = 2, - .chargingLimitSource = {{ChargingLimitSourceEnum::EMS}}}; + ChargingProfileCriterion criteria; + criteria.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + criteria.stackLevel = 2; + criteria.chargingLimitSource = {{ChargingLimitSourceEnum::EMS}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(std::nullopt, criteria); @@ -935,31 +1120,36 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_AllCriteriaSetRe TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteriaAndEvseId0_AllCriteriaSetReturnsNone) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile2); auto profile_id_3 = 3; - auto profile_3 = ChargingProfile{.id = profile_id_3, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_3, ChargingLimitSourceEnum::CSO); + ChargingProfile profile3; + profile3.id = profile_id_3; + profile3.stackLevel = stack_level + 1; + profile3.chargingProfilePurpose = ChargingProfilePurposeEnum::TxProfile; + profile3.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile3, ChargingLimitSourceEnum::CSO); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 3); - ChargingProfileCriterion criteria = { - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile, - .stackLevel = 2, - .chargingLimitSource = {{ChargingLimitSourceEnum::CSO}}, - }; + ChargingProfileCriterion criteria; + criteria.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + criteria.stackLevel = 2; + criteria.chargingLimitSource = {{ChargingLimitSourceEnum::CSO}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(0, criteria); @@ -970,102 +1160,115 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteriaAndEvseId0_AllCri TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_IfProfileIdAndEvseIdGiven_ReturnsMatchingProfile) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile_2); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile2); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 2); - ChargingProfileCriterion criteria = { - .chargingProfileId = {{profile_id_1, profile_id_2}}, - }; + ChargingProfileCriterion criteria; + criteria.chargingProfileId = {{profile_id_1, profile_id_2}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria(STATION_WIDE_ID, criteria); EXPECT_EQ(sut.size(), 1); - EXPECT_THAT(sut[0].profile, testing::Eq(profile_2)); + EXPECT_THAT(sut[0].profile, testing::Eq(profile2)); } TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingProfileIds_ReturnsEVSEAndSource) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile_2, ChargingLimitSourceEnum::EMS); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile2, ChargingLimitSourceEnum::EMS); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 2); - ChargingProfileCriterion criteria = { - .chargingProfileId = {{profile_id_1, profile_id_2}}, - }; + ChargingProfileCriterion criteria; + criteria.chargingProfileId = {{profile_id_1, profile_id_2}}; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria({}, criteria); EXPECT_EQ(sut.size(), 2); - EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile_1, DEFAULT_EVSE_ID, ChargingLimitSourceEnum::CSO))); - EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile_2, STATION_WIDE_ID, ChargingLimitSourceEnum::EMS))); + EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile1, DEFAULT_EVSE_ID, ChargingLimitSourceEnum::CSO))); + EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile2, STATION_WIDE_ID, ChargingLimitSourceEnum::EMS))); } TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_MatchingCriteria_ReturnsEVSEAndSource) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile_2, ChargingLimitSourceEnum::EMS); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile2, ChargingLimitSourceEnum::EMS); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 2); - ChargingProfileCriterion criteria = { - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile, - }; + ChargingProfileCriterion criteria; + criteria.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; std::vector sut = this->database_handler.get_charging_profiles_matching_criteria({}, criteria); EXPECT_EQ(sut.size(), 2); - EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile_1, DEFAULT_EVSE_ID, ChargingLimitSourceEnum::CSO))); - EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile_2, STATION_WIDE_ID, ChargingLimitSourceEnum::EMS))); + EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile1, DEFAULT_EVSE_ID, ChargingLimitSourceEnum::CSO))); + EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile2, STATION_WIDE_ID, ChargingLimitSourceEnum::EMS))); } TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_OnlyEVSEIDSet_ReturnsProfileOnEVSE) { auto profile_id_1 = 1; auto stack_level = 1; - auto profile_1 = ChargingProfile{.id = profile_id_1, - .stackLevel = stack_level, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile_1); + ChargingProfile profile1; + profile1.id = profile_id_1; + profile1.stackLevel = stack_level; + profile1.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile1.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(DEFAULT_EVSE_ID, profile1); auto profile_id_2 = 2; - auto profile_2 = ChargingProfile{.id = profile_id_2, - .stackLevel = stack_level + 1, - .chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile}; - this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile_2, ChargingLimitSourceEnum::EMS); + ChargingProfile profile2; + profile2.id = profile_id_2; + profile2.stackLevel = stack_level + 1; + profile2.chargingProfilePurpose = ChargingProfilePurposeEnum::TxDefaultProfile; + profile2.chargingProfileKind = ChargingProfileKindEnum::Absolute; + this->database_handler.insert_or_update_charging_profile(STATION_WIDE_ID, profile2, ChargingLimitSourceEnum::EMS); auto profiles = this->database_handler.get_all_charging_profiles(); EXPECT_EQ(profiles.size(), 2); @@ -1074,5 +1277,5 @@ TEST_F(DatabaseHandlerTest, GetChargingProfilesMatchingCriteria_OnlyEVSEIDSet_Re this->database_handler.get_charging_profiles_matching_criteria(DEFAULT_EVSE_ID, {}); EXPECT_EQ(sut.size(), 1); - EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile_1, DEFAULT_EVSE_ID, ChargingLimitSourceEnum::CSO))); + EXPECT_THAT(sut, testing::Contains(testing::FieldsAre(profile1, DEFAULT_EVSE_ID, ChargingLimitSourceEnum::CSO))); } \ No newline at end of file diff --git a/tests/lib/ocpp/v201/test_device_model.cpp b/tests/lib/ocpp/v201/test_device_model.cpp index c16092bb5..2ce6e2823 100644 --- a/tests/lib/ocpp/v201/test_device_model.cpp +++ b/tests/lib/ocpp/v201/test_device_model.cpp @@ -48,29 +48,31 @@ TEST_F(DeviceModelTest, test_allow_zero) { TEST_F(DeviceModelTest, test_component_as_key_in_map) { std::map components_to_ints; - const Component base_comp = {.name = "Foo"}; + Component base_comp; + base_comp.name = "Foo"; components_to_ints[base_comp] = 1; - const Component different_instance_comp = { - .name = "Foo", - .instance = "bar", - }; - const Component different_evse_comp = { - .name = "Foo", - .evse = EVSE{.id = 0}, - }; - const Component different_evse_and_instance_comp = { - .name = "Foo", - .evse = EVSE{.id = 0}, - .instance = "bar", - }; - const Component comp_with_custom_data = { - .name = "Foo", - .customData = json::object({{"vendorId", "Baz"}}), - }; - const Component different_name_comp = { - .name = "Bar", - }; + Component different_instance_comp; + different_instance_comp.name = "Foo"; + different_instance_comp.instance = "bar"; + + Component different_evse_comp; + different_evse_comp.name = "Foo"; + EVSE evse0; + evse0.id = 0; + different_evse_comp.evse = evse0; + + Component different_evse_and_instance_comp; + different_evse_and_instance_comp.name = "Foo"; + different_evse_and_instance_comp.evse = evse0; + different_evse_and_instance_comp.instance = "bar"; + + Component comp_with_custom_data; + comp_with_custom_data.name = "Foo"; + comp_with_custom_data.customData = json::object({{"vendorId", "Baz"}}); + + Component different_name_comp; + different_name_comp.name = "Bar"; EXPECT_EQ(components_to_ints.find(base_comp)->second, 1); EXPECT_EQ(components_to_ints.find(different_instance_comp), components_to_ints.end()); @@ -83,16 +85,21 @@ TEST_F(DeviceModelTest, test_component_as_key_in_map) { TEST_F(DeviceModelTest, test_set_monitors) { std::vector requests; - const EVSE evse = {.id = 2, .connectorId = 3}; + EVSE evse; + evse.id = 2; + evse.connectorId = 3; - const Component component1 = { - .name = "UnitTestCtrlr", - .evse = evse, - }; - const Component component2 = {.name = "AlignedDataCtrlr"}; + Component component1; + component1.name = "UnitTestCtrlr"; + component1.evse = evse; + + Component component2; + component2.name = "AlignedDataCtrlr"; - const Variable variable_comp1 = {.name = "UnitTestPropertyAName"}; - const Variable variable_comp2 = {.name = "Interval"}; + Variable variable_comp1; + variable_comp1.name = "UnitTestPropertyAName"; + Variable variable_comp2; + variable_comp2.name = "Interval"; std::vector components = { {component1, std::nullopt, variable_comp1}, @@ -109,16 +116,18 @@ TEST_F(DeviceModelTest, test_set_monitors) { dm->clear_monitors(ids, true); } - const SetMonitoringData req_one{.value = 0.0, - .type = MonitorEnum::PeriodicClockAligned, - .severity = 7, - .component = component1, - .variable = variable_comp1}; - const SetMonitoringData req_two{.value = 4.579, - .type = MonitorEnum::UpperThreshold, - .severity = 3, - .component = component2, - .variable = variable_comp2}; + SetMonitoringData req_one; + req_one.value = 0.0; + req_one.type = MonitorEnum::PeriodicClockAligned; + req_one.severity = 7; + req_one.component = component1; + req_one.variable = variable_comp1; + SetMonitoringData req_two; + req_two.value = 4.579; + req_two.type = MonitorEnum::UpperThreshold; + req_two.severity = 3; + req_two.component = component2; + req_two.variable = variable_comp2; requests.push_back(req_one); requests.push_back(req_two); @@ -138,16 +147,21 @@ TEST_F(DeviceModelTest, test_get_monitors) { MonitoringCriterionEnum::ThresholdMonitoring, }; - const EVSE evse = {.id = 2, .connectorId = 3}; + EVSE evse; + evse.id = 2; + evse.connectorId = 3; - const Component component1 = { - .name = "UnitTestCtrlr", - .evse = evse, - }; - const Component component2 = {.name = "AlignedDataCtrlr"}; + Component component1; + component1.name = "UnitTestCtrlr"; + component1.evse = evse; + + Component component2; + component2.name = "AlignedDataCtrlr"; - const Variable variable_comp1 = {.name = "UnitTestPropertyAName"}; - const Variable variable_comp2 = {.name = "Interval"}; + Variable variable_comp1; + variable_comp1.name = "UnitTestPropertyAName"; + Variable variable_comp2; + variable_comp2.name = "Interval"; std::vector components = { {component1, std::nullopt, variable_comp1}, @@ -161,11 +175,12 @@ TEST_F(DeviceModelTest, test_get_monitors) { auto monitor1 = results[0].variableMonitoring[0]; // Valued used above - const SetMonitoringData req_one{.value = 0.0, - .type = MonitorEnum::PeriodicClockAligned, - .severity = 7, - .component = component1, - .variable = variable_comp1}; + SetMonitoringData req_one; + req_one.value = 0.0; + req_one.type = MonitorEnum::PeriodicClockAligned; + req_one.severity = 7; + req_one.component = component1; + req_one.variable = variable_comp1; ASSERT_EQ(monitor1.severity, 7); ASSERT_EQ(monitor1.type, MonitorEnum::PeriodicClockAligned); @@ -178,16 +193,20 @@ TEST_F(DeviceModelTest, test_clear_monitors) { MonitoringCriterionEnum::ThresholdMonitoring, }; - const EVSE evse = {.id = 2, .connectorId = 3}; + EVSE evse; + evse.id = 2; + evse.connectorId = 3; - const Component component1 = { - .name = "UnitTestCtrlr", - .evse = evse, - }; - const Component component2 = {.name = "AlignedDataCtrlr"}; + Component component1; + component1.name = "UnitTestCtrlr"; + component1.evse = evse; + Component component2; + component2.name = "AlignedDataCtrlr"; - const Variable variable_comp1 = {.name = "UnitTestPropertyAName"}; - const Variable variable_comp2 = {.name = "Interval"}; + Variable variable_comp1; + variable_comp1.name = "UnitTestPropertyAName"; + Variable variable_comp2; + variable_comp2.name = "Interval"; std::vector components = { {component1, std::nullopt, variable_comp1}, @@ -195,16 +214,18 @@ TEST_F(DeviceModelTest, test_clear_monitors) { }; // Insert some monitors that are hard-wired - const SetMonitoringData hardwired_one{.value = 0.0, - .type = MonitorEnum::PeriodicClockAligned, - .severity = 5, - .component = component1, - .variable = variable_comp1}; - const SetMonitoringData hardwired_two{.value = 8.579, - .type = MonitorEnum::UpperThreshold, - .severity = 2, - .component = component2, - .variable = variable_comp2}; + SetMonitoringData hardwired_one; + hardwired_one.value = 0.0; + hardwired_one.type = MonitorEnum::PeriodicClockAligned; + hardwired_one.severity = 5; + hardwired_one.component = component1; + hardwired_one.variable = variable_comp1; + SetMonitoringData hardwired_two; + hardwired_two.value = 8.579; + hardwired_two.type = MonitorEnum::UpperThreshold; + hardwired_two.severity = 2; + hardwired_two.component = component2; + hardwired_two.variable = variable_comp2; std::vector requests; requests.push_back(hardwired_one); diff --git a/tests/lib/ocpp/v201/test_ocsp_updater.cpp b/tests/lib/ocpp/v201/test_ocsp_updater.cpp index 88dd8958c..2c830e081 100644 --- a/tests/lib/ocpp/v201/test_ocsp_updater.cpp +++ b/tests/lib/ocpp/v201/test_ocsp_updater.cpp @@ -35,64 +35,71 @@ class OcspUpdaterTest : public ::testing::Test { this->evse_security = std::make_shared(); this->status_update = [this](auto request) { return this->charge_point->get_certificate_status(request); }; - this->example_ocsp_data.push_back(OCSPRequestData{.hashAlgorithm = HashAlgorithmEnumType::SHA256, - .issuerNameHash = "issuerHash1", - .issuerKeyHash = "issuerKey1", - .serialNumber = "serial1", - .responderUrl = "responder1"}); - this->example_ocsp_data.push_back(OCSPRequestData{.hashAlgorithm = HashAlgorithmEnumType::SHA384, - .issuerNameHash = "issuerHash2", - .issuerKeyHash = "issuerKey2", - .serialNumber = "serial2", - .responderUrl = "responder2"}); - this->example_ocsp_data.push_back(OCSPRequestData{.hashAlgorithm = HashAlgorithmEnumType::SHA512, - .issuerNameHash = "issuerHash3", - .issuerKeyHash = "issuerKey3", - .serialNumber = "serial3", - .responderUrl = "responder3"}); - - this->example_hash_data.push_back(CertificateHashDataType{ - .hashAlgorithm = HashAlgorithmEnumType::SHA256, - .issuerNameHash = "issuerHash1", - .issuerKeyHash = "issuerKey1", - .serialNumber = "serial1", - }); - this->example_hash_data.push_back(CertificateHashDataType{ - .hashAlgorithm = HashAlgorithmEnumType::SHA384, - .issuerNameHash = "issuerHash2", - .issuerKeyHash = "issuerKey2", - .serialNumber = "serial2", - }); - this->example_hash_data.push_back(CertificateHashDataType{ - .hashAlgorithm = HashAlgorithmEnumType::SHA512, - .issuerNameHash = "issuerHash3", - .issuerKeyHash = "issuerKey3", - .serialNumber = "serial3", - }); + OCSPRequestData ocsp1; + ocsp1.hashAlgorithm = HashAlgorithmEnumType::SHA256; + ocsp1.issuerNameHash = "issuerHash1"; + ocsp1.issuerKeyHash = "issuerKey1"; + ocsp1.serialNumber = "serial1"; + ocsp1.responderUrl = "responder1"; + this->example_ocsp_data.push_back(ocsp1); + + OCSPRequestData ocsp2; + ocsp2.hashAlgorithm = HashAlgorithmEnumType::SHA384; + ocsp2.issuerNameHash = "issuerHash2"; + ocsp2.issuerKeyHash = "issuerKey2"; + ocsp2.serialNumber = "serial2"; + ocsp2.responderUrl = "responder2"; + this->example_ocsp_data.push_back(ocsp2); + + OCSPRequestData ocsp3; + ocsp3.hashAlgorithm = HashAlgorithmEnumType::SHA512; + ocsp3.issuerNameHash = "issuerHash3"; + ocsp3.issuerKeyHash = "issuerKey3"; + ocsp3.serialNumber = "serial3"; + ocsp3.responderUrl = "responder3"; + this->example_ocsp_data.push_back(ocsp3); + + CertificateHashDataType certificate_hash_data1; + certificate_hash_data1.hashAlgorithm = HashAlgorithmEnumType::SHA256; + certificate_hash_data1.issuerNameHash = "issuerHash1"; + certificate_hash_data1.issuerKeyHash = "issuerKey1"; + certificate_hash_data1.serialNumber = "serial1"; + this->example_hash_data.push_back(certificate_hash_data1); + + CertificateHashDataType certificate_hash_data2; + certificate_hash_data2.hashAlgorithm = HashAlgorithmEnumType::SHA384; + certificate_hash_data2.issuerNameHash = "issuerHash2"; + certificate_hash_data2.issuerKeyHash = "issuerKey2"; + certificate_hash_data2.serialNumber = "serial2"; + this->example_hash_data.push_back(certificate_hash_data2); + + CertificateHashDataType certificate_hash_data3; + certificate_hash_data3.hashAlgorithm = HashAlgorithmEnumType::SHA512; + certificate_hash_data3.issuerNameHash = "issuerHash3"; + certificate_hash_data3.issuerKeyHash = "issuerKey3"; + certificate_hash_data3.serialNumber = "serial3"; + this->example_hash_data.push_back(certificate_hash_data3); v201::GetCertificateStatusRequest example_get_cert_status_request_1; - example_get_cert_status_request_1.ocspRequestData = - v201::OCSPRequestData{.hashAlgorithm = v201::HashAlgorithmEnum::SHA256, - .issuerNameHash = "issuerHash1", - .issuerKeyHash = "issuerKey1", - .serialNumber = "serial1", - .responderURL = "responder1"}; + example_get_cert_status_request_1.ocspRequestData.hashAlgorithm = v201::HashAlgorithmEnum::SHA256; + example_get_cert_status_request_1.ocspRequestData.issuerNameHash = "issuerHash1"; + example_get_cert_status_request_1.ocspRequestData.issuerKeyHash = "issuerKey1"; + example_get_cert_status_request_1.ocspRequestData.serialNumber = "serial1"; + example_get_cert_status_request_1.ocspRequestData.responderURL = "responder1"; this->example_status_requests.push_back(example_get_cert_status_request_1); v201::GetCertificateStatusRequest example_get_cert_status_request_2; - example_get_cert_status_request_2.ocspRequestData = - v201::OCSPRequestData{.hashAlgorithm = v201::HashAlgorithmEnum::SHA384, - .issuerNameHash = "issuerHash2", - .issuerKeyHash = "issuerKey2", - .serialNumber = "serial2", - .responderURL = "responder2"}; + example_get_cert_status_request_2.ocspRequestData.hashAlgorithm = v201::HashAlgorithmEnum::SHA384; + example_get_cert_status_request_2.ocspRequestData.issuerNameHash = "issuerHash2"; + example_get_cert_status_request_2.ocspRequestData.issuerKeyHash = "issuerKey2"; + example_get_cert_status_request_2.ocspRequestData.serialNumber = "serial2"; + example_get_cert_status_request_2.ocspRequestData.responderURL = "responder2"; this->example_status_requests.push_back(example_get_cert_status_request_2); v201::GetCertificateStatusRequest example_get_cert_status_request_3; - example_get_cert_status_request_3.ocspRequestData = - v201::OCSPRequestData{.hashAlgorithm = v201::HashAlgorithmEnum::SHA512, - .issuerNameHash = "issuerHash3", - .issuerKeyHash = "issuerKey3", - .serialNumber = "serial3", - .responderURL = "responder3"}; + example_get_cert_status_request_3.ocspRequestData.hashAlgorithm = v201::HashAlgorithmEnum::SHA512; + example_get_cert_status_request_3.ocspRequestData.issuerNameHash = "issuerHash3"; + example_get_cert_status_request_3.ocspRequestData.issuerKeyHash = "issuerKey3"; + example_get_cert_status_request_3.ocspRequestData.serialNumber = "serial3"; + example_get_cert_status_request_3.ocspRequestData.responderURL = "responder3"; this->example_status_requests.push_back(example_get_cert_status_request_3); } diff --git a/tests/lib/ocpp/v201/test_profile.cpp b/tests/lib/ocpp/v201/test_profile.cpp index e677edaee..6960a2398 100644 --- a/tests/lib/ocpp/v201/test_profile.cpp +++ b/tests/lib/ocpp/v201/test_profile.cpp @@ -27,11 +27,13 @@ using std::chrono::minutes; using std::chrono::seconds; period_entry_t gen_pe(ocpp::DateTime start, ocpp::DateTime end, ChargingProfile profile, int period_at) { - return {.start = start, - .end = end, - .limit = profile.chargingSchedule.front().chargingSchedulePeriod[period_at].limit, - .stack_level = profile.stackLevel, - .charging_rate_unit = profile.chargingSchedule.front().chargingRateUnit}; + period_entry_t period_entry; + period_entry.start = start; + period_entry.end = end; + period_entry.limit = profile.chargingSchedule.front().chargingSchedulePeriod[period_at].limit; + period_entry.stack_level = profile.stackLevel; + period_entry.charging_rate_unit = profile.chargingSchedule.front().chargingRateUnit; + return period_entry; } const ChargingProfile absolute_profile = @@ -51,13 +53,17 @@ const ChargingProfile weekly_profile = const ChargingProfile weekly_profile_no_duration = SmartChargingTestUtils::get_charging_profile_from_file("singles/Recurring_Weekly_NoDuration_301.json"); -CompositeSchedule DEFAULT_SCHEDULE = { - .chargingSchedulePeriod = {}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, -}; +CompositeSchedule generate_default_schedule() { + CompositeSchedule schedule; + schedule.chargingSchedulePeriod = {}; + schedule.evseId = EVSEID_NOT_SET; + schedule.duration = 600; + schedule.scheduleStart = dt("12:00"); + schedule.chargingRateUnit = ChargingRateUnitEnum::A; + return schedule; +} + +CompositeSchedule DEFAULT_SCHEDULE = generate_default_schedule(); class ChargingProfileType_Param_Test : public ::testing::TestWithParam, @@ -240,11 +246,12 @@ TEST_P(CalculateProfileEntryType_Param_Test, CalculateProfileEntry_Positive) { std::vector period_entries = calculate_profile_entry(now, end, session_start, profile, period_index); - period_entry_t expected_entry{.start = expected_start, - .end = expected_end, - .limit = profile.chargingSchedule.front().chargingSchedulePeriod[period_index].limit, - .stack_level = profile.stackLevel, - .charging_rate_unit = profile.chargingSchedule.front().chargingRateUnit}; + period_entry_t expected_entry; + expected_entry.start = expected_start; + expected_entry.end = expected_end; + expected_entry.limit = profile.chargingSchedule.front().chargingSchedulePeriod[period_index].limit; + expected_entry.stack_level = profile.stackLevel; + expected_entry.charging_rate_unit = profile.chargingSchedule.front().chargingRateUnit; for (period_entry_t pet : period_entries) { EVLOG_debug << ">>> " << pet; @@ -257,12 +264,12 @@ TEST_P(CalculateProfileEntryType_Param_Test, CalculateProfileEntry_Positive) { } else { period_entry_t second_entry = period_entries.at(1); - period_entry_t expected_second_entry{ - .start = expected_2nd_entry_start.value(), - .end = expected_2nd_entry_end.value(), - .limit = profile.chargingSchedule.front().chargingSchedulePeriod[period_index].limit, - .stack_level = profile.stackLevel, - .charging_rate_unit = profile.chargingSchedule.front().chargingRateUnit}; + period_entry_t expected_second_entry; + expected_second_entry.start = expected_2nd_entry_start.value(); + expected_second_entry.end = expected_2nd_entry_end.value(); + expected_second_entry.limit = profile.chargingSchedule.front().chargingSchedulePeriod[period_index].limit; + expected_second_entry.stack_level = profile.stackLevel; + expected_second_entry.charging_rate_unit = profile.chargingSchedule.front().chargingRateUnit; EVLOG_debug << " second_entry> " << second_entry; EVLOG_debug << "expected_second_entry> " << expected_second_entry; @@ -334,18 +341,20 @@ TEST_P(CalculateProfileEntryType_NegativeBoundary_Param_Test, CalculateProfileEn } TEST(OCPPTypesTest, PeriodEntry_Equality) { - period_entry_t actual_entry{.start = dt("2T08:45"), - .end = dt("3T08:00"), - .limit = absolute_profile.chargingSchedule.front().chargingSchedulePeriod[0].limit, - .stack_level = absolute_profile.stackLevel, - .charging_rate_unit = absolute_profile.chargingSchedule.front().chargingRateUnit}; + period_entry_t actual_entry; + actual_entry.start = dt("2T08:45"); + actual_entry.end = dt("3T08:00"); + actual_entry.limit = absolute_profile.chargingSchedule.front().chargingSchedulePeriod[0].limit; + actual_entry.stack_level = absolute_profile.stackLevel; + actual_entry.charging_rate_unit = absolute_profile.chargingSchedule.front().chargingRateUnit; period_entry_t same_entry = actual_entry; - period_entry_t different_entry{.start = dt("3T08:00"), - .end = dt("3T08:00"), - .limit = absolute_profile.chargingSchedule.front().chargingSchedulePeriod[0].limit, - .stack_level = absolute_profile.stackLevel, - .charging_rate_unit = absolute_profile.chargingSchedule.front().chargingRateUnit}; + period_entry_t different_entry; + different_entry.start = dt("3T08:00"); + different_entry.end = dt("3T08:00"); + different_entry.limit = absolute_profile.chargingSchedule.front().chargingSchedulePeriod[0].limit; + different_entry.stack_level = absolute_profile.stackLevel; + different_entry.charging_rate_unit = absolute_profile.chargingSchedule.front().chargingRateUnit; ASSERT_EQ(actual_entry, same_entry); ASSERT_NE(actual_entry, different_entry); @@ -558,14 +567,14 @@ TEST(OCPPTypesTest, CalculateProfile_RelativeLimited) { } TEST(OCPPTypesTest, ChargingSchedulePeriod_Equality) { - ChargingSchedulePeriod period1 = ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = NO_LIMIT_SPECIFIED, - }; - ChargingSchedulePeriod period2 = ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = NO_LIMIT_SPECIFIED, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = NO_LIMIT_SPECIFIED; + + ChargingSchedulePeriod period2; + period2.startPeriod = 0; + period2.limit = NO_LIMIT_SPECIFIED; + ASSERT_EQ(period1, period1); ASSERT_EQ(period1, period2); @@ -580,10 +589,9 @@ TEST(OCPPTypesTest, ChargingSchedulePeriod_Equality) { period1.limit = 1; ASSERT_NE(period1, period2); - period2 = ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = 1, - }; + period2.startPeriod = 0; + period2.limit = 1; + ASSERT_EQ(period1, period2); // Optional phases @@ -602,24 +610,26 @@ TEST(OCPPTypesTest, ChargingSchedulePeriod_Equality) { } TEST(OCPPTypesTest, ChargingSchedule_Equality) { - std::vector periods = {ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = 10, - }, - ChargingSchedulePeriod{ - .startPeriod = 100, - .limit = 20, - }}; - ChargingSchedule schedule1 = ChargingSchedule{ - .id = 0, - .chargingSchedulePeriod = periods, - .duration = std::chrono::duration_cast(minutes(10)).count(), - }; - ChargingSchedule schedule2 = ChargingSchedule{ - .id = 0, - .chargingSchedulePeriod = {periods.at(0)}, - .duration = std::chrono::duration_cast(minutes(10)).count(), - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 10; + ChargingSchedulePeriod period2; + period2.startPeriod = 100; + period2.limit = 20; + std::vector periods = {period1, period2}; + + ChargingSchedule schedule1; + schedule1.id = 0; + schedule1.chargingSchedulePeriod = periods; + schedule1.chargingRateUnit = ChargingRateUnitEnum::W; + schedule1.duration = std::chrono::duration_cast(minutes(10)).count(); + + ChargingSchedule schedule2; + schedule2.id = 0; + schedule2.chargingSchedulePeriod = {periods.at(0)}; + schedule2.chargingRateUnit = ChargingRateUnitEnum::W; + schedule2.duration = std::chrono::duration_cast(minutes(10)).count(); + ASSERT_NE(schedule1, schedule2); // Perios must match @@ -669,12 +679,15 @@ TEST(OCPPTypesTest, ChargingSchedule_Equality) { TEST(OCPPTypesTest, CalculateChargingSchedule_Empty) { std::vector combined_schedules{}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = NO_LIMIT_SPECIFIED}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A}; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = NO_LIMIT_SPECIFIED; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = dt("12:00"); + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, dt("12:00"), dt("12:10"), std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -687,17 +700,17 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_Exact) { DateTime end = dt("12:10"); std::vector combined_schedules{ {now, end, 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = 24.0, - .numberPhases = 3, - }}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = 24.0; + period.numberPhases = 3; + + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, ChargingRateUnitEnum::A, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -711,18 +724,19 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_ShortExact) { std::vector combined_schedules{{now, DateTime(end.to_time_point() - seconds(1)), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = 24.0, - .numberPhases = 3, - }, - ChargingSchedulePeriod{.startPeriod = 599, .limit = NO_LIMIT_SPECIFIED}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 24.0; + period1.numberPhases = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 599; + period2.limit = NO_LIMIT_SPECIFIED; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, ChargingRateUnitEnum::A, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -736,17 +750,17 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_LongExact) { std::vector combined_schedules{{DateTime(now.to_time_point() - seconds(1)), end, 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = 24.0, - .numberPhases = 3, - }}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = 24.0; + period.numberPhases = 3; + + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, ChargingRateUnitEnum::A, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -760,19 +774,23 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_AlmostExact) { std::vector combined_schedules{{DateTime(now.to_time_point() + seconds(1)), DateTime(end.to_time_point() - seconds(1)), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = NO_LIMIT_SPECIFIED}, - ChargingSchedulePeriod{ - .startPeriod = 1, - .limit = 24.0, - .numberPhases = 3, - }, - ChargingSchedulePeriod{.startPeriod = 599, .limit = NO_LIMIT_SPECIFIED}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = NO_LIMIT_SPECIFIED; + ChargingSchedulePeriod period2; + period2.startPeriod = 1; + period2.limit = 24.0; + period2.numberPhases = 3; + ChargingSchedulePeriod period3; + period3.startPeriod = 599; + period3.limit = NO_LIMIT_SPECIFIED; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -785,17 +803,16 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_SingleLong) { DateTime end = dt("12:10"); std::vector combined_schedules{ {dt("11:00"), dt("12:30"), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 1, - .limit = 24.0, - .numberPhases = 3, - }}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period; + period.startPeriod = 1; + period.limit = 24.0; + period.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -808,18 +825,19 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_SingleShort) { DateTime end = dt("12:10"); std::vector combined_schedules{ {dt("11:00"), dt("12:05"), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = 24.0, - .numberPhases = 3, - }, - ChargingSchedulePeriod{.startPeriod = 300, .limit = NO_LIMIT_SPECIFIED}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 24.0; + period1.numberPhases = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = NO_LIMIT_SPECIFIED; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -832,17 +850,17 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_SingleDelayedStartLong) { DateTime end = dt("12:10"); std::vector combined_schedules{ {dt("12:02"), dt("12:30"), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, - .limit = NO_LIMIT_SPECIFIED, - }, - ChargingSchedulePeriod{.startPeriod = 120, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = NO_LIMIT_SPECIFIED; + ChargingSchedulePeriod period2; + period2.startPeriod = 120, period2.limit = 24.0, period2.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -856,14 +874,20 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_OverlapStart) { std::vector combined_schedules{ {dt("12:05"), dt("13:00"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("11:30"), dt("12:30"), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 24.0, .numberPhases = 3}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 32.0, .numberPhases = 1}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 24.0; + period1.numberPhases = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 32.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -877,14 +901,20 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_OverlapEnd) { std::vector combined_schedules{ {dt("11:30"), dt("12:05"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("11:30"), dt("12:30"), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 32.0, .numberPhases = 1}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 32.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 24.0; + period2.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -898,15 +928,24 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_OverlapMiddle) { std::vector combined_schedules{ {dt("12:02"), dt("12:05"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("11:30"), dt("12:30"), 24.0, 3, std::nullopt, 1, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 24.0, .numberPhases = 3}, - ChargingSchedulePeriod{.startPeriod = 120, .limit = 32.0, .numberPhases = 1}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 24.0; + period1.numberPhases = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 120; + period2.limit = 32.0; + period2.numberPhases = 1; + ChargingSchedulePeriod period3; + period3.startPeriod = 300; + period3.limit = 24.0; + period3.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -920,13 +959,16 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_OverlapIgnore) { std::vector combined_schedules{ {dt("12:05"), dt("13:00"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("11:30"), dt("12:30"), 24.0, 3, std::nullopt, 31, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = 24.0; + period.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -940,14 +982,20 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_NoGapA) { std::vector combined_schedules{ {dt("11:50"), dt("12:05"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("12:05"), dt("12:30"), 24.0, 3, std::nullopt, 31, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 32.0, .numberPhases = 1}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 32.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 24.0; + period2.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -961,14 +1009,20 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_NoGapB) { std::vector combined_schedules{ {dt("12:05"), dt("12:30"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("11:50"), dt("12:05"), 24.0, 3, std::nullopt, 31, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 24.0, .numberPhases = 3}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 32.0, .numberPhases = 1}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 24.0; + period1.numberPhases = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 32.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -982,14 +1036,20 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_Overlap) { std::vector combined_schedules{ {dt("11:50"), dt("12:05"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("12:05"), dt("12:30"), 24.0, 3, std::nullopt, 31, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 32.0, .numberPhases = 1}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 32.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 24.0; + period2.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -1004,14 +1064,20 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_OverlapInverted) { std::vector combined_schedules{ {dt("12:05"), dt("12:30"), 32.0, 1, std::nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("11:50"), dt("12:05"), 24.0, 3, std::nullopt, 31, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 24.0, .numberPhases = 3}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 32.0, .numberPhases = 1}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 24.0; + period1.numberPhases = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 32.0; + period2.numberPhases = 1; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -1025,15 +1091,23 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_1SecondGap) { std::vector combined_schedules{ {dt("11:50"), DateTime{"2024-01-01T12:04:59Z"}, 32.0, 1, nullopt, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("12:05"), dt("12:30"), 24.0, 3, nullopt, 31, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{.startPeriod = 0, .limit = 32.0, .numberPhases = 1}, - ChargingSchedulePeriod{.startPeriod = 299, .limit = NO_LIMIT_SPECIFIED}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 32.0; + period1.numberPhases = 1; + ChargingSchedulePeriod period2; + period2.startPeriod = 299; + period2.limit = NO_LIMIT_SPECIFIED; + ChargingSchedulePeriod period3; + period3.startPeriod = 300; + period3.limit = 24.0; + period3.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -1047,16 +1121,24 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_WithPhaseToUse) { std::vector combined_schedules{ {dt("11:50"), DateTime{"2024-01-01T12:04:59Z"}, 32.0, 1, 3, 21, ChargingRateUnitEnum::A, std::nullopt}, {dt("12:05"), dt("12:30"), 24.0, 3, std::nullopt, 31, ChargingRateUnitEnum::A, std::nullopt}}; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, .limit = 32.0, .numberPhases = 1, .phaseToUse = 3}, - ChargingSchedulePeriod{.startPeriod = 299, .limit = NO_LIMIT_SPECIFIED}, - ChargingSchedulePeriod{.startPeriod = 300, .limit = 24.0, .numberPhases = 3}}, - .evseId = EVSEID_NOT_SET, - .duration = std::chrono::duration_cast(minutes(10)).count(), - .scheduleStart = now, - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 32.0; + period1.numberPhases = 1; + period1.phaseToUse = 3; + ChargingSchedulePeriod period2; + period2.startPeriod = 299; + period2.limit = NO_LIMIT_SPECIFIED; + ChargingSchedulePeriod period3; + period3.startPeriod = 300; + period3.limit = 24.0; + period3.numberPhases = 3; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period1, period2, period3}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = std::chrono::duration_cast(minutes(10)).count(); + expected.scheduleStart = now; + expected.chargingRateUnit = ChargingRateUnitEnum::A; CompositeSchedule actual = calculate_composite_schedule(combined_schedules, now, end, std::nullopt, DEFAULT_AND_MAX_NUMBER_PHASES, LOW_VOLTAGE); @@ -1065,15 +1147,16 @@ TEST(OCPPTypesTest, CalculateChargingSchedule_WithPhaseToUse) { } TEST(OCPPTypesTest, CalculateChargingScheduleCombined_Default) { - CompositeSchedule expected = { - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, .limit = DEFAULT_LIMIT_AMPS, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}}, - .evseId = EVSEID_NOT_SET, - .duration = DEFAULT_SCHEDULE.duration, - .scheduleStart = DEFAULT_SCHEDULE.scheduleStart, - .chargingRateUnit = DEFAULT_SCHEDULE.chargingRateUnit, - - }; + ChargingSchedulePeriod period; + period.startPeriod = 0; + period.limit = DEFAULT_LIMIT_AMPS; + period.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = DEFAULT_SCHEDULE.duration; + expected.scheduleStart = DEFAULT_SCHEDULE.scheduleStart; + expected.chargingRateUnit = DEFAULT_SCHEDULE.chargingRateUnit; const CompositeSchedule actual = calculate_composite_schedule(DEFAULT_SCHEDULE, DEFAULT_SCHEDULE, DEFAULT_SCHEDULE, DEFAULT_SCHEDULE, DEFAULT_LIMITS, LOW_VOLTAGE); @@ -1083,21 +1166,26 @@ TEST(OCPPTypesTest, CalculateChargingScheduleCombined_Default) { TEST(OCPPTypesTest, CalculateChargingScheduleCombined_CombinedTxDefault) { CompositeSchedule profile = DEFAULT_SCHEDULE; - CompositeSchedule tx_default_schedule = { - .chargingSchedulePeriod = {{0, 10.0, nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - CompositeSchedule expected = CompositeSchedule{ - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, .limit = 10, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 10.0; + CompositeSchedule tx_default_schedule; + tx_default_schedule.chargingSchedulePeriod = {period1}; + tx_default_schedule.evseId = EVSEID_NOT_SET; + tx_default_schedule.duration = 600; + tx_default_schedule.scheduleStart = dt("12:00"); + tx_default_schedule.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period2; + period2.startPeriod = 0; + period2.limit = 10; + period2.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period2}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = 600; + expected.scheduleStart = dt("12:00"); + expected.chargingRateUnit = ChargingRateUnitEnum::A; const CompositeSchedule actual = calculate_composite_schedule(profile, profile, tx_default_schedule, profile, DEFAULT_LIMITS, LOW_VOLTAGE); @@ -1107,29 +1195,35 @@ TEST(OCPPTypesTest, CalculateChargingScheduleCombined_CombinedTxDefault) { TEST(OCPPTypesTest, CalculateChargingScheduleCombined_CombinedTxDefaultTx) { CompositeSchedule charging_station_max = DEFAULT_SCHEDULE; - CompositeSchedule tx_default_schedule = { - .chargingSchedulePeriod = {{0, 10.0, nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - CompositeSchedule tx_schedule = { - .chargingSchedulePeriod = {{0, 32.0, nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - - CompositeSchedule expected = { - .chargingSchedulePeriod = {ChargingSchedulePeriod{ - .startPeriod = 0, .limit = 32.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 10.0; + CompositeSchedule tx_default_schedule; + tx_default_schedule.chargingSchedulePeriod = {period1}; + tx_default_schedule.evseId = EVSEID_NOT_SET; + tx_default_schedule.duration = 600; + tx_default_schedule.scheduleStart = dt("12:00"); + tx_default_schedule.chargingRateUnit = ChargingRateUnitEnum::A; + ChargingSchedulePeriod period2; + period2.startPeriod = 0; + period2.limit = 32.0; + CompositeSchedule tx_schedule; + tx_schedule.chargingSchedulePeriod = {period2}; + tx_schedule.evseId = EVSEID_NOT_SET; + tx_schedule.duration = 600; + tx_schedule.scheduleStart = dt("12:00"); + tx_schedule.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period3; + period3.startPeriod = 0; + period3.limit = 32.0; + period3.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period3}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = 600; + expected.scheduleStart = dt("12:00"); + expected.chargingRateUnit = ChargingRateUnitEnum::A; const CompositeSchedule actual = calculate_composite_schedule( DEFAULT_SCHEDULE, charging_station_max, tx_default_schedule, tx_schedule, DEFAULT_LIMITS, LOW_VOLTAGE); @@ -1138,42 +1232,64 @@ TEST(OCPPTypesTest, CalculateChargingScheduleCombined_CombinedTxDefaultTx) { } TEST(OCPPTypesTest, CalculateChargingScheduleCombined_CombinedOverlapTxAndTxDefault) { - CompositeSchedule tx_default_schedule = { - .chargingSchedulePeriod = {{0, 10.0, std::nullopt}, {300, 24.0, nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - - CompositeSchedule tx_schedule = { - .chargingSchedulePeriod = {{0, NO_LIMIT_SPECIFIED, nullopt}, - {150, 32.0, std::nullopt}, - {450, NO_LIMIT_SPECIFIED, nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - - CompositeSchedule charging_station_max = { - .chargingSchedulePeriod = {{0, NO_LIMIT_SPECIFIED, nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - - CompositeSchedule expected = { - .chargingSchedulePeriod = - {ChargingSchedulePeriod{.startPeriod = 0, .limit = 10.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}, - ChargingSchedulePeriod{.startPeriod = 150, .limit = 32.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}, - ChargingSchedulePeriod{.startPeriod = 450, .limit = 24.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 10.0; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 24.0; + CompositeSchedule tx_default_schedule; + tx_default_schedule.chargingSchedulePeriod = {period1, period2}; + tx_default_schedule.evseId = EVSEID_NOT_SET; + tx_default_schedule.duration = 600; + tx_default_schedule.scheduleStart = dt("12:00"); + tx_default_schedule.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period3; + period3.startPeriod = 0; + period3.limit = NO_LIMIT_SPECIFIED; + ChargingSchedulePeriod period4; + period4.startPeriod = 150; + period4.limit = 32.0; + ChargingSchedulePeriod period5; + period5.startPeriod = 450; + period5.limit = NO_LIMIT_SPECIFIED; + CompositeSchedule tx_schedule; + tx_schedule.chargingSchedulePeriod = {period3, period4, period5}; + tx_schedule.evseId = EVSEID_NOT_SET; + tx_schedule.duration = 600; + tx_schedule.scheduleStart = dt("12:00"); + tx_schedule.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period6; + period6.startPeriod = 0; + period6.limit = NO_LIMIT_SPECIFIED; + CompositeSchedule charging_station_max; + charging_station_max.chargingSchedulePeriod = {period6}; + charging_station_max.evseId = EVSEID_NOT_SET; + charging_station_max.duration = 600; + charging_station_max.scheduleStart = dt("12:00"); + charging_station_max.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period7; + period7.startPeriod = 0; + period7.limit = 10.0; + period7.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + ChargingSchedulePeriod period8; + period8.startPeriod = 150; + period8.limit = 32.0; + period8.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + ChargingSchedulePeriod period9; + period9.startPeriod = 450; + period9.limit = 24.0; + period9.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period7, period8, period9}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = 600; + expected.scheduleStart = dt("12:00"); + expected.chargingRateUnit = ChargingRateUnitEnum::A; const CompositeSchedule actual = calculate_composite_schedule( DEFAULT_SCHEDULE, charging_station_max, tx_default_schedule, tx_schedule, DEFAULT_LIMITS, LOW_VOLTAGE); @@ -1182,46 +1298,80 @@ TEST(OCPPTypesTest, CalculateChargingScheduleCombined_CombinedOverlapTxAndTxDefa } TEST(OCPPTypesTest, CalculateChargingScheduleCombined_CombinedOverlapTxTxDefaultAndChargingStationMax) { - CompositeSchedule tx_default_schedule = { - .chargingSchedulePeriod = {{0, 10.0, std::nullopt}, {300, 24.0, std::nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - - CompositeSchedule tx_schedule = { - .chargingSchedulePeriod = {{0, NO_LIMIT_SPECIFIED, std::nullopt}, - {150, 32.0, std::nullopt}, - {450, NO_LIMIT_SPECIFIED, std::nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - - CompositeSchedule charging_station_max = { - .chargingSchedulePeriod = {{0, NO_LIMIT_SPECIFIED, std::nullopt}, - {500, 15.0, std::nullopt}, - {550, NO_LIMIT_SPECIFIED, std::nullopt}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; - - CompositeSchedule expected = { - .chargingSchedulePeriod = - {ChargingSchedulePeriod{.startPeriod = 0, .limit = 10.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}, - ChargingSchedulePeriod{.startPeriod = 150, .limit = 32.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}, - ChargingSchedulePeriod{.startPeriod = 450, .limit = 24.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}, - ChargingSchedulePeriod{.startPeriod = 500, .limit = 15.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}, - ChargingSchedulePeriod{.startPeriod = 550, .limit = 24.0, .numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES}}, - .evseId = EVSEID_NOT_SET, - .duration = 600, - .scheduleStart = dt("12:00"), - .chargingRateUnit = ChargingRateUnitEnum::A, - }; + ChargingSchedulePeriod period1; + period1.startPeriod = 0; + period1.limit = 10.0; + ChargingSchedulePeriod period2; + period2.startPeriod = 300; + period2.limit = 24.0; + CompositeSchedule tx_default_schedule; + tx_default_schedule.chargingSchedulePeriod = {period1, period2}; + tx_default_schedule.evseId = EVSEID_NOT_SET; + tx_default_schedule.duration = 600; + tx_default_schedule.scheduleStart = dt("12:00"); + tx_default_schedule.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period3; + period3.startPeriod = 0; + period3.limit = NO_LIMIT_SPECIFIED; + ChargingSchedulePeriod period4; + period4.startPeriod = 150; + period4.limit = 32.0; + ChargingSchedulePeriod period5; + period5.startPeriod = 450; + period5.limit = NO_LIMIT_SPECIFIED; + + CompositeSchedule tx_schedule; + tx_schedule.chargingSchedulePeriod = {period3, period4, period5}; + tx_schedule.evseId = EVSEID_NOT_SET; + tx_schedule.duration = 600; + tx_schedule.scheduleStart = dt("12:00"); + tx_schedule.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period6; + period6.startPeriod = 0; + period6.limit = NO_LIMIT_SPECIFIED; + ChargingSchedulePeriod period7; + period7.startPeriod = 500; + period7.limit = 15.0; + ChargingSchedulePeriod period8; + period8.startPeriod = 550; + period8.limit = NO_LIMIT_SPECIFIED; + CompositeSchedule charging_station_max; + charging_station_max.chargingSchedulePeriod = {period6, period7, period8}; + charging_station_max.evseId = EVSEID_NOT_SET; + charging_station_max.duration = 600; + charging_station_max.scheduleStart = dt("12:00"); + charging_station_max.chargingRateUnit = ChargingRateUnitEnum::A; + + ChargingSchedulePeriod period_expected1; + period_expected1.startPeriod = 0; + period_expected1.limit = 10.0; + period_expected1.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + ChargingSchedulePeriod period_expected2; + period_expected2.startPeriod = 150; + period_expected2.limit = 32.0; + period_expected2.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + ChargingSchedulePeriod period_expected3; + period_expected3.startPeriod = 450; + period_expected3.limit = 24.0; + period_expected3.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + ChargingSchedulePeriod period_expected4; + period_expected4.startPeriod = 500; + period_expected4.limit = 15.0; + period_expected4.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + ChargingSchedulePeriod period_expected5; + period_expected5.startPeriod = 550; + period_expected5.limit = 24.0; + period_expected5.numberPhases = DEFAULT_AND_MAX_NUMBER_PHASES; + + CompositeSchedule expected; + expected.chargingSchedulePeriod = {period_expected1, period_expected2, period_expected3, period_expected4, + period_expected5}; + expected.evseId = EVSEID_NOT_SET; + expected.duration = 600; + expected.scheduleStart = dt("12:00"); + expected.chargingRateUnit = ChargingRateUnitEnum::A; const CompositeSchedule actual = calculate_composite_schedule( DEFAULT_SCHEDULE, charging_station_max, tx_default_schedule, tx_schedule, DEFAULT_LIMITS, LOW_VOLTAGE); diff --git a/tests/lib/ocpp/v201/test_smart_charging_handler.cpp b/tests/lib/ocpp/v201/test_smart_charging_handler.cpp index ceb4678f3..ace74ce2d 100644 --- a/tests/lib/ocpp/v201/test_smart_charging_handler.cpp +++ b/tests/lib/ocpp/v201/test_smart_charging_handler.cpp @@ -114,11 +114,10 @@ class SmartChargingHandlerTestFixtureV201 : public DatabaseTestingUtils { std::vector create_charging_schedule_periods(int32_t start_period, std::optional number_phases = std::nullopt, std::optional phase_to_use = std::nullopt) { - auto charging_schedule_period = ChargingSchedulePeriod{ - .startPeriod = start_period, - .numberPhases = number_phases, - .phaseToUse = phase_to_use, - }; + ChargingSchedulePeriod charging_schedule_period; + charging_schedule_period.startPeriod = start_period; + charging_schedule_period.numberPhases = number_phases; + charging_schedule_period.phaseToUse = phase_to_use; return {charging_schedule_period}; } @@ -126,9 +125,9 @@ class SmartChargingHandlerTestFixtureV201 : public DatabaseTestingUtils { std::vector create_charging_schedule_periods(std::vector start_periods) { auto charging_schedule_periods = std::vector(); for (auto start_period : start_periods) { - auto charging_schedule_period = ChargingSchedulePeriod{ - .startPeriod = start_period, - }; + ChargingSchedulePeriod charging_schedule_period; + charging_schedule_period.startPeriod = start_period; + charging_schedule_periods.push_back(charging_schedule_period); } @@ -137,8 +136,10 @@ class SmartChargingHandlerTestFixtureV201 : public DatabaseTestingUtils { std::vector create_charging_schedule_periods_with_phases(int32_t start_period, int32_t numberPhases, int32_t phaseToUse) { - auto charging_schedule_period = - ChargingSchedulePeriod{.startPeriod = start_period, .numberPhases = numberPhases, .phaseToUse = phaseToUse}; + ChargingSchedulePeriod charging_schedule_period; + charging_schedule_period.startPeriod = start_period; + charging_schedule_period.numberPhases = numberPhases; + charging_schedule_period.phaseToUse = phaseToUse; return {charging_schedule_period}; } @@ -151,16 +152,18 @@ class SmartChargingHandlerTestFixtureV201 : public DatabaseTestingUtils { std::optional validTo = {}) { auto recurrency_kind = RecurrencyKindEnum::Daily; std::vector charging_schedules = {charging_schedule}; - return ChargingProfile{.id = charging_profile_id, - .stackLevel = stack_level, - .chargingProfilePurpose = charging_profile_purpose, - .chargingProfileKind = charging_profile_kind, - .chargingSchedule = charging_schedules, - .customData = {}, - .recurrencyKind = recurrency_kind, - .validFrom = validFrom, - .validTo = validTo, - .transactionId = transaction_id}; + ChargingProfile charging_profile; + charging_profile.id = charging_profile_id; + charging_profile.stackLevel = stack_level; + charging_profile.chargingProfilePurpose = charging_profile_purpose; + charging_profile.chargingProfileKind = charging_profile_kind; + charging_profile.chargingSchedule = charging_schedules; + charging_profile.customData = {}; + charging_profile.recurrencyKind = recurrency_kind; + charging_profile.validFrom = validFrom; + charging_profile.validTo = validTo; + charging_profile.transactionId = transaction_id; + return charging_profile; } ChargingProfileCriterion create_charging_profile_criteria( @@ -198,8 +201,12 @@ class SmartChargingHandlerTestFixtureV201 : public DatabaseTestingUtils { ClearChargingProfile create_clear_charging_profile(std::optional evse_id = std::nullopt, std::optional purpose = std::nullopt, std::optional stack_level = std::nullopt) { - return ClearChargingProfile{ - .customData = {}, .evseId = evse_id, .chargingProfilePurpose = purpose, .stackLevel = stack_level}; + ClearChargingProfile clear_charging_profile; + clear_charging_profile.customData = {}; + clear_charging_profile.evseId = evse_id; + clear_charging_profile.chargingProfilePurpose = purpose; + clear_charging_profile.stackLevel = stack_level; + return clear_charging_profile; } void create_device_model_db(const std::string& path) { @@ -1274,9 +1281,9 @@ TEST_F(SmartChargingHandlerTestFixtureV201, AddProfile_StoresChargingLimitSource auto response = handler.add_profile(profile, DEFAULT_EVSE_ID, charging_limit_source); EXPECT_THAT(response.status, testing::Eq(ChargingProfileStatusEnum::Accepted)); - ChargingProfileCriterion criteria = { - .chargingProfileId = {{profile.id}}, - }; + ChargingProfileCriterion criteria; + criteria.chargingProfileId = {{profile.id}}; + auto profiles = this->database_handler->get_charging_profiles_matching_criteria(DEFAULT_EVSE_ID, criteria); const auto [e, p, sut] = profiles[0]; EXPECT_THAT(sut, ChargingLimitSourceEnum::SO); @@ -1296,9 +1303,9 @@ TEST_F(SmartChargingHandlerTestFixtureV201, ValidateAndAddProfile_StoresCharging auto response = handler.conform_validate_and_add_profile(profile, DEFAULT_EVSE_ID, charging_limit_source); EXPECT_THAT(response.status, testing::Eq(ChargingProfileStatusEnum::Accepted)); - ChargingProfileCriterion criteria = { - .chargingProfileId = {{profile.id}}, - }; + ChargingProfileCriterion criteria; + criteria.chargingProfileId = {{profile.id}}; + auto profiles = this->database_handler->get_charging_profiles_matching_criteria(DEFAULT_EVSE_ID, criteria); const auto [e, p, sut] = profiles[0]; EXPECT_THAT(sut, ChargingLimitSourceEnum::SO);