From a55c4a9da22a26fce18511bf6b02d72dfc7f667f Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Thu, 3 Oct 2024 13:24:41 +0200 Subject: [PATCH 1/9] Extended the connection_state_changed_callback with the actual network connectivity slot info Added on_network_disconnected that will switch to the next network connection profile when invoked Extended the configure network connectivity profile callback with a cpp future / promise mechanism Added networkconnectivity documentation. These were originally added in PR 410 and have been updated to match the current implementation. Added on_try_switch_network_connection_profile function to try to switch to a higher priority network connection profile Signed-off-by: Wilco den Besten --- .../standardized/InternalCtrlr.json | 18 ++ doc/networkconnectivity/README.md | 57 +++++ .../networkconnectivity_libocpp.png | Bin 0 -> 95546 bytes .../networkconnectivity_libocpp.puml | 54 +++++ include/ocpp/v201/charge_point.hpp | 34 ++- include/ocpp/v201/charge_point_callbacks.hpp | 8 +- include/ocpp/v201/connectivity_manager.hpp | 71 +++++- .../ocpp/v201/ctrlr_component_variables.hpp | 1 + include/ocpp/v201/ocpp_types.hpp | 7 + lib/ocpp/v201/charge_point.cpp | 30 ++- lib/ocpp/v201/connectivity_manager.cpp | 218 +++++++++++++++--- lib/ocpp/v201/ctrlr_component_variables.cpp | 7 + tests/lib/ocpp/v201/test_charge_point.cpp | 3 +- 13 files changed, 456 insertions(+), 52 deletions(-) create mode 100644 doc/networkconnectivity/README.md create mode 100644 doc/networkconnectivity/networkconnectivity_libocpp.png create mode 100644 doc/networkconnectivity/networkconnectivity_libocpp.puml 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/doc/networkconnectivity/README.md b/doc/networkconnectivity/README.md new file mode 100644 index 000000000..70e2f4b5b --- /dev/null +++ b/doc/networkconnectivity/README.md @@ -0,0 +1,57 @@ +# 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. +In this case, you might want to tell libocpp that the higher priority interface is up and that it can try to connect to it. +For example, if the modem has 2nd priority, but you want to avoid high costs due to data rates and switch back to the wired network as soon as it is available. +A call is implemented for exactly this reason: `bool on_try_switch_network_connection_profile(const int32_t configuration_slot)`. +When you call this function, you are telling libocpp that there is a network connection profile available, and that it may try to connect to that network connection profile (although the priority of that profile may be higher and there may be some more checks). + +## 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. + +![Sequence diagram](networkconnectivity_libocpp.png) \ No newline at end of file diff --git a/doc/networkconnectivity/networkconnectivity_libocpp.png b/doc/networkconnectivity/networkconnectivity_libocpp.png new file mode 100644 index 0000000000000000000000000000000000000000..9bf773ddbf327b66f0bd9c177685b09b71082f39 GIT binary patch literal 95546 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" +skinparam sequenceArrowThickness 2 + +== libocpp wants to connect to network connection profile == + +{start} libocpp -> core: std::future(configure_network_connection_profile_callback(\nconfiguration_slot, NetworkConnectionProfile)) +activate core #DarkSalmon +core -> core: Setup network, e.g. setup modem +{end} core -> libocpp: promise.set_value(status,\nip_address, etc) + +deactivate core +{start} <-> {end}: ... possible delay ... + +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, \ne.g. disable modem +else timeout reached, next network connection profile selected + libocpp --> core: std::future(configure_network_connection_profile_callback(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) +end + + +== 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(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) + + +== 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(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) + + +== 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(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) + + +@enduml \ No newline at end of file diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index 9d9e7aa08..f0cf2f62e 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 @@ -109,6 +108,28 @@ 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. + /// \param ocpp_interface The interface that is disconnected. + /// + /// \note At least one of the two params must be provided, otherwise libocpp will not know which interface is down. + /// + virtual void on_network_disconnected(const std::optional configuration_slot, + const std::optional 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. @@ -413,8 +434,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); @@ -820,6 +843,11 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa virtual void connect_websocket() override; virtual void disconnect_websocket() override; + void on_network_disconnected(const std::optional configuration_slot, + const std::optional 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; diff --git a/include/ocpp/v201/charge_point_callbacks.hpp b/include/ocpp/v201/charge_point_callbacks.hpp index 83a114e4b..a154f6e69 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..2e15dbc40 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 @@ -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 /// @@ -115,6 +116,28 @@ 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. + /// \param ocpp_interface The interface that is disconnected. + /// + /// \note At least one of the two params must be provided, otherwise libocpp will not know which interface is down. + /// + void on_network_disconnected(const std::optional configuration_slot, + const std::optional 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 +148,38 @@ 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 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 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 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 07a5180df..53de0f966 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/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 504987602..427fc0607 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -234,6 +234,15 @@ void ChargePoint::disconnect_websocket() { this->connectivity_manager->disconnect_websocket(); } +void ChargePoint::on_network_disconnected(const std::optional configuration_slot, + const std::optional ocpp_interface) { + this->connectivity_manager->on_network_disconnected(configuration_slot, 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) { @@ -1177,9 +1186,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)); @@ -4186,14 +4195,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 @@ -4223,11 +4233,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 @@ -4239,7 +4250,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); } } diff --git a/lib/ocpp/v201/connectivity_manager.cpp b/lib/ocpp/v201/connectivity_manager.cpp index f9c435355..f0e1a59a0 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; } @@ -89,6 +92,7 @@ bool ConnectivityManager::is_websocket_connected() { void ConnectivityManager::start() { init_websocket(); if (websocket != nullptr) { + this->disable_automatic_websocket_reconnects = false; websocket->connect(); } } @@ -121,6 +125,50 @@ bool ConnectivityManager::send_to_websocket(const std::string& message) { return this->websocket->send(message); } +void ConnectivityManager::on_network_disconnected(const std::optional configuration_slot, + const std::optional ocpp_interface) { + + if (!configuration_slot.has_value() and !ocpp_interface.has_value()) { + EVLOG_warning << "Network disconnected. Not clear which network is disconnected: configuration slot and ocpp " + "interface are empty"; + return; + } + + 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.has_value() and (configuration_slot.value() == actual_configuration_slot)) or + (ocpp_interface.has_value() and + (ocpp_interface.value() == network_connection_profile.value().ocppInterface))) { + this->disconnect_websocket(ocpp::WebsocketCloseReason::GoingAway); + // Since there is no connection anymore: reconnect with the next available network connection profile. + reconnect(ocpp::WebsocketCloseReason::GoingAway); + } +} + +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; + } + + 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) { @@ -131,15 +179,49 @@ void ConnectivityManager::init_websocket() { 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 = std::stoi(configuration_slot); + + 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; + } + 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(); @@ -169,13 +251,9 @@ 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) { @@ -184,14 +262,7 @@ void ConnectivityManager::init_websocket() { << 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); + reconnect(reason); } }); @@ -248,6 +319,99 @@ 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() { + int actual_configuration_slot = std::stoi( + ocpp::split_string( + this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), ',') + .at(this->network_configuration_priority)); + std::optional network_connection_profile = + this->get_network_connection_profile(actual_configuration_slot); + + if (this->websocket_disconnected_callback.has_value() and network_connection_profile.has_value()) { + this->websocket_disconnected_callback.value()(actual_configuration_slot, network_connection_profile.value()); + } +} + +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_configuration_slot < current_slot) { + // Priority is indeed higher (lower index means higher priority) + return true; + } + + return false; +} + +int ConnectivityManager::get_active_network_configuration_slot() { + return std::stoi( + ocpp::split_string( + this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), ',') + .at(this->network_configuration_priority)); +} + +std::optional ConnectivityManager::get_configuration_slot_priority(const int configuration_slot) { + // Convert to string as a vector of strings is used. + const std::string configuration_slot_string = std::to_string(configuration_slot); + + const std::vector network_connection_priorities = ocpp::split_string( + this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), ','); + auto it = std::find(network_connection_priorities.begin(), network_connection_priorities.end(), + configuration_slot_string); + if (it != network_connection_priorities.end()) { + // Index is iterator - begin iterator + return it - network_connection_priorities.begin(); + } + return std::nullopt; +} + void ConnectivityManager::next_network_configuration_priority() { // retrieve priorities from cache diff --git a/lib/ocpp/v201/ctrlr_component_variables.cpp b/lib/ocpp/v201/ctrlr_component_variables.cpp index 16e6e3e1f..3f521d7bb 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/tests/lib/ocpp/v201/test_charge_point.cpp b/tests/lib/ocpp/v201/test_charge_point.cpp index a007c8507..f7bc18d4b 100644 --- a/tests/lib/ocpp/v201/test_charge_point.cpp +++ b/tests/lib/ocpp/v201/test_charge_point.cpp @@ -516,8 +516,7 @@ TEST_F(ChargepointTestFixtureV201, 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)); From 4b98853da288700e78b41ceac29dd45b0a8dee32 Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Thu, 3 Oct 2024 15:43:04 +0200 Subject: [PATCH 2/9] Fix after rebase on main: use cached values Signed-off-by: Wilco den Besten --- include/ocpp/v201/connectivity_manager.hpp | 2 +- lib/ocpp/v201/connectivity_manager.cpp | 18 +++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/include/ocpp/v201/connectivity_manager.hpp b/include/ocpp/v201/connectivity_manager.hpp index 2e15dbc40..7a41d11c3 100644 --- a/include/ocpp/v201/connectivity_manager.hpp +++ b/include/ocpp/v201/connectivity_manager.hpp @@ -47,7 +47,7 @@ class ConnectivityManager { int network_configuration_priority; /// @brief Local cached network connection profiles std::vector network_connection_profiles; - /// @brief local cached network conenction priorities + /// @brief local cached network connection priorities std::vector network_connection_priorities; WebsocketConnectionOptions current_connection_options{}; diff --git a/lib/ocpp/v201/connectivity_manager.cpp b/lib/ocpp/v201/connectivity_manager.cpp index f0e1a59a0..3a9fc1293 100644 --- a/lib/ocpp/v201/connectivity_manager.cpp +++ b/lib/ocpp/v201/connectivity_manager.cpp @@ -330,15 +330,12 @@ void ConnectivityManager::on_websocket_connected([[maybe_unused]] int security_p } void ConnectivityManager::on_websocket_disconnected() { - int actual_configuration_slot = std::stoi( - ocpp::split_string( - this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), ',') - .at(this->network_configuration_priority)); std::optional network_connection_profile = - this->get_network_connection_profile(actual_configuration_slot); + 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()(actual_configuration_slot, network_connection_profile.value()); + this->websocket_disconnected_callback.value()(this->get_active_network_configuration_slot(), + network_connection_profile.value()); } } @@ -391,19 +388,14 @@ bool ConnectivityManager::is_higher_priority_profile(const int new_configuration } int ConnectivityManager::get_active_network_configuration_slot() { - return std::stoi( - ocpp::split_string( - this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), ',') - .at(this->network_configuration_priority)); + return std::stoi(this->network_connection_priorities.at(this->network_configuration_priority)); } std::optional ConnectivityManager::get_configuration_slot_priority(const int configuration_slot) { // Convert to string as a vector of strings is used. const std::string configuration_slot_string = std::to_string(configuration_slot); - const std::vector network_connection_priorities = ocpp::split_string( - this->device_model.get_value(ControllerComponentVariables::NetworkConfigurationPriority), ','); - auto it = std::find(network_connection_priorities.begin(), network_connection_priorities.end(), + auto it = std::find(this->network_connection_priorities.begin(), this->network_connection_priorities.end(), configuration_slot_string); if (it != network_connection_priorities.end()) { // Index is iterator - begin iterator From 5772549a4cc334483b7c4f66f12f9e18a7cda2e2 Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Fri, 4 Oct 2024 14:38:28 +0200 Subject: [PATCH 3/9] Fix incorrect comparsion Signed-off-by: Wilco den Besten --- lib/ocpp/v201/connectivity_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ocpp/v201/connectivity_manager.cpp b/lib/ocpp/v201/connectivity_manager.cpp index 3a9fc1293..d5f34a4ed 100644 --- a/lib/ocpp/v201/connectivity_manager.cpp +++ b/lib/ocpp/v201/connectivity_manager.cpp @@ -379,7 +379,7 @@ bool ConnectivityManager::is_higher_priority_profile(const int new_configuration return false; } - if (new_configuration_slot < current_slot) { + if (new_priority.value() < current_priority.value()) { // Priority is indeed higher (lower index means higher priority) return true; } From 6e1cc7fbe87cd6290d079c7f3dc36102bd1dfee3 Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Mon, 7 Oct 2024 08:31:36 +0200 Subject: [PATCH 4/9] Added new network profile functions to Chargepoint to retrieve the cached network profile information as that's helpful when using the newly added optional functions Signed-off-by: Wilco den Besten --- include/ocpp/v201/charge_point.hpp | 13 ++++++++++ include/ocpp/v201/connectivity_manager.hpp | 14 +++++----- lib/ocpp/v201/charge_point.cpp | 12 +++++++++ lib/ocpp/v201/connectivity_manager.cpp | 30 ++++++++++++---------- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index f0cf2f62e..8333e4b2f 100644 --- a/include/ocpp/v201/charge_point.hpp +++ b/include/ocpp/v201/charge_point.hpp @@ -337,6 +337,13 @@ 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; + + virtual std::optional + get_network_connection_profile(const int32_t configuration_slot) = 0; + + virtual std::optional get_configuration_slot_priority(const int configuration_slot) = 0; + + virtual const std::vector& get_network_connection_priorities() const = 0; }; /// \brief Class implements OCPP2.0.1 Charging Station @@ -932,6 +939,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/connectivity_manager.hpp b/include/ocpp/v201/connectivity_manager.hpp index 7a41d11c3..39fe0e7eb 100644 --- a/include/ocpp/v201/connectivity_manager.hpp +++ b/include/ocpp/v201/connectivity_manager.hpp @@ -90,6 +90,14 @@ class ConnectivityManager { /// 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); + + const std::vector& get_network_connection_priorities() const; + /// \brief Check if the websocket is connected /// \return True is the websocket is connected, else false /// @@ -174,12 +182,6 @@ class ConnectivityManager { /// int get_active_network_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 Moves websocket network_configuration_priority to next profile /// void next_network_configuration_priority(); diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 427fc0607..24294d5a5 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -4520,6 +4520,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 d5f34a4ed..021fee9fd 100644 --- a/lib/ocpp/v201/connectivity_manager.cpp +++ b/lib/ocpp/v201/connectivity_manager.cpp @@ -85,6 +85,23 @@ ConnectivityManager::get_network_connection_profile(const int32_t configuration_ return std::nullopt; } +std::optional ConnectivityManager::get_configuration_slot_priority(const int configuration_slot) { + // Convert to string as a vector of strings is used. + const std::string configuration_slot_string = std::to_string(configuration_slot); + + auto it = std::find(this->network_connection_priorities.begin(), this->network_connection_priorities.end(), + configuration_slot_string); + 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 network_connection_priorities; +} + bool ConnectivityManager::is_websocket_connected() { return this->websocket != nullptr && this->websocket->is_connected(); } @@ -391,19 +408,6 @@ int ConnectivityManager::get_active_network_configuration_slot() { return std::stoi(this->network_connection_priorities.at(this->network_configuration_priority)); } -std::optional ConnectivityManager::get_configuration_slot_priority(const int configuration_slot) { - // Convert to string as a vector of strings is used. - const std::string configuration_slot_string = std::to_string(configuration_slot); - - auto it = std::find(this->network_connection_priorities.begin(), this->network_connection_priorities.end(), - configuration_slot_string); - if (it != network_connection_priorities.end()) { - // Index is iterator - begin iterator - return it - network_connection_priorities.begin(); - } - return std::nullopt; -} - void ConnectivityManager::next_network_configuration_priority() { // retrieve priorities from cache From 14284ba70da171846f3bc342e3b0b6de157bc380 Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Mon, 7 Oct 2024 15:43:21 +0200 Subject: [PATCH 5/9] Fix missing break causing an indefinite wait for a std::future Signed-off-by: Wilco den Besten --- lib/ocpp/v201/connectivity_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ocpp/v201/connectivity_manager.cpp b/lib/ocpp/v201/connectivity_manager.cpp index 021fee9fd..223a9025a 100644 --- a/lib/ocpp/v201/connectivity_manager.cpp +++ b/lib/ocpp/v201/connectivity_manager.cpp @@ -222,6 +222,7 @@ void ConnectivityManager::init_websocket() { 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(); From 093cb147a7315d125afbe2edae026da2cbded0ff Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Tue, 8 Oct 2024 13:10:46 +0200 Subject: [PATCH 6/9] Add function description to newly added functions Signed-off-by: Wilco den Besten --- include/ocpp/v201/charge_point.hpp | 13 +++++++++++++ include/ocpp/v201/connectivity_manager.hpp | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index 8333e4b2f..727534951 100644 --- a/include/ocpp/v201/charge_point.hpp +++ b/include/ocpp/v201/charge_point.hpp @@ -338,11 +338,24 @@ class ChargePointInterface { 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; }; diff --git a/include/ocpp/v201/connectivity_manager.hpp b/include/ocpp/v201/connectivity_manager.hpp index 39fe0e7eb..99e511de6 100644 --- a/include/ocpp/v201/connectivity_manager.hpp +++ b/include/ocpp/v201/connectivity_manager.hpp @@ -85,7 +85,7 @@ 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); @@ -96,6 +96,11 @@ class ConnectivityManager { /// 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 From 54a6fc412103eda183d87668ea9ff3814b909f8d Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Fri, 18 Oct 2024 12:06:15 +0200 Subject: [PATCH 7/9] Processed review comments Signed-off-by: Wilco den Besten --- doc/networkconnectivity/README.md | 8 +- include/ocpp/v201/charge_point.hpp | 23 ++++-- include/ocpp/v201/connectivity_manager.hpp | 22 ++++-- lib/ocpp/v201/charge_point.cpp | 11 ++- lib/ocpp/v201/connectivity_manager.cpp | 91 +++++++++++++--------- 5 files changed, 96 insertions(+), 59 deletions(-) diff --git a/doc/networkconnectivity/README.md b/doc/networkconnectivity/README.md index 70e2f4b5b..5f73d5ddc 100644 --- a/doc/networkconnectivity/README.md +++ b/doc/networkconnectivity/README.md @@ -37,11 +37,9 @@ It will bind to the given network interface (a string containing the name of the ## 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. -In this case, you might want to tell libocpp that the higher priority interface is up and that it can try to connect to it. -For example, if the modem has 2nd priority, but you want to avoid high costs due to data rates and switch back to the wired network as soon as it is available. -A call is implemented for exactly this reason: `bool on_try_switch_network_connection_profile(const int32_t configuration_slot)`. -When you call this function, you are telling libocpp that there is a network connection profile available, and that it may try to connect to that network connection profile (although the priority of that profile may be higher and there may be some more checks). +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 diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index 727534951..254910f04 100644 --- a/include/ocpp/v201/charge_point.hpp +++ b/include/ocpp/v201/charge_point.hpp @@ -115,12 +115,18 @@ class ChargePointInterface { /// unavailable, whereas the system can detect this sooner. /// /// \param configuration_slot The slot of the network connection profile that is disconnected. - /// \param ocpp_interface The interface that is disconnected. /// - /// \note At least one of the two params must be provided, otherwise libocpp will not know which interface is down. + virtual void on_network_disconnected(const std::optional 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. /// - virtual void on_network_disconnected(const std::optional configuration_slot, - const std::optional ocpp_interface) = 0; + /// \param ocpp_interface The interface that is disconnected. + /// + virtual void on_network_disconnected(const std::optional ocpp_interface) = 0; /// \brief Switch to a specific network connection profile given the configuration slot. /// @@ -356,7 +362,7 @@ class ChargePointInterface { /// priority. /// @return The network connection priorities /// - virtual const std::vector& get_network_connection_priorities() const = 0; + virtual const std::vector& get_network_connection_priorities() const = 0; }; /// \brief Class implements OCPP2.0.1 Charging Station @@ -863,8 +869,9 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa virtual void connect_websocket() override; virtual void disconnect_websocket() override; - void on_network_disconnected(const std::optional configuration_slot, - const std::optional ocpp_interface) override; + void on_network_disconnected(const std::optional configuration_slot) override; + + void on_network_disconnected(const std::optional ocpp_interface) override; bool on_try_switch_network_connection_profile(const int32_t configuration_slot) override; @@ -956,7 +963,7 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa std::optional get_configuration_slot_priority(const int configuration_slot) override; - const std::vector& get_network_connection_priorities() const 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 diff --git a/include/ocpp/v201/connectivity_manager.hpp b/include/ocpp/v201/connectivity_manager.hpp index 99e511de6..2421d7773 100644 --- a/include/ocpp/v201/connectivity_manager.hpp +++ b/include/ocpp/v201/connectivity_manager.hpp @@ -48,7 +48,7 @@ class ConnectivityManager { /// @brief Local cached network connection profiles std::vector network_connection_profiles; /// @brief local cached network connection priorities - std::vector network_connection_priorities; + std::vector network_connection_priorities; WebsocketConnectionOptions current_connection_options{}; public: @@ -101,7 +101,7 @@ class ConnectivityManager { /// priority. /// @return The network connection priorities /// - const std::vector& get_network_connection_priorities() const; + const std::vector& get_network_connection_priorities() const; /// \brief Check if the websocket is connected /// \return True is the websocket is connected, else false @@ -136,12 +136,18 @@ class ConnectivityManager { /// unavailable, whereas the system can detect this sooner. /// /// \param configuration_slot The slot of the network connection profile that is disconnected. - /// \param ocpp_interface The interface that is disconnected. /// - /// \note At least one of the two params must be provided, otherwise libocpp will not know which interface is down. + void on_network_disconnected(const std::optional configuration_slot); + + /// + /// \brief Can be called when a network is disconnected, for example when an ethernet cable is removed. /// - void on_network_disconnected(const std::optional configuration_slot, - const std::optional ocpp_interface); + /// 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(const std::optional ocpp_interface); /// \brief Switch to a specific network connection profile given the configuration slot. /// @@ -169,6 +175,10 @@ class ConnectivityManager { /// 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); diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 24294d5a5..62e7a21d9 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -234,9 +234,12 @@ void ChargePoint::disconnect_websocket() { this->connectivity_manager->disconnect_websocket(); } -void ChargePoint::on_network_disconnected(const std::optional configuration_slot, - const std::optional ocpp_interface) { - this->connectivity_manager->on_network_disconnected(configuration_slot, ocpp_interface); +void ChargePoint::on_network_disconnected(const std::optional configuration_slot) { + this->connectivity_manager->on_network_disconnected(configuration_slot); +} + +void ChargePoint::on_network_disconnected(const std::optional ocpp_interface) { + this->connectivity_manager->on_network_disconnected(ocpp_interface); } bool ChargePoint::on_try_switch_network_connection_profile(const int32_t configuration_slot) { @@ -4528,7 +4531,7 @@ std::optional ChargePoint::get_configuration_slot_priority(const int config return this->connectivity_manager->get_configuration_slot_priority(configuration_slot); } -const std::vector& ChargePoint::get_network_connection_priorities() const { +const std::vector& ChargePoint::get_network_connection_priorities() const { return this->connectivity_manager->get_network_connection_priorities(); } diff --git a/lib/ocpp/v201/connectivity_manager.cpp b/lib/ocpp/v201/connectivity_manager.cpp index 223a9025a..dba2517e4 100644 --- a/lib/ocpp/v201/connectivity_manager.cpp +++ b/lib/ocpp/v201/connectivity_manager.cpp @@ -86,11 +86,8 @@ ConnectivityManager::get_network_connection_profile(const int32_t configuration_ } std::optional ConnectivityManager::get_configuration_slot_priority(const int configuration_slot) { - // Convert to string as a vector of strings is used. - const std::string configuration_slot_string = std::to_string(configuration_slot); - auto it = std::find(this->network_connection_priorities.begin(), this->network_connection_priorities.end(), - configuration_slot_string); + configuration_slot); if (it != network_connection_priorities.end()) { // Index is iterator - begin iterator return it - network_connection_priorities.begin(); @@ -98,8 +95,8 @@ std::optional ConnectivityManager::get_configuration_slot_priority(const in return std::nullopt; } -const std::vector& ConnectivityManager::get_network_connection_priorities() const { - return network_connection_priorities; +const std::vector& ConnectivityManager::get_network_connection_priorities() const { + return this->network_connection_priorities; } bool ConnectivityManager::is_websocket_connected() { @@ -142,12 +139,31 @@ bool ConnectivityManager::send_to_websocket(const std::string& message) { return this->websocket->send(message); } -void ConnectivityManager::on_network_disconnected(const std::optional configuration_slot, - const std::optional ocpp_interface) { +void ConnectivityManager::on_network_disconnected(const std::optional configuration_slot) { + + if (!configuration_slot.has_value()) { + EVLOG_warning << "Network disconnected. Not clear which network is disconnected: configuration slot is empty"; + return; + } + + 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.has_value() and (configuration_slot.value() == 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(const std::optional ocpp_interface) { - if (!configuration_slot.has_value() and !ocpp_interface.has_value()) { - EVLOG_warning << "Network disconnected. Not clear which network is disconnected: configuration slot and ocpp " - "interface are empty"; + if (!ocpp_interface.has_value()) { + EVLOG_warning << "Network disconnected. Not clear which network is disconnected: ocpp interface is empty"; return; } @@ -157,12 +173,12 @@ void ConnectivityManager::on_network_disconnected(const std::optional c if (!network_connection_profile.has_value()) { EVLOG_warning << "Network disconnected. No network connection profile configured"; - } else if ((configuration_slot.has_value() and (configuration_slot.value() == actual_configuration_slot)) or - (ocpp_interface.has_value() and - (ocpp_interface.value() == network_connection_profile.value().ocppInterface))) { + } else if (ocpp_interface.has_value() and + (ocpp_interface.value() == 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); - // Since there is no connection anymore: reconnect with the next available network connection profile. - reconnect(ocpp::WebsocketCloseReason::GoingAway); + this->disable_automatic_websocket_reconnects = false; } } @@ -181,7 +197,7 @@ bool ConnectivityManager::on_try_switch_network_connection_profile(const int32_t << configuration_slot; return false; } - + this->disconnect_websocket(WebsocketCloseReason::Normal); reconnect(WebsocketCloseReason::Normal, get_configuration_slot_priority(configuration_slot)); return true; } @@ -195,8 +211,7 @@ 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 int config_slot_int = 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 @@ -250,13 +265,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; @@ -272,17 +287,8 @@ void ConnectivityManager::init_websocket() { 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) { - reconnect(reason); - } - }); + 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()); @@ -357,6 +363,15 @@ void ConnectivityManager::on_websocket_disconnected() { } } +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]() { @@ -406,7 +421,7 @@ bool ConnectivityManager::is_higher_priority_profile(const int new_configuration } int ConnectivityManager::get_active_network_configuration_slot() { - return std::stoi(this->network_connection_priorities.at(this->network_configuration_priority)); + return this->network_connection_priorities.at(this->network_configuration_priority); } void ConnectivityManager::next_network_configuration_priority() { @@ -430,8 +445,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")); From 9fbd0702a12f953b6f9d483af317e94edc3b0a56 Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Wed, 23 Oct 2024 14:14:12 +0200 Subject: [PATCH 8/9] Remove std::optional from the split on_network_disconnected function Signed-off-by: Wilco den Besten --- include/ocpp/v201/charge_point.hpp | 8 ++++---- include/ocpp/v201/connectivity_manager.hpp | 4 ++-- lib/ocpp/v201/charge_point.cpp | 4 ++-- lib/ocpp/v201/connectivity_manager.cpp | 20 ++++---------------- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index 254910f04..fdb9c554c 100644 --- a/include/ocpp/v201/charge_point.hpp +++ b/include/ocpp/v201/charge_point.hpp @@ -116,7 +116,7 @@ class ChargePointInterface { /// /// \param configuration_slot The slot of the network connection profile that is disconnected. /// - virtual void on_network_disconnected(const std::optional configuration_slot) = 0; + 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. @@ -126,7 +126,7 @@ class ChargePointInterface { /// /// \param ocpp_interface The interface that is disconnected. /// - virtual void on_network_disconnected(const std::optional ocpp_interface) = 0; + virtual void on_network_disconnected(OCPPInterfaceEnum ocpp_interface) = 0; /// \brief Switch to a specific network connection profile given the configuration slot. /// @@ -869,9 +869,9 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa virtual void connect_websocket() override; virtual void disconnect_websocket() override; - void on_network_disconnected(const std::optional configuration_slot) override; + void on_network_disconnected(int32_t configuration_slot) override; - void on_network_disconnected(const std::optional ocpp_interface) override; + void on_network_disconnected(OCPPInterfaceEnum ocpp_interface) override; bool on_try_switch_network_connection_profile(const int32_t configuration_slot) override; diff --git a/include/ocpp/v201/connectivity_manager.hpp b/include/ocpp/v201/connectivity_manager.hpp index 2421d7773..e72360209 100644 --- a/include/ocpp/v201/connectivity_manager.hpp +++ b/include/ocpp/v201/connectivity_manager.hpp @@ -137,7 +137,7 @@ class ConnectivityManager { /// /// \param configuration_slot The slot of the network connection profile that is disconnected. /// - void on_network_disconnected(const std::optional configuration_slot); + 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. @@ -147,7 +147,7 @@ class ConnectivityManager { /// /// \param ocpp_interface The interface that is disconnected. /// - void on_network_disconnected(const std::optional ocpp_interface); + void on_network_disconnected(OCPPInterfaceEnum ocpp_interface); /// \brief Switch to a specific network connection profile given the configuration slot. /// diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 62e7a21d9..9894c0def 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -234,11 +234,11 @@ void ChargePoint::disconnect_websocket() { this->connectivity_manager->disconnect_websocket(); } -void ChargePoint::on_network_disconnected(const std::optional configuration_slot) { +void ChargePoint::on_network_disconnected(int32_t configuration_slot) { this->connectivity_manager->on_network_disconnected(configuration_slot); } -void ChargePoint::on_network_disconnected(const std::optional ocpp_interface) { +void ChargePoint::on_network_disconnected(OCPPInterfaceEnum ocpp_interface) { this->connectivity_manager->on_network_disconnected(ocpp_interface); } diff --git a/lib/ocpp/v201/connectivity_manager.cpp b/lib/ocpp/v201/connectivity_manager.cpp index dba2517e4..69d1e1b4a 100644 --- a/lib/ocpp/v201/connectivity_manager.cpp +++ b/lib/ocpp/v201/connectivity_manager.cpp @@ -139,20 +139,14 @@ bool ConnectivityManager::send_to_websocket(const std::string& message) { return this->websocket->send(message); } -void ConnectivityManager::on_network_disconnected(const std::optional configuration_slot) { - - if (!configuration_slot.has_value()) { - EVLOG_warning << "Network disconnected. Not clear which network is disconnected: configuration slot is empty"; - return; - } - +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.has_value() and (configuration_slot.value() == actual_configuration_slot)) { + } 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); @@ -160,12 +154,7 @@ void ConnectivityManager::on_network_disconnected(const std::optional c } } -void ConnectivityManager::on_network_disconnected(const std::optional ocpp_interface) { - - if (!ocpp_interface.has_value()) { - EVLOG_warning << "Network disconnected. Not clear which network is disconnected: ocpp interface is empty"; - return; - } +void ConnectivityManager::on_network_disconnected(OCPPInterfaceEnum ocpp_interface) { const int actual_configuration_slot = get_active_network_configuration_slot(); std::optional network_connection_profile = @@ -173,8 +162,7 @@ void ConnectivityManager::on_network_disconnected(const std::optionaldisconnect_websocket(ocpp::WebsocketCloseReason::GoingAway); From 63b58a7f6790fefdcaa5b3057776fbd48207f35e Mon Sep 17 00:00:00 2001 From: Wilco den Besten Date: Thu, 24 Oct 2024 09:08:42 +0200 Subject: [PATCH 9/9] Replace puml with inline mermaid Signed-off-by: Wilco den Besten --- doc/networkconnectivity/README.md | 70 +++++++++++++++--- .../networkconnectivity_libocpp.png | Bin 95546 -> 0 bytes .../networkconnectivity_libocpp.puml | 54 -------------- 3 files changed, 59 insertions(+), 65 deletions(-) delete mode 100644 doc/networkconnectivity/networkconnectivity_libocpp.png delete mode 100644 doc/networkconnectivity/networkconnectivity_libocpp.puml diff --git a/doc/networkconnectivity/README.md b/doc/networkconnectivity/README.md index 5f73d5ddc..45e9fb5c1 100644 --- a/doc/networkconnectivity/README.md +++ b/doc/networkconnectivity/README.md @@ -1,14 +1,14 @@ # Network connection profile interface -libocpp automatically tries to connect using the given network connection profiles. +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. +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 +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))` @@ -19,24 +19,24 @@ std::future future = promise.get_future(); return future; ``` -If the network was setup successfully, you can set the values in the promise with +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. +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. +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. +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. @@ -52,4 +52,52 @@ in these callbacks, so you can keep the network connection in use (e.g. not disa 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. -![Sequence diagram](networkconnectivity_libocpp.png) \ No newline at end of file +```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/doc/networkconnectivity/networkconnectivity_libocpp.png b/doc/networkconnectivity/networkconnectivity_libocpp.png deleted file mode 100644 index 9bf773ddbf327b66f0bd9c177685b09b71082f39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95546 zcmcG$bzGEd8#OwLqyj1>t%!7}3`j@_3IfvINOy^JDGGuD(n?5oODNqf4N6FhgtT;i z*I?}ZzUMu^^Zj$?kG*l`ndiCly4JPUy2npeS_~J53h*NMMr+Sk&jgVU)mx_)2VaW-m7(&p~U!B=X17f^YN zO_2NgHI+>X<++8LNnfwb8uu%^E$jY=4_T;{C~hx!){c06I`74je1)AsbLJAhc9tw| zcU@>`{z=p2TZ{S9m@>!5Zo?&T zgIJ5<&{@9W-taPe;9Oqd9Q>E0f(5hU4K03V!E8@W`0z^Y>Gy^9i=BtPVYYkxGgSKA zs~i3!;z5z&$}DUw(~AlCsiiYix?F89vR!g|Pcv_khVPsX=$eXh?znS}(#T*qZvCdN z@ZFt>Evhi2JKM8sF4&zUjQXc0{A>jjbOy3FQ9{fdq2p&l$m zPHG8w?{36*6)wGg5$@Bfd)Hl+2A4%xJn2iBM3~U7{2VD>3>)3t2Y0jjfJbb6c_lS8 z;gYXK#R-{DRZCXw){^sL&pUNUUDhCE5Ml~)XgNz8MXlJMQ}CAAH>KVOnNM4Lsga8D zu;*Aso{vTUTeQl7S})l}U#y%kxzG8k<*{R0_{rYaX0C@H6ucy5=A^iDv!8hNYTg48 zMI1C52CENTA9{lD73wm(PZ8bKT1olrgIs-Yaa+$r79m0zHb}^p`X<;6iN9U&YI93r zT<$5!v`?PW+{+ef@z}7#inhB-#15S7VPVXcTyvIe6pudZrKBisva;=>_eu3h(~GpF z_*jum!au@}*6XJa8X4_#UAJXd(pYxOUC&BmYUoO3e+W^(cq)0(M~0^&hg|xeqldoi zsAi=Z@r1fm%0Q;@%uvUEd3bH>&%^5EG~;1;hSj4$t;yE}%-`fp?oj%@EBI0wdFUBf zIJ=)CC?h?@s*+SAW{<`6IYC=AmeJRde~T!VF=VjyJRUm&;e`;tdq>epdvO$7Q*q`< z(1!3#Fy*<%=TdUA@}+DORVk%U$5;~jb0z&HnG$@WQjFB1G}uy1T?Xu`w{Ep-N?cc9 zEH^sTw85-=nrdRBb${ldx-+zk|MsKKhemHmb-(}g=y>^sr^~sMr;F$3x!GMCBMeMz z*zWK`^1ML*^_MK+Q-YJfgl?RV`Sq6nlZi7Yf61P^!9DYREQ<5T;fFC7W5b<^JX0_TUZ43m`D_ds0{l~Bvb@$+-EbrtzV`84@!vt|RC^mKuSGv;5c z0$y2Ymf9K0$mFF;CE-&au}a>$L@D6*X`DTaUiSJetkLB}0CyujjZN-&En; zzGFnB^2Kw)5CcR0M-7?v06CXMf6Wt-(NX@2N_Wvf3debQHjTSqyf0kUc3;Ra>z^g( z^dJ6ci#Z-O-2S9QGIlD)1V;h z_6W;C(Nt-7J_d${#uPhM)t#@Uk{gZ<)UXIjjZAFF_#9{gSjIh%>(MZeD&6;~+JqII z7WzFIem;12d%7iCLH0Sws-u#bLeUb!q2ys_+(;sFgki==+h*8 z@sH!07WVKfKGg`BQL0|$E-fz)qTo5&UkPH>sS135SY*1Af6xAK^=DR@-OTL%_A$59 zDweTvB((>3Coy9c#o*_dn3#0>J|$Y#rCBmgv-KQPmR`@31x}iwMKQo;Or|0Hw+c>*vH13V>73DXjFSNyuRY6w!n^AQcTRMzOI&8Uy@pR|M+Bc86`W} zDwfBXYMv^zkK}M!Z8Prt_OYxiSLc{rJ0vWuH&=`PVf4YD_T^PeD?H%dP+$0*LsF^x z&b@@ba@FIA$2+`_c_Cay^rx9rv}h1%=qKZ=G-U6qi?ecak89e`ol72kQi$~KgQenJ z+!0-!nCr3rZN|EqUzh*}wSlB^qj#djlFulk8iw>}EojosVWh-*@N*H#pH)K0{%e)! zMl9Pki4gKTEb&gn(=#yK9i%(i2~l@y=d<$UDZ{kY7Yp>8KZuJn29O_jbaX5oA6C1o zm63!Wplx?VHOit3jKtsh)}LW*p8ZeL&f!T%b1m7wMo;VSPjb7U#Wfh$jnu{~;>>I^ zzGd~b_J5AnMw64pRo87QVy?uk8i+ZZ-cLh^J2WRR5Di!i=F6$Murn=9Bv=2}9C1wz zXnP)Zz&14IcTPHe{EXQ(sjfTWY9Uewuc&x+6~m2-#~EiNYK!I&llL|P@g{hxQEtgD z)fW9zFZpyqz=J5H)ZmL@G!kxxFXwQiH!g9|4ULZKcg8f1dwMRmvsXuR{y<~;TE2b# zT4F5U_VGsFJTCcBTu{)8>k4aIud7@JI(_lLCbiSsCr|o$^F?hgR#7}n+Y7$htdz5G zJfB9sE2fW6xfjkV@5Zy5bB!)AWj#nxTk`SF|Jbte*PGgV-P2nK1|B!P+E!2q^7z?N z>p1rI7KE$4eLZ~5iFi#8B)6f_Skz5JBVFD6P=%C*0klDLAMJawypK~Nl~W`-i_I}B zw}uTeBT}{#<Ieek3}`$obDDGg*xu}QIXL+F_KS`TBLl<9_1TaAq)G?FN!0a&h#TC&IT|0Tw#Q;_uJ7zj z2OS^$lqv8-EVL>9NuLq23#cyDU@)Aaf3!R4OWi=@wT@3ojzBD)XY)i5pOfC1BFq<` z8-xtzYqN_cWOMpD9xoRptt*;G2tX6ByaRC&2nVc4LBtt)#B{tqzy!*=8T^{3wb;}& zXQH3|{rD^f{V(h>;`JAMz*kT&FTKm$s3HY{Agupi-*dCPHb`r4fk5Pod^|V>*IvT> z+eApNO?R3x5D30!?bL{;1YWk?1(2ceCf6}_YbT!JUwA4d{O4mYbnjn4$jcHsf;{DbVK3lEcCRrcP{>Ikt}M7H!OdaIV?$F@>vXCx4mQgK)c`-^VUcI z99;+H!6eHK=R*P|qo5zn)Sj;b19Rn5a5(c!`%+6Ic8Q)%B-cvAOp`%}5b*~$X}KTI z_dfSwC%E?I$W^mZ`8uEE%Gz+Tc}dUgrcBtuV@pGONQ}I zEz3GXMA3HN9QgF_``<}+1S`TJUrMdH$vRlTU%S!s$LXtO6Jvnz9$YN%Re3Ua9D>cFNarD=?>-pLr=;BNK2amp)}gNvJohi7xHC&~HeccfN< zUSm;FQA`X47k8Fc`Igryv`m#e>AsFA&LrDU&`fepv%7`M%gbwPYlnNANklWQ3Bd%o zB__|BKLCPQPK)zMycZZ4sHUd&<8zL_Zz1Hhn2en7CDbc;rt;FQ%%pq-kn>XX!kWSgYJ22R_0pdv(WG;e(Y{RO)B5qC3YQ z{gGN3PaO);>5xjEK|ULpOUeaCvQv8QM)f|p6~}!O@90c|1dk622VFLQ8nrBM1gYx> zH(|?}dms5tjigQAEPZ}D;7hiR@_dHe$TW<)jNeNmMkzT3l|1bZ6%r2^QcRJ2*hw`n z?ufr3@qCW(($Y)9)c5X#6=SmZy(9XBT8PwS+pR@4f#|nyGd}xhR!;XCuRocM>hIUA zaP1R}xO1t{QU&RIzQd%?s>!y({ll-b>=So> z41XiWL24J)sSS)JJ*gF+zP(DsnSQu{hTx-3rWF_D_Nz8U)CA54Q4xwUxkVlBzRmy8 z)@`y_*d?GJy|-O`EHz_lYHG2Sl~<+biA^`|vCpa@`EeTQv^t@wRgs}nEr3FVwQb?d zl=ipIFpyO4FP9^^A9YuD`FgNvm4z|of3WCbupD}CsN;F~cx9~8l#7=)Du+$5Y;n=z zvmzJUVo#E9edvgJX_-+zZ$?JOO2tpStc;ym%ZK*@La*w)Opj$=Vl|<7I!v=g@7d;2 z)Vw5P@%7^kW@gF%Jtz`m=X}B&nwtx6IRhwgbqkIecII$%RQ6cj*^;tK5Sr#>j!|0T zC%Y7b>_pR%LmSJ)MA+%cUi{vHHNK6HN@gXdI(T=y!v_ZkBUm+f=LA0FdmbM_IhCJ( z9dndk2m_De()sff@%xLl0pzz#=04{?6u}%7yK1trTX&WnE1|Kq)vxXx5ma347i0Kc zBiu2)yqAMI!~!WE_I+#cBT$@-J3dJBX5(hn{AhD@u&b@Djpu`&cHydMH&Q7+{yy{a zX!$J~ny0DbPSdztC662(RSrC^pNvL!`rn0N;%%Y1|DpQOa{XKDbaT+k=Nxj%SW$Aj z&A=adqU~?oCQ9izIaREKN-ZaUKv$44z z?)&W7D=Pjy*xeQvq+XHV3UsHA&C6SP$zH`U{&ETh&h@EZY;SED-Fkj-ct~l#O))4> z77#$e)7#wa>oA%mhR?;qLCbx(-p;NVFls#4pV0?(((ngsnN7x9@_UJ2)4AO~4QSj@ z`S}g2zcaiE6)*IAN*~RN29Py6+@(*0EYD%w*>B~2WQ)DQY=YF#Rae;Dexsr3*)m`e ze!YQ2LOEBnb!?TUU`~9zJpePI z3=}9k`8T2#8vrN-Ak-Km(0_v^gpfA?IDxxZC$Q?QG+n~uq$G8GeeqiesrLb=cH?gj`L#inL1^{~i%XcGN&WQLBohbl78Um2v8w6_QdetV_XbgIG6*49=p zgDRjhefMXVU?Yvx^XJdMJc3k%!j{*r%|JwEF7vtb4DD-) zCApV zAz3PUTZ=<|eSL*MA!aG(Ox4bpuT(|zyK;@-#Zlcfc;V}dY57{&*fLA4knTCL^3wWD zd(j@O^o{LARjFRBXnLuren{D?tE(N+w`ON&0UCW?2Oz7gRc7y(L$k9x8%O`(jVufl z@VSz`S}oq*-eRvVT~SQ36Tf99bN_xiq}|Ph0Zd#ndlWVfbKrMg{|4y@*)J|76&{8s zD?m|dHzTpVJUXvAkfmY`sPaHlj!s-!I#nrxU8mEm%{z4J4tAOeV8t%B zGrrMF91o?X`M98#Y=0t|+fFB|>c;;)hTqSI*V?!6!BEuXyh&Pu;A~A>o$)lsAE2K%MNDAPt?TSjr z@@{^Qw(gY;$=%DV+){UO;bj)IojPN=x;j~3kxCKZ0YfZOo-~Pv8|k_+SKDU#1;Fa! zy8!@8rqS7&+(HjcgzM?ZFyzki+0R|Zm+l|#5&mjf&g~LJtG5y z7(q|%-rH`E_=C)RjTG65(m7=;uPQGS`G2xI8fY89)3i+kCYqy!-#`6k3cA@jsg=?4 zFYWCwH_uEiW-LjT2d$YFizp{MQ9YR2=7N6HD;}rS%jMw>KoIo$L%Fmr z>?4XOdV$N|I^UOO8WG@Y^5rZsi|!0kIK__3L!SIxkC*qt*N?W|;Va|SadFJVG3iQS zk*N>f(zHGeZGuhnLX;jhec~C(q!>=~&)vE&aZFW@fp(E-q{I^RwQvB$a^=dEbz$)@ zElf(8-8TMYvKjIzK>~X(!n2BHbB@#b!^vv>Tz6KlY!)Pl`0nlPH8nJJBblvx5*rim zy&ObRUbPH$`F^a-yu8}6c{`Ib>Pe+yu-;E3m)?waLYL;OPJ!l94Ur?$d@Sw}ZB#ea zJw<20^KKD)SISLU1YODY<$vg}!_a>>hw1^d6v|CGC;dKu*D(7f9tpr^E8~t;ee>AI)~cWqAc2kA;RJY@!7L}Fs zhxm42afr+9;2p?*y5>+OY9u~}HqG#UNFS&tZG%GgY#FV@EYr-~MUTvi(=TctAuLc_)d|#Lq1=bc8ru5U!NF)n)_Sahr@HDr zpY%Z=UKsz*W%+>X%IQ#Q;N}f&05;rFRj48fFk6?CHZr0MxW2Q^kF`8jSq}3ENkjhW z!dT^g6sP%4?D;LyG>F&4TGO^X`{p-}A6biv`gkw0_xYo-6xW_FSE$t|FYmZ^hFfkDL1#<#aC|Kii~U@ z&lKy}+S(c`dz_U#BrH0I8eO-qbQEL>OQ_UK$>#vD!)>jRq#6p~UE}4=74CvtXKgX4 zJh0=$ibXgoA{P`a-ZhD4e`Vfy`{RH)B8&3ll=QXA!Yf~GZ)T!m#ytfAa#GUP74EeF zlSRp#pw_ejJhk8{=lI8rrE=dNTCZy4>zaBM+d6#%eIMJ*4Q)&GVY8?P+~YANG<3AOR^{( zXcCCww^LY#b%8Y7Dc9`BP$lzDfV)6mdqeXTsw)Px)koLxrIAPJnx#OOvmd_-9Y#f% zb-%1E<{5t-kKN}+rWd5}$&fEpu74BSVF?KE!6pBkE_d}v;F_~dh<#jWhDsjQ{@15^ zX9xCzh7}kEY$%FBfFDk9D_4tYic3pnPzd--(A$H__Wsy1O5u4 z#>Bdc@!q|jP0g#^l!6|&d3lH1+uNI)>6w`Ps|OY)YP}~XC&$LdD0%H)zjLd5IKDN?(}oO*zXMQfX`i_ba-vTb^@{=lnx1Mn=D0 zxzy!pQem66t}f4oj5L9x9eweXUkNuXj$yLf<7n4-(p&y1P*Lhf{9206{poT*qNZ+j z2M&%Tp5r;VC;%x|IYc}2^X=jQ)lwCPo9(+Q%qn~~jj=e!B1g1jBd8#cLTPIL#K_o) zh$a*)veY5_4T}i7>SK5wrRSBP)UB>}3aJln&~h_U{gYFf5vqaft9Xb9MegOIHm6^L z24T%8_aa62q69ipprp>zr^k1;=N~nJddCCY^?@3B8wJY3eUwV?nIBwpg9kncb z^X83QlBgeNjUoV3vyqY_J+0?+dvV@E{stv^ZEKms@>fy{ zip7~3CCHXQE*_f4DU5%4qpO#X+Q|Tqof(ON67cER~Elc zm5Oe2eE6JS!e{B8$(tzSIQP%BwUZ#=PhLAXo;15|Gu_m?Ln!9r;$mTu9Tyjum1XXY zfeXN(uG)BL@6%3l;ZK=N#SCGs$iq0DdQ*ER5p^OGlIEi)Z>Crfm+o&ugKzzj91Iim zWn#F4F+5Rvt0nS%80)RzCeND}n!3LaCvk~1HDDN#lYLalQRlpQbA7VjH#@oLDM743 z@kGOs;p#`*>A9~*z+Le_UQh&r7g|d9xsaGcVBY?yIgoN6w$R82>#leFBRxH9-&#Y3 zDFc?%dM0t_X`6$n^W>5+j`7p#ph@X0Fya!CC_!aze>zCet&CUhQDmS&Gpu7K3{jdX=r!$#G?S>vE^7cSdj;5iZp{}l= zt-*3f>xV(2oZ20~Dl(JmqoX6x7P9jPE9Y)Kz{UL<8XB5047HV--)-(0sG>rJCEeZK zUAGpU=Xr8!zHtgvyzi$)-RodZXXt`brfg`sihO@v~CgKduvoSBlJ=*J1TGqiT! z57tAK6{?10%A$AgYF2y1rUJPw_0T6ARcr{<5jO=Rr;;xiNJ~rm;!&Q%mQF}WP>FLR zHt$Q7f_ zj~8a+Jf*$uczNV|Ci*{E>yR1ukuozd#GA><$-xst{H<=^y7fV?(f{q+x5!c001*6A zBqMt_vegRbcnlx+lQG5^5XV5cj0VLL@(6L6bPV?$nKoFGYLri!B8X((L&gYZa*%}8 zw?1*#?|I+`Jkw}%##q#{aG3I)Oco_2OxpaHJnHDrabRY;eg=fH&F=hYpI5V;v7scr zm&0*}Uc|%*xJMZc$%goZ{{rJSX?A3U5)0G!+iok)HU*GpyB!}L`kYAv=h1O-LL!j; zV%{oTIb1$m32S?7njeK~O+jS{i~5G@e)`1S46pUxFh&JhH5)x+Z;$s;$o?&;3>c*c zxo2WvJyysulY9%q!lX->AnI3`3GbXB{-?Xld_izln;H*erbi-U&Oc(y%c! zTQ%1q*?&~Nm(NY)9bV(1MA=WB6bT}UoFcKkQiIlu%3>ZyNUp5UlQVs0V$DX3Ida1K zeS3uvf_xzOxSYlkVld-GLi4TM(NH#K1s6k7Y*6t4&jXhI)*SzVP{T1J~%jFV9K{EzEOl7pi%@CY-cQAd1+~5LxU{2YQh!Q z8%*5dW!rP*Wo7aLleP0TPtb^%6tO};ATykm7<7`5kPsHG1MUPgMwi3A$C(jvaWcU{ z?wVpr!FA%PYB>0~Z@2o-n2BW$vROs{^S8KpK9<@VWR{N@4z0&Zs^rXako{0VW_<_8yL_7~k{DfUJ9jwPK>u<6RS zf4yw6Ep7R9gP={Fm0tdYVzq}m4t@3k6#aPbCmI~@JoFOH4bvDTk0EAJ6%sKnNeA5P zuWv#?mN)qihORdk*`h%{z{QtN)~WVD!#D@lsdt2AHieFn*v|r1Mm_|c22wzO&|fMF z`IhIAQ2-g|pnQO6n+$riR;A11KPZ>zeHS+{wIM3gGZXBIm`FPGwJE`@(R?7 z@z??Q^I|y0AS7~Wfm0ayLsBZ%1&#tAWc<#Vj+yx-MgxGrhe-ntOT+vi)PJxZPko3~ zny`MYK<;Dq>Z(SG1r(zbpsbC3v`qpP3Qy)~pCo%F(tb`>eB0M8%0U)KnuJX=UA15; zoLR*rN;&A&t8cZ8TwD%y&#*1jnaafU$@7AIS|G+H@FEWzNpwDb{0Q~1*i8#|;d-O4 zcUo!ElmhLo-UP8fL9*E2+gtDAZftDqaTpx=X0i{@f2dPRXAxr;o0XN7mNv^?eIQ&| z^dc3Aqz2}6PI*D@9+s3oD6%-s`d?Ig2fJrcSw`?;a8AH8fQ2_6f2i9M@e)@$WbgNu z_c=o^?p{dtYaxUzTTMRd;%`pfG(J^x{#peTl_!P*C@TBnZGK@M2S>*=gSs)lu-I5i zYU-6d4v?6R@?86U63C|SlW{^U?r3jkW?@-O3KB?E8XS%O^yw2GC2uzsqZH@~vfzf` zdGx*8KS#ZIVW@BxcDR!buhXiolhel!A8uLR{WU@>J8RRRfA$+uQ^%#JGaP3t&) zWeTXg81NuqHo$8p!>`F@4G-X{=)bwzxR@Up+Uu!Er;RRrJQDFMn#F@+gV)4Upw(w~77}*erX*sh@wL&8yA+H1C zP*4kmpgS9+|UM``G_zP6H{c~y1j92zB1wfK7jO$$ltpE!(W8jw2b&vDdW86Q7 zkzi_j196Fkp57QrKK~DI&VYJw8bzdliAGfivbM8LBI*)Vl&j^BETQZG_~c#GC8i6^ zr@TsGl(0}SlhozJqod3P%X#Y*y-#Y!-Rv`n@8F3=^#P6(FqEnno2~UX$oi>bBR=6m zt4~Zgh9t-8FXrY<_DBf`>W$WjE+8UM-$98kUhiMJvo?fWlLTxaN#J?BKW>lebK8s#l0I#<~3%#Z+TD<-Bd7*n-0bxGktC_fPc5&WTSq-V3f9`e*X z#p5vf&861b$u<3Tg9Hh%sMy%73W8sQSta)?gk+E|e}3;ESiq97?YHvNItCIROWNom zUoCcA0w&4>CCdb(mi?(=qR5dQJ*hgC&(VV-CNa@p@pUrTILH=JS_W~OxQE9PhM#E*H;oG6h{b8^?O%tW0pvh|nVIoA5ZV3LGc`de0C=@l zuV_&^S(-WQyc@pqigOf6fZZe5!Fr)V)dHfdrHOG#P3$eB2Gjrj5BCFwA<0h=`bWcNv@R zyo-&Mn%4uqoW_B z_Dl>sA(!msSps!OYE-J z7$4;BD>{;>wbQ1cx7>@aMywChn3s&XU|&B4wxW=_*}PG21>yb6+K$*S?yp` zW;1yjJq^&Htl89aGQOKD3aenuE}ZZ0S5%K2m+}YcQCvK@zl-*oWmJ4kYuonXP(7YP z5&-W`p_7<`cQW1cwSE{={4TeprKFJGPr(KtB;%S%9V#u-2$st>2b)b)ZW#V zcE)b~o4EKbHb>x^!>MmO3601Aq_*FOYgl#e77k=8nGfb)$>}`lW|CGpaM(jHN!ZOW z>EHhwKQDeQRp|X1{QTWhi0XcwR62mix`FqQTriejy3VO?)v|mJQpunBdwVHHIyPis zz%?f7ou_u$BQecP8UQTdooj^^SitT|5E~DebzROCassTg)F4VeI_lzf>&tgVMVZEy zAsG6spxj7=PED^!IUJWYqbz%qTVXSs_ovrJyx*ris1g$u#VD7dAsS`>jaS|5?d`3s z*w|H)du|7g+D>iD59SapO*DM1_Vfh20s^J8vojgr`)-B!2!sC46M&8Hu(SHm;SCw< zEdxi4_n?9s&z-2?e}k`5&K@@>51(Y0!2Xx%=OoXVkY(;m#a{xc_$BJcfkHs>yf9f{ zv5d68#Q3Mv}_aNm+4c7wXcu5AVQg!KKT`)s)|W1E~3&Ds#d#6;7t%9G4$c} zPv5vW*=xY>1G>6>yFwzIDU263*A}Sh1@{P3Ozf%1$QG8DsV-gOwwb5_Ut4G!$Q}1L zD=RBe65jlBP4`^`1KLLK=VwXU#FqNfWYV5+27EE)Wo4Br8L3W3r$=*?Dzl%TT3OLa z-(RnRuJ;jtTd8gD>FLbdTEu+I;Hmt+R zM*d^~P~|wvZYzO~icPS#CBn~3w@)t&`zHf>Ce6+mYS`M^TCf5^MwfW-p#LbSj970( z<_St55y3|}D&iGSqK2r=o>{N1I`4kbCvj_dQN~s{po0#+jyB}ezKx^N ztVpf{F2oOTaU;C!P{67!GYc~fYn#-f<^=}fT2G-*Yr%9dCDGX z8mQXu2kF=l`PWetBLgNXub=yc0-tqtpMaTP*x@($686RT2LPZh{B7>=l1C9fhW`&- z1Fbp_y+Jvge|1X@+q(~yy7P46LPM{zG2A#q&%lbp;pg~Krs&`Ip~FlANd*Fr*xkDn zy!Q7sjqtFfWz~OoKIIiAJtTUd;R;N_=K-lVv7I{f%Z<-#)6J9=6gO|)Y(T$xZAidN z034C_`{?wmnKS7YE5hkLm|PZ9AH4AUnD4L*lOT{zgJOMVVtdVYQ$N(dZW z+yH<7#C}C^b+)I_Vb@|y;Ddt)5r$@jElp?&%m4fIvhrS$|1A#09dHDHd^C$k)YRMzsbRPEd&)}n@w<|YkLHM0 zuZazyu<3!TxqDaEAW`Lf_!)R%U<3j#%)M4Hj!{JN&*XcRJ|&n5K%vuAK#1;bL@fSJ zuC9GvE8tcpeEY69p$^QAvblp`JdrXsG|W*g5YCb9Ju4lSA#k{%;Li+Rl38ec22iJl zRAzwiJOH?uh6CoxzZVl3NfK}e1Re=&|HkVjl*Nx{MnC~N0I3d)5=G#jXww3UPSI(m zjcB+9Mo={tFp47hH}Yan%`tLuhv!~A;zC?UZH1#3=)bnY8)oju3;ljwWu>LKm#$w1 znsurHMY&A^;xUPMFQKkZCRMSwkVW`$tH0k!D1K0)fD9Uw`%;fc8 zbLwdyKu_Qc#KqtFl1i)t4+$RH{hAPy{f(@NP#J*>8s~H$!V0FIs7%v+iHaZ3OP}_^ zDVR^jrrv;nw7_kA?}E0|${2stAykB>yw|QZe)$q<1QN}-%MVuNI|Z^>(t9wfYVQrO zbCzjECTCIJ?X2@=L`3UqTP3_W(quo{Muc#GZf$)hVxW1yjuP7!VFkeQQqFD}tHX>> zADn2&Hr%X8v-2;78VvR?mdUoBi;}u zC{X)6;ZjUUNQf$3&zlIKgyA597MO}arv@cm_4L~MdSZ{Zvhruh7_f7j#s)>P7b>^M z0IU;BfywD@VheOXSCwHNoqo6-NKwWQvRB4mR^aWA9H4wVQ4UdHytojjqz#5MpImlB zzWD3$e+;01ZH9KGOI20XaGxEaDX;yU&?)b_b5fo=lfK!IY^I+)d9tXnDJ(3kDgb)u z%kpwpbjr+6W8rBhO>L}f`Jq|qQVf^~Ml6vR0t76ly_Kk>Zz2~0ON5$*Zv=tD2K#1Z zsE}R>92~3L+tve_;kUVa7K##q*I~bq{=0=m6Ae%=nFFu_E@kSpmt8v?()e4S5qgIf zXeu4mhiEx(%v~_dgi!kOEW)4ZA>?&_SgepkzCWEXU;}Xj;8ee-@nuv^{;P}zY#yqX z|KX~1oPd@lA|CVa`(A1g0WabG{r3rm|F5Dw!XoOZbia4h&`Q z5J)-sHzAv$!U#gOF3$6ak(ksJv?cHho|j~8y*QspTbltEUm6-cVA7{flcJ4S=yEdf zpz847`6}!RNYXGn|51mwwEu@*qXW_>so=>`LvbFt&N`v>qs0cDwLDj^23 z@hl$R*2czz2M^p}97K{D&;_~mMho{Z{KPQN;K+- z0+LIw4=QS~N`8duHo2EdI2@YNo_Z}szsI^zk7FY%qxh~Ti7v~3bUDm{VfNd%Z`~VX zm9CW)6&}Zjj>>@SATBB?2V7KyLfho7bZ`bVhTmmlB1@qdMWXp$|UdpDLwKHyXGHZXN#h3jE(CdJ3=F7#)FO*zf!z0xWe9JvW) zYf&wXLctIzhmq`oeqS_~C9mt2#lwe_t)cYubKg-q4=%Q}*%Ijuy))Oum35X>pK)bz zilnCwUM$w~lKX>O0FKD|$gTs?O&4bgE+UjBwhLQZZF2YLtSR@qw{KgGKn0U3WKobS z-X8ai3r|Z;mFKA@B?5vpB&06Y`5Vair*Y?QSqus#IhWjbx%o9M&LSej2b_%L8;_cD zRV{32APZ9SIbpH?!kih-6s-Z7d}cxRIV_lhbv^G;QCB#sh%yK*NJ=!* z%onTWkfJzkLlRxWKg$HggK0$9(y0~cE38j$Kfe#CzIwWW-~xyk*2xA21}!Vxq~tMg zpiE-dD(fE_;sK_DgClgk36Am15cOaBs0YLnu;!q+Wx`RP63Y?r*mJ|-DdqbbNn!WJ zy(=57YM-JLDh|@@%Jl)32WX z8+NLq+LZwZP$1k<3%Gr>7`h2ZqqvqM>1p5$aXH9wM<5NtVgvGe%9j^3O+wLhVTe} zFD;EGh+rx!^uRu0R!r}PqYloK;G}>x0TFHh28mH2l>s9SjBrE^%Ut2>q|T-^Bf zqh#iMOtuja@qn+0Boog48NQR4Pgvktz41M|x1C)wFfgRNd}BaG51kK78jy2F57Qr2j_(R4uZ) z0C5cmP8&Kp=E432qS*O+smrDrI4s}=Ii}offQjmLf17DqD4mqqcon~;CG7Zrr?`~8 zNH#6KXsrq-+xz#imO97B1sNH|lVk&?BAQ>3PA@Jl&d!R>R5F@L^a3iqGw{^g+q4iU zxZ8H$ury-ViL7{s&=xM8I&}(cPhtKT^h^nMV^>bqT!c|a@%T*zCe6f83DA9jh>ik| zIS=;1k<=BdU$y~O7M9!2>jow!aDe-#nlt1n2vu>Bkz(S0T%Bkk>O4zf%4xzSytqn? z1G4153D^tE5KchN{B1ADsQZ=8?h^PvCnNm8GCykT9}ob9FhM}jd^Z%Ior1eebzxy) zAmOZ;>DEJ`BRb6?ApsZ;DJ5H~N`&}?^QUlr>0oh%yCa4tl-=xCvf9n|0dNT5J(dnc zZ81LvKECL10E52hw8eJfANqj)DO^V#Rz!Jx2`z5lzRf_U_0i_Z&0cWBFs@B&Uri`x*^EmUMvMn@F>);Hd+le&1_ z>`J86z>)?T;M>cosVQ(lG#R6VHDnP|PJCco9QD4wo0(Z=c{yL+qqV7f#4yp<5&k$I4)RGq;s+DJak9X;R(2oC@iFCX7esSF|a9kAzsCxrtTGT6; zpq=M}3RQ7_t$NIT2SulOB`dSxgQJ&A1&#|SD-%o2e*bPY^gbm%{_@qU`mj?_9)~&* z_v#uOXJC^3pCh5>UIgM`71z#RBf_5AEb98h(|Mq7pu}{aOd?|9cRY_ok4WjwxbiSc zmY`I>&3R)-9Sgw>pczH4gk2G~i2@xY9P0mhI7^%O_~jc8V`)`YRUpG%wibB|+X$&* z7^F}|Arn(3@MU3DPD1j`Bu^C}H8j5YcWQtWqj6x43JMI|f`blgYYD9Rjx&tJAs~u2 zMx*&_q^^5o;sY9Tb#--|>%PBoVBT{?KJhOE zA9_zv1YcGi$-4#X8|Gh3E@6;;n(LY68G6aNzBK&KSCo3xj}qgiP9yTq{rO6DF=>3! zoZ})4ARe{AZEo&2KK4*&8Hr1U$lJiLs7O%q1N*Yhizvwi4hbon^!Mv0>GvC@D;Q`% zSJ$(qhJI-o7$ld<6Cki$HsG&!#k#8~9mxG6`)fo==TkVQ$+3Z$A$h!r8XVOEaJ>@)-;tq|MXYt( ztUvt%AtBQ0>qqzjB80`$)$fm9{Xn2dFbi!Lm%SGku0n7GIm;Z>;XG}Aw&2aPQF`<~#*!7f<`SUPWp{>8?@mB1mQ3d#Te zv4b@H)yLmX*o|L5M(~^V`~Ut81FwWyAmt^`zJhLW7o-=t@6JT-pOP?Y4Y{Uw0iKpq zKZ9@a34%pqjRJ*;H$e&>CaM((uXT1#PNj3V8*i|2!}*>cqwDkGD%uHK?3y316P;YK zNYu`m5rHQiCS=#11|Do#SVH6MRF_UZ9K@A?Y@iOkM!n)=e*L&WNeMWVqLzn?nWgZ_ zxgKO2C^uoG9UQogmQz!Y6q`qW{Fn}`SZwA1AkU7@=vyl}aO!Av;>i(>bdIL?mFQvy zXEaq`eE?mJnOwNXyp+FwSVEWMe2o7?bsHB@bb!YLIgy@?Z2`76xLkQ>aJV!Ijil7K zp{fdTsk@+j6zRnpObu>PX_^c{c57@9YILX`@`mWyZifKP+S@wyDN!|D7{rP=9-#@;O$G1Q3M|JBpuIF`~*Lfc2aU7>z>SBXBds%!}jk$ zF9I*e67)UhVI~4;^!E@!72JZnfuI&Pd3-DA;l2Mx4(FOHwNsAQq|TPhR6+gR!)N`C z#lR=)uO=sN_0AyZH6o(ITel|LQz|uNe~rG)Htx7MqQ;+cx*J&5Xv!Ng2tQN>w2z}0 zPT?ud#AyYeaGOZty?dItNO2rCJes~-!y8cQ_o`uZ1f>9y&I^615f)>RCFYe^+BTi^ zGY3%gQ7e=2O2czs1{B&ri^Oc((W5|)5(9}VwtGN76vO*nWaDQ*r)Q?w%p?Z=jNCcz zh^i2VIL7cpv2k%A9nPGh+}0@uz*EN{JKdS+=DRseN5|6j#kDmfJ+%cB{cju* z$Ow)gBe;jbLYoQ-3ne8aPz&Dcd_j)_;wtq{WKGz6i|6*9kT{sF4k&R%_N9L>e9lN{ z*)v7GLRMxf0R_IQ%^8Nov~Fo?8de53fUl2_vMZ+VxHlo zh}-xk%KGT`2j;YDsQp9Dw?$73_{~n0%%1dyE-X5QRuL%4snR}hTIn^~vm!!5K5e2^ zAEw^%4cJjz32&E9Frx z&4YQ%rqKhn^rl0)nQ6sjbv~P!^+A+(Cz4{9e@Se7vD>O>y zLyvzg`LIRwvV2!12*UHRPM=2IoKu_eFk8+>TvCK3Pt5V}oui<*)YG$|!0b2%fRZU- z`&Z)Nx2`&7^T-}o1L4SX_SGeo6qgOa*h*!n;SOA`uQERo?kV2{=1vC5cyThMd^McmjB}qT_^f`(0cz- zOds2ge@guA+(+FPb;3SpKM_Ij%kB98|L^Y~v*WMxUEyG#;z>m?cs>oApR+WqUr!+|8Z(Sfi|`cUO_*E% z`ERZ|+fu1;BSk%K8xPM?o`<{7SFtG=96r2m&6@q}UNs|Vzj9t2mN@3M|JQWz6ym!$YbuCsz$*Ws(7=6r1D_N-8Hc@kYYmLMZJ7Es0hH? zhOlLIUEnx#_3Bl0aP;n)y3f;g%)vR39XDULL~g^52uLWx9jJWG(f~9 zE+qw&ON)Yg_MufZ&JP%NWohXe3F@Z@33`y)8StFPo6`nw=)n=45Dn6ub(FP(WGZfIBdFX3-kxWz{H_qk^b5R)eFOb0Oza7joM0J?0P z0Fd_mM-^IiywcLKE&8~BP=OCuI{-hmqKeznGJz|a$hyGjpAL68Q^HqSDmv=jRQXC*&>PZtqJ^5P>(tPbsHcim)}-B`2`$GP%Ilp30-YU&XIP4^zHzFmx+lRMM#ISPQiFwGOWiFPU2Nm#9)@^E>sWGj-EUlZbdVk^-v>~( zF)xHi!^g)521*HG`G9iZzQNSP%U36MVQ!h~`gP0o4Ww7U)JZ>Fo|2L>`lb0sEe+5y zVq>je*jVe)oxGK$A6DobK|?Egg)w!FRW;h$-QG-e88??P+atb5n}4!oGv0Cdo@ba& ze3HFbn!)yxi<81?CYK)Qu$^~vad|UjbS^#k6gkn3h^=g2bcT^rtqSbRya!4q=cS3H z(TT%h&fEIf#862+iNhv`Ipg#*Z(5Nz9rune2^^l>lAEl5MzE-YW$jvF0N17&b!!8Y z&!DJBMn7?9b?m9vu@$UZYF4rdO<4 zSl`GfIyw2|W^aM@Rxu$Vj0x^QkVVC1WR3(smJI~6??xrEv)lTYI-g#qVdh_2fix1y zu*%$5S44iGs0iMj0!p5{pp~^&fLB!d6ZpGjW=qhvBhLv;l zEl~>LMwj9a8ra5Jd0Um5@9k-K@v5!&AC?kB{cyCMf;C-cjiWJYvIQR^SmU)qqF%xqtQFxc6ctf!AdA zhGd-EbbY$flF6#$91+_+hpW0xbY5KviT!CjrifN~{Dsd^ckbN5VaA8LclFI*l$vQ} z#$vbeh?KQAMJhji{0Kza@>B3P!HWcQz2kn`Bb_V>P>%A}ED9`#K^V|C58Uw4saEr3 zqW#tijrTk~JrS6+auP)dS<-RO19h-2%81kL0@Q7H?i>zDP|79Z==!S0u#25N4BEpx zV-`^((m6+Drkv=sBrsoLDU2uV2!ZFX;gU z7u}%`WMbyEf8&S~kunwi6(k^ZBIY^pGjD})R?lWn--M?Hf@9fU&eXTTv$gGUo(fvs zzm{<3T-`?>yy@o6)oa&o^1NB@&bg+5QD_;HoxFTbgy*JLo7X+Vhrk zvKk6$toAZI_nl_lJ{vW0xwoX>hF*4`+16< zF31L;D0N9z`anq~Fm^y~@S$tQRW&;5C?T#DP5P>^JTf#?E=h%6kz}uDS1+@=EvZ4! ztZ_ZXaxO(^y%&4N=Cqw8(n+v_DSKoR&nAf|M)`C5{G?Zlz;;Rd#s#2%iS29hI|gMELj4M)dzbz22Y-UqY%PBEfo&M8Jkl zy(J-A8gGO03T5F&z;^8HfsJMc=91#fN>cD9q&mxh%3&34flrvWT?;xX=r3&DvV~U| zWD2T5(f#ke1yIquz+myI*!FI=&+VPtN?sDWNh5W{hTDKWtgQM2sLUB_Ovg;{llP=x zQ%SsB!7lMYPT6?jwLM%^tzecVy5@jA{|?yvJ0EY8=O5O^J^{>v%CfFObuvf?U>iEw zcr6tw^9D2>bK<|?o1FLM^rdROck@i-2CtBM_$@7m&b8y5)w_GgvD=5(=kjJMUh{AV zHNIs$+$D>=k2c|5a8m0}OEiaC*#!?^$v}&pu$CeInu=E~+@WS*r8=0B4uAoxYijg5 zun73oNgeJGxB>1dax3z#`XOwa66O5wZ}RwRLiSRrXD^Uu?SQl6+&4gGYU1^;9|Usq zh?)_0mBp~DJhp+weiiUYyH(t9sQkNtuR5>pSaU`2_z8GZvi{>i&3ysRuP;vG{a5*333BY}A?*XFY{&N)f#RO7y8ku#8>#hV?%FIxQd} z-gY59G<3Abw7-|M?MWxuCa%!rBn}cdR4jG0>G|GX|INuKf3FR|l3VO>jN?(mY!Pz&N{8Hz(Gy>zM~_91>4Gd?H+- zb>}Z#dXkXP@xZ)l8{?q3TCDgkQNqsB5%+$7(IS7ff3G5r*vf&{@s0rr2b*O)KWisr zTpr+*Bb28DcuHCZQu3QDJ%P+lDRo>Vdf#d(!5_;upQ?^rvu+(>&2#(w+*zTzYcA3J zA*fSsS6@z1O4sw$_Q$C-%r)67nTF_7C` zk%?eCa7a4B$w>+4DU@b*<0L{N)WZ*~TgA+4=5}>w?)IMO)-|zdY9VVGQ>~g(`1MjI zT#yo1Oj5W~!C6?eggS$U;F``xnj()ax%3O53OqVLB<}YDV@Z2zQ2Z8`O8CU&r1iUd z`-K+035$tQc|V0S+%HAE!_!0aZ$Krj4VNXLL_okb?whz{`UFDG6^hBHxl?x_>LDN| z#A?uaoxi^Ojx4y@eF@FKPOQU8yJxlf2Mp!j^KIY0otrxibPy2CTJ|LFB%EAnk;>&A z-`u>@d09Go04J~jI~IrT$0oh2w`uR*C#c>l_axd;@V@hb;&#EI&pDlPw>2*Gt<-!A zjpLhHr1i64!E_RJ^n8#pa0skEQp$(^0`Yo_LidBBM$5p^Q!ck5G#X_3%aH3)J&hsW zyUQx)R)K)H(!|=$p=wBNkITs|l~02O9+X8292exqKz+j%fux1hafkxQukI>6;PM1>X5Tw||<68-4K@A5B&Bvtjl@q1+kLyJu4Y1sOEdYaK>0VOs# zdYUD)Q>o`W58+dmIo0kqm^r@FY-d-^D4fI0AxY`irW&IAdlPMoeV5=NH2G7R-nTLL zc4##Js`XpiJW5;>PV789&J=Q6w8-*!4>ybJ8Lv(lXw^(ixVX0IcWBXRNPl0Ny^Ior zy_O!9`tec#Ds~1Ztwl`*0+}plO&isB-iKUP-+SMr<>V9D2RVARN{V#c?BjHb}Y53GKlgj46!MJ+Qh z7liRe-(rncK(#3W?(ol2R8$0B3od`McGQc3scBfm^2pmzOIV*;mjCdv&I`9grXE72}t5CbOk2>00j3@uj|R zov4-WzI{|Ou|7cwiz`pUQOXRD!(!CJ^)6Vu9FGyEbTnKNVDF$giB$iQAE0HYZy%z9 z;0>2s=zB-Txsv5bfAO_%4(a9kWOl|R?C?LJV#Wa1`Uh8&QVb zjAeBzBqMLBe-O+eU&U<{Q_o-7+&m6Dx@7j@kr6m|JX&kS2zh1#OD1_mBD1w^e;8wK zJ-Qo(s}!?2M3=_eM4(G(+t`cKBfv} z?aDB8z9~;3ZEcDR=W?kJEA?AU$KKa`)T3rfK;e*X_V=q1`SJ=1_4V}x6CI#suUbiC;onw<*$NkMXeAf+-~%^V z;Y1Z@PaURBKS&NrsXjOr3$xa$$rD;;cG^yd(>6-hv9%oN$#qPS3NFz1zeuwaO+^~s z9p4MWy+SPbm7^;IkM5$A%w#ib%RJER*fNWp(B7~5(W$omZv|P|Gry5TMSN{PJND)Y zNR*Wj>KLO;`Jb~en|?OMtMDq(sP3wPO+ZkPt+r9})mQ(y(7(Ri^`K5a9be!SBGh{E z;jt!O!j?|#>^nfYg9SbD*mQ7SH7Rzv#bP(zH3h<7xPXC>^5yfLsXRx%5&&;OJd#pu z-)J%XtDDGA!Po+|{%i|b$zI3@9C6;k3owLq9Y$^hN^3G=?)2u3si$7&?`7L#Q|-ZY znp%d>zA1y)=fMw`4Cn6#IE?QR{`4sK6 z+%Vt7F{Q9UyME)7sD`B0obHXY3Y_jDC{tuh_;rG=@6RQzy!!#=>ENN$g5u(bb4sa4 zq0ctIIbSReaUHnF)%7Arr$=8qT4LmF-gL#|T3LgEu{s6Lx?yv)yoxU=D=J2cp3!%4 z5$|yO`fgVt-Nv z8)F?ekR&Z9^*@7i-h;{In44BL4Kk%=_CBfN*_kOc zLk4WRqnh8rHQs(TQDlv2C5?gwf-wXlX~y+9d?t-8#Y>P?kzy@4>JJ7W=&!K16# z_Ss#|kq8{*Z)@f^Zx;4$z4Eo@NAS_(6tTF8HQ)Bw2gDGV@Js@Lu78Z%8?r;csLm9b zB2t-T@5Pq$ zy~}vK4(i8AnFr*YH&q$^u&cp8>m*2%IRfEj+|%|E$V@i!WC@ zX>W*iOsAR6;?PrJ^pdoLz#i!2cMZ14%=c{?hj*tRY|M%3+~#*%G;*lYx~@*?wKHws z%d7c|o+hJ$N86UGC$~9eV#0X#nvUF65v$Pd>4&NMpcfJ9$po$U5b?-73{A)=0HZm3 z18`a#;4h7tyTP}exwWhCT`h=bLhs1W(gpwmDZV~hY>nz1DkeKSyAvl!)8~?*p@0wE zTRS%NP-7QaoZAU13bK-{SpAF^hC-q(1CHCYpNEzPpu8riKhU~yO1j9KNCXm+(~bFX ztw%&_57wYpJNfu5!TWess+6Au?+Qc-pk6tkV?%Ov>PpCut89(f^1UCd+lvh+Z@U)W za2j)5cW0|*)4PYKHKKhRM+}a-H)#$1V0ZHIi8D$O$vsxOxDD&WVCeKMRIsq+f?%CP znW{imP~iIAKK5HXQd8*E%_>9r8aXAz{nAK3Ce5e)H}|Jl-IUjnx1W(x2gshRsZf^# z5)kZ#w;7yBJLN2Uye5`jtBBhxB*VySE}q17PrpkVR=u!YxsEApvS4K=7vMCa;aZd= zFP6Qx&&k-`cgh|5P_Zdfg6=gS({Q`)X@Py z94G~bKV5Z>eR^sk(IAS=dkKU+AYAW;wUL|h2@_yV)}Z)8sSNCL{`&%AI_sm&NJ&Nb zpct$2|D!}D%1>z%RHJYtBdXD&2Ma}Kv968HH=v|W9VZsw6PPPdi65pa_NJIXjWqZB z_Gk7v=TNvutSAltcbSg&o<7Uh%5%fvrPV_{@L2)}Qb3 z2jN3uGvDPuG83V zrFceeZt1ALkIqtYxjPw2Ak50Z4MXGi_LIAGf0iO~l(+d8QPIg4xH2%VjwMgUtXHKi z?So9t<)a@Ds5p(zp@x5p$9WhtBQl}{DPAfLD&$S`rAmW)nI+gdu$F-p84$uq=?lWB zfF2aR@4+ZzCt}$X;pODokDlQR= z1LKYn^z#^V*J*);XU;q_+&rJlgMD*(F;(_p?worR?ju4tXpPqMX*08-CHO-dZdDv9 zhrju~z0v#TJBHBagy;`I*sOo?Hx*nV)HCK}O`{YdF9MpSPSe_2+Bk~BIG zDjoHem&b8c{rPsf2#*O_&ara%MwaAE!FFr#g>K0Fh@9rFQzE=EaK7cY77EN)EWu0F zuBkis0pb3~hGXs-1~+rF(V@@X-9t6lHD|mUAeyw~dVH#e&FGK-=WP){`l#K%i)DX> zz=Tp1EoO~Qinfy0=JZWJWe4{KjCp!enPv*CUz(1p*Wm(aqG1j7M|Fj)RCI;dRvV1> z#0aA=@k56wL2AA~#}!&b3Fa2(tKgv!-W?m9^e)`ZSh+jB)%~9bedxf;g6X=&^&K5Y zpIm@l$Az}LCSPpfOu)yNiMs=R;rC=qM%Ma6xc~dby?uQ0Eb6XQS(?wnQF>Sm9m)%! z!f?rZgOj`+xmf4{Ma65ex6G(Zm1gLhC{+=e(@-6d?6r(Z_H~|*3QJ6$VKrMdGVScv zik2~We96vt*`vf5>!?wQ_zQH?4xZr`_Eg+k+17AG@;^T4_YK_G)15KsslQ#N{uMC< z5B3KrvA=K>Ft$eCNN=R44B8}(FPGW_^?&R>Reh5)?B>lx*Rnn=0@+x?Gg<7GYO?M>9a|pZ8V1ryCR*@rKvT#s(gz@J z>OlPqq#o55()npLax;g}bqD!Ci#i<<)L)W(F`W&hA{VC|ItD&IK9FL8dq%`^8lWrF zhCMqe^nMA0^bqjqhF7?%En1E2f?rtsRmv~<*gvCu>I$RYUQnMWHTAs( zB=WOdaL7h*muw_C#=MbfBNOyysL0M)e>(}_6A_h&x%VD^FyZ6tE1C87Zc|npoqd+j zyoLOlI4MX}w6*Ku)k7}rgZAN1QnGV^+vEHhOtkS+?j5^K>D;!knGJ*)WB+C_by_MP zi7&KmFCS7*AgKY@ucx$FDB^5$?Pnx4$N&KM<#1KBDOdZQ37{>MCtxr`r~Yz6lhdUD zAe#RJAcdzE+AsU=3z}XO5cT#^F8P)Ag!XC3eENn}(3Eb@QPR@NztV8Wu)l~(`uZHy zoHEOGz*Crh4p-J(6H($Z{Yl>nrFiVo)r*JBe(#Fcwfc))w$I-}hayTg>`D1KX1gq% zEHZY4D!vTOJ0yrXrE?u8Gsj7{o;c&oidqdxCuMh5&mQ0~|A-eWcX;fvRaT2>r%`9* z$r|LBhop3PhvS)9ts^12C8dH7O^$!(yFsh$k5cMy*Q)&a4v$LD``CMT?rb09;NB#D zVdZ>GW&V!O|6t|(=ijx%14vw~__-7RgyOQ=lRGVx$w4_LQdwjt#KgFvVMz$Ew$};u@+D|qN_5#*ge{d?-r4x~ z+6bnW(ccgYRa@tmFH<-TL`&upJWOOUN3#Rn>rQ~j;{&a%jEuX%0})DZ+`o$(mj+NOXYc+EHdNAF91Kj7IR0wDQ?!^t!vKi&G ztxd<1BlyUidsRy<;anCP9eP7ATFP0-o25N%X>feJPzT(m8h{Hb`_t5Hyw-iE$D7r( z)7nV7;b6COhz~ss4Wu~7Ds%SXq@BWyWQyD44k(ot z9{to@ES2ih68X3mk#5C0CodH~`HF5*3-~@R?dpx!%!De2q|}F{6bV(=OIS#L61Hej zbx1UmXnwRyoOwj$Dpma7qwy!~rVkVs7enU>Q^v47?Bc4ftB^JSNb=?QiE$^W6F^Wm zkXXStu0@jG{jZ?!S6`0ec?S@iRuKF4a`}TRhZ(w$E=~^fXvC)+u*yJHS=q_N!jgy1 zTcxhvmo7s0z~mfu~l?Ab$lU6Y6El;9m3?z6`?~ zc2v5DP8dwAWn}DYM-R@zs{x5?E2QgLJE6=tA0nHz=k)81RwRUFqNl8(VL+V@Wq$p0 zKH<6_xTYVOtdEsjPBIg&K%zr${-wk)5ys} z%Xn8JR{m|FCuu*LhQgXhO3K*&38fxe3*ENNajO63$B>C+B3!_d0K+LTIpLParbUTn zhI0T&X5`T=ONUhdmF!rdVwqou;h&1y5Lila)%^L+4<`iEioO-z4%Y!g5sLSZPa3!3qbvPs zBPOmO+GIvXn7%OZ?D=yE;WdSD{0oVW{z;O8Nr-0)Nx#v3ZWU3}@ekv`nXj44o{})K zOyji*v?iCAG!Z{BPjPWkU`8=b&@ME=AAxVD^X#wjva&K5)q;Jk6^Riew{_C#+s1;W zw0?y~L;AbQDS>MHzwc1K9Uf@cAzMhE2nBZt-O>#kzPufT7U$m&;^vRJB?Jy1S2D)L z%jd|?vVFG2fpaBygHrgB*~f9KB8Jg8il z{sg!GC~z0(#RM&ch(wdbCB0(qdoR#UP>c#syjS*oK>M%1{(FQLXgvQX6)O!KP|1AjyAO0TK|5BMpj&(Xh zy{K>*vr#lF&^4I-*<4~;bUrYTD6%O)R&S2)SXN2@NaGxRO>=WT3KJxA`r4l~wa3c+ zcRQw59~16)B~XUWBTCpxlyF7ot&F1(!rq(+QYK*bR-)E{Bq=+g`(gmaF+)iXn5RH4T*evf<7vnbDhU%GrjrRm|wx z$_|%r#asaaW)5kWn^wun7E|a#lG)kVvIabd@3wtd)N&kZ74itd&uZP6t(@rw9~9tB zswyfN>cP#7*g{w!BJ2Wb06{DGOh8~Dfvo}2T*J&P|Et@_@%E7?7cYRcAjkzd|Kt`g zMgvn+D`=wW7z7HSZ7ez0CM`PavQ9V`;`+yVwoyU)Dc?(0yYF4bT;_Wk=L z?$nZ>{Q~^OXw_{it<@;D>LvFcc$v1eakf2p6t7QdqiadC0+=>+n3^>Ui@lH20lz7?RKjoVI?i_4pactIEd z>%!RJJY=VzQ9uH^$hl(;K0cWyB@hUPPQf>=uP~OT4-XF`x1{~be4}c&CA1P_L%2Rw zX2$9_k<|jmkTZ?J8aSw>T0An-M6VX96#MJlF8z&sIo*V#v4g?}@*CE;?M>wk?<8X% zp1`9g??QTvrfy~1*1{#GW|XYwYr?*8%&q8daC$>VhUy6e(N5;7^~Sc@2z zYsGo-(ia|d(I-8b?3E*^Q;#IP2!Qp+gxN!3mV20aF_29=s4KqyOJFo*)w!>zg}xdYez z@<5W&Qk&98|L7d0WnmSAU4)BT)^oM8~y+od`HKZjo^ZNS6 z*id#&?eqKi4@YK2yTNR6<7?Rg!mnI5JUlu&t4K(<=@9K+BYOWV2;4U_tHTQOe4_a| zOmnMV$`cYcGE=OWYCews#Do@iSyFkW-Wa4R4K<;mOf89x#85fJZP2C4MJ+VKv*e#B z-GB*IhUsI;d{T;3C$~TZPIiu4WTBw)$BM63r$b#hCGF@9nAq9FB(>sYmoh3mW7`P|*g#S9EBz>YFGkn)4jX4#W&!x&ru zVoIdPkEfQ9@#7!=?2(;?dFmn8Z<^Oq{Zghfw%iuo?OMnn;$o^eziHIxH%&S~o(Anm z10#}NoDI?Xl_Feaj~&}2$OTHyEapZl`X?-1K}(z5HX$%<2HrUk=ES@S0Y-Z}?em^0 zkygj~`$V0=3qYx%k3jV{ZR?@ycH&+k-O@v-7*KuG#d}Ov9VO@TMU=N#We%ZUyaT@q z9Ix-NZq_co#d-}cX}Y!G#Y}KJWDX(;P|cGUoatOtpkIQR84;a<#5;cds7@ zuj~AJajaap8jv}xG1>&tmD>HmgrSFF)W{o5a1d}4@Ol(4s$5#dXuRR(K5x_Gy<;i( zAH-_MzZ%Be`UVS!K&7`{4G3)AI|MT>4MujYJar5ZA=<8(Q>)dnh@*D+#UVTXdfSp; z+$CAO9!lUm+DKKw*$czDLOn!23vw(mAB-rBOL=MzH#YAUxE(HF{gi+QTOHKlyVg$y zqu8U}mvOGVURi_8_fJOO2N5|yD{dgfHKi_#o0~G~{x;{x?e=7s&?V+wZ!AVJayLUr zkearqj`8q1$=z}fpFPh!^t?fxRJu_hF2~h6RO(j2B<3(TRYo$lO==4vWx&kVk z^sV4vl2Fr53;Awb|C+4p3t=LH?BRkN5N32QFl?C|ItgOk573{&!-?5Q&u;9Lck(29 zL6XelUlLUbKYwnuXhJQmVq;)O+QlU3oDqWFT`_=B+)mK?7{lKKsc{Gq@tMaVlj;vF#;qb1mLI=Uk0#Rp3}HNvvOsUDZ7ko zC?{zc++B@0Nn)rFhMeE-F20GoYy5s0ht9wuO0YxIURds;^Z6FW$*@cya@r0RHQBL7 z9kfqvzrNTZ-m*kjNI^Yb`u%N#9c;IeQq8gM1S+?o9h>C49k$!!y3{;vUfQebfUYPw z4bMQ~lM)S|lzpw}rg4cXSSkohucoZ4tcn9*!^Oh`_vjHw%7Pz;Fm0dQOh{`wx83!3 zbmQ%-;Z(ZFPq|zl?=C4SQWSsbr=^x|GL7Z(Np5+meug+5t7!FAtFD;2OmI1I$ornM z4i|3wrgUZ3j$ejJwzYTlVdRX0|(Ti!lY|&qqFqoiZE0Z zHZjqkiwJTbZNOi0pU@=vMTEfn5|3FT!&Ol#><7B|36-`>k#-vQzFcU^Y>~>|Rr%HN+lj ze+^zIsN_7r`e{j{sGFF40N`gi1@=`k7wzClyG@n5x+yT9p%+|Ac&}BR!M}6uiF3{v znZ@|2$ut`V`4I(2{#EVgNG;&}?mGYVFwHSIIi>bFqRlmFS`l>zMZ?yPl!HY3E}NJt zdiwM%(97Viu8779OzhDr4|U}mWwf|br3D{vdxZnI>pAny$;?!3 zFI6;#fy33SnKEcKK&IMbZpTjVN~x-@n3QtgBe{GveqaL;n7K-MhMDG-Sqf;coUo)BgMeQ0>UEBzoN} z-98LupxA5ueh61WzT!NJl!2bpb@S#;Fe~1OMWBqI;{mFbDLU0xPY*u#mz)lkyg@p+ zTSCRD?|f}yuy#DN5trx;cv?%Et##|K`&?WrIEvFEjmmM7yXN4fhQkuB)Yg8VBf)Ad zA5IRH`;>@2stJv4ads=I`>Slp>ifVHD{XzMx?SZ0{XbtHe&4T;hUX#|rhoLB{Uz>0 z@I24b(=$r3X(9X3hMigC8V+NQswqw)LGCZ@LkakC_z%@Dym^|}Xl0$1R}clMOsH(g z*4-{;e)RXCMi`#ceZ)`e>+OEYGeMxh6mm+q$a3Bt3UsghkrEZx6SC;5AzmS+`!jub z1qC62IF5R+xF_BX^jWncSaIDV(RviuOw6rtXb`~(9CWY92thr`>uqL4MiU-V(oz$f zHwW7$w@QYAtkXTs(FT^R#YNBzC^it-%n|<3fzyKZg0+~K`2V)J!`lj5IFFEbKTav% zqob`&1T~=uh1x{Hx{W$E0rZBKLW3{59{TW!Kdjs{R<$8mC{ zm^`B19y#X6a&TJRdt>WfhbJ|uH0oCuR}jc_r;cg`U{Rg2kHOZOI;BYEJeU2hN-t67Mv}jq9PNqqICVimhFZg5_Av}Z6 z4q$(VVeiDg$vC$G3`;$}IjZ9Mj?YBgm^92vZI$s-R=%Kb5!N{IB|L!MBwtHuHygSU zCjCU{AZ1>Bhzy@zfTXbSTfH5VHN#v!|;gOIw2-THee@ zjJ35<+HPf;a+nPs|MYiLk?-}3G}<}BUf({V~hY=|M;Gqa((@_kg zA|(9u&pCZ>ik$IY&f(ID$-v>U9@kQ>6-teCn*1&HGz|kX@#ZM!BxZ_&Sf6A1n7^Y6 za&V1IzZ3gV14V2%eCBN&Tau+9$RTd$$#LMa-MmgHr3i(OBi2m2l0@hS1UxF`$xnq3 z)8)?hOh_x7(deqA8a~YD#h5J%DG&yEZ1E^la>Sz{UHk}-xq^=vXqXh^$ZCCKql9*Q zY4zpuJ(kv^3v|NRU?lXf2P+5{%98xV)`NA^YvPnC`!eYVh$-?RO}LVr+b{SEXsw62 zwS9G8@3$vYHvJ6pMob=6)pO1p8Snd@F6q0srhjMr54=Xw5 z$`(dTbm+p;jp6anx&S3~Wq39=_4>64M=o5%&%xS&+nWc5I}}Eo+MX)4yA-OT>(6B4 z&hdAD63$R)WKcBcs7TNJ)L8R2A56KoGgF%@)77aj-fde=S!oF* zEZ|_m{|86}4>xxuXD&9p0ikt3C{I;k&Fo}-W#Nn7oslGtOo%M~Ib3F0zB#32Dxzmz z{8l%2ON(0WzBx)^$iFCsULA|mE`cC9K3s$R7+qnl6GjO4I zNdmXgvGv=1ERNTK*~eCo(OvV@zEg3+;3m)kK=Q;M+ zn82=d%$M!s4f!aE=@an^bxF*p!~F|+vQk1%d*`fO@1a%D`_yTJUZBtK;4}hVNv4rM z3;FTMGWr4&30G1c-F)=N@i~tAK*RuDx!=Ow-M|YT$q_P*=-pYTZ%?ft$j5>6&6(fi z<0oH)D!Oj=J{TWu&@MWkpLg&eSB-CDsXH+yRhg+@``%UUMcFs{!j6Jb*d^_{TW{au z-O2;+`Trq;Sv(}PXi@&N`q}9lqrb-C`;niwK}>gW377Q(mC(BI50&t6?jNS%lAbl| zu7rrDP9hOsG@poHogSQ3rc}07s!Sg}aHV_tf$*cIKm0+aHEVXAs$Ms#AewlV@B4`2 zqC4@gAP;Grx)A)}-RU!QL8IqEc@kUoamnq zt)A=u?o)c?qD8Z^fH{2PsFw7_iQ4hF2M-$Eh_=zwfs=oGTR$16$~b3Lm0x%44aWUl z8}r(O5nx^7orV%X*!$<3O;Fff5$x0W`lUJb+BIV(zP&SV@AhLNMUPlm(SCh>eelXa zVg)Xk`b^gK3pe(FftVJJpz>!UEhp6!WN@Q2c?2^+Zf4wWRpgxydej{s;YA0pDf(5b z#zsZ}H*0}gGA&h=dUvUYnKB&7u}UStKYc>wzI4OdEb!wqF;GyhP2|UcGG+ zI$HpW6pt=lC!Id{LO}|iP&jZpjxS8>ywL+QKYsD`jw_!!{Moz&-9x9^&mA&cqx}dS z%_!T@<0C2<=6FdbLP(7&=?%ya zl+U`@96NRlls$txhr4(u${aueR{LV#-UCJ;{Y%+xl4!kxC4dyk4kRC#e5S0*k`@*Y z*1ugG9`{FGmHQ3?UA+JyCsVU|8e*dj1{-o~#8-RE=aB{?R|tLp;&z~j7=NS^I%hKF zsNLI+Y!GE7W_9S+>&`$uR(Es0EL$XP*GXmFus)?OO>xsoNkZ{Nua3TqGU#?dfWQT? zs8D}+VA9rEc*a0~mDO8zGp&Mex0GEQw59Uo{C@!1CPjns7@wE#u1g$9qA=#dtyRE$`ddFZ&dcw#FSB}6I$$ZM~p)EL4aN2Fq3m= zo}>~HWBIZ><^b6aopw?HlC6M|R>AIDR?JeJLfTI5fqa zpn?{}vV7B3uM$=&{)ZUI(%8%{8bq}OW@a&h8@a3`L&)f$ac}Wkf;j12NDwFE`KxNk zAGPgBy8ABh!Bs_jDG$dguqBlO+6cC!&~5(o*oSA|KftC1a*;!nFO|edRcPeH>7<|^ zx@62PDQRS=csGYUi-r|tZPdA%La#f?gs6+Q@yS!PxdOm#i5u z?a-xjR7`E%PWw5bloJYwzl6=Cy>>}a`X<=VBhLnrR!z#$tN(+Zx#C7x)-Wtd!3(zj z@aQ|5O%m=uMF!5J0c&JD((E*+t149LNEH}L~TJ)rj?ty zR!8V_`l_9CQCc{!vQ8SI&cU#Hm+vPyIXO*DV=U#HcCGoR;>JWWRlJQI5lhH&u+NhkIQY+sz#%(BQf!|6S+U0)xXVdXz}BEg zOiHPP`J~I>p&Harbo6GQL}l3kVn-J35n$ z*Qam0>I$mS^*=@T2cYE(*>%U#K5y{?V-x4d@Cr|xDtM|*Y^pAlyx!hzJw@Iag89*U zKyfs@) zN+A?F!>n!=dkOO_VooddDmLlK?hq5dsOXxdJaxDF)vMM@_x-2dCDW!7u9Vwnv*Im` z{EgOx31}Ixdsme4E*z*=P=bL6Wz2t&k(ikNBhOY1Gvuo_FZKLh66}Z(KW#C}On!U< z^<(PpzA;2n(Pr=90Md4ahw^THAJmht=ILdG_{4a4$d8a=;Y?OcGDzlHHk<-UvtNd) zSQQ4&VS{HfZL$zpQ^Dcw*WR=H+gi^eb2BqcEDr+rtizh+`w&9jh2LbaKwpiofZhn> zQal<=`M@e>aqcR5%v;se5Gic#?wgKbFR)q58=1dht6tfm2ly8N)`MTxlU`k8RIe5fpp_j8lUCgsn z*XNua>GVks;^$rXjj}h3zn}lUy*6`gTdH`5(h3gZ?H`7H1Mv$#);YO4_Uoe3Ok(f? z{y21Th58TTg>WW_7uma%2g}0${JK{I>Uz~r$EXC4S}q~=j*QNi-}ekH+YNXdvblx?tbe-eqrb)y7TR0H|H#(knESDIV^R4 zfH6<}&e-J~lVS3<{vz7vp+ejP>-Sh(F*Sya-Xdzhz#=0C)o2kuxWx_f+aGYv@nt>(Qz3JZ_t?O@T#dt3+co-NBq53_4Z%~|qQ8iFXKH|k z+pAU4E*QJmmjlx|Tsd{Zo_W4>>z_F@OKg6;`bvll&g|tP%)G$BJh+IqF|)Hj1{qnX zx}pM#AD#G-6PO;btZ@A3>`&GXCu~`?*+#^crz2JPIZF@^x--+`gH=EgZ%!m#tVvZd zK);f=N)Jge-pymGwWHBZisu924Q%GCz;CP^bXNLJ?6 z)mqf{y5~Ya75|MoBL+PeVwwspSp$EhME!VcY|A%DQs$kUnwZc`Cb3L%ez^IIvcuyR z=j$VSHXE*c`K!3^mJsHLCBRJ{YBkxc%Peu;KX~`K@eel1n0cMgWf#hr&>$T*eC+Ck z>SPY*TMB<5>L(!S2!)>%TTQ`UgZR%zg z-RyLC%>^Hx^66`S^gC-uCFMp_%6Ch%zd}^m&mQYyzi&Nhl{!ZH+mR5)cF`OV+U*afrpizgxJw#jOaj(uKn!S2@6hEoKs3pA3 z8f6v*zWl{JfjmtbtS4It#TKY6-9^3iuf3F1c4-CdC>~mTguahEPOhI{&3>{gtJ7>J zz0v2XJd~y(TTgoWv?Wcv3#Rc{>I_ENMIE>DR|8g3ckX;}In1pZ*=aW;`tv30(Uet2 zhV=({YVCqYqWxH`m}DRIb#@+W>9kYCM9x|_7K!qlc+n8G4RjHuw=bX5P!+40xa!}2 ztlL*d4^5NLew!I+@2)Y(HVQLv3Cx(fs>f$fRD!OUpxwUYrTEL6x{C(U{$Z(31!t#B4#_i6tW8})GmAAyX;dNE^F_) zxo)ge>njc~)uQODTQ*%y_>y?oaJXeG?R~rXotT)7C1zSPmbmrAf`r}vHH2`3Q4-L8(x8`kbYCx=hUqX4EOyb*da zPv|Ad`{O>v$T#o@J{lL>VSd+--zbxh`cbYn<6i28%tzcl@Jq!^GN@G8$gW&p76HkP z2wv_)DQeliAmR->tMN=V<|JAN{aDLa<4?s;54V8yoqk!C`s;_5*Ne;x#ntZDphXBU~#8fEgA9)xDJ2D zd=GZfV!M(%iyH(YIfUJbf$Jkz4Mq+^ikn`TlfUuUtwC1dANqkml4M*s`iB?5*61yW zR3*(Gc`SUBR(IVH*FOD|wJxVMVtLa&zKt0yo&1jD`96sFxp!;l_mYlo5$on=tGF{& zY#7_XQxr4*qFa8{;r%h}KYP@NWxt{8BQr5Z?2FyLd`l`^HH z`m5Z%{Kqj>yY3N23%)3_JO8q7-4jazEf#B=<+BIeTLkG@(HBJo>F!EO^kreK8o?fP-#(e0{udrh3P1(sbUgH|;4pQe}Bz zv*qxZN1S<|K9R`m@mZ|Q*_45ibWxYP`n zlfRzYmFZ&rvZ2<+ufaFw*YAtu-&2%Od0(dAJMJD*%X(O~NJOrGQ+Z+Mjznoz5&uJL zhHk|>-JI^X9FtNJF&MNS{oyoAv!vA8z`)8nCJ;|OWAJsW0pg~(WN%*bk>rl&+OG9i zSv>Tf5YO)rUHDA-jhTIxKd(uuE#NGp7OXnJdYdbb_oVBk9k0312kFuHcOIwZ>=sJz zQq=IMrW_b;d8hMjh;;ttmloJFzZvj;;*5v~CI8jByhF&Sc4lv|1&%%MfBf6TEHPCU zV6J;)wig|Te0(-Fkx{l3-6vcCy}jj++#|T(&60pT&hHgYzXE7PF6nj}%o)cBAnC>y zO@HK8#vIJdQiOxZ+=eRcy!Q!DI`i{XJq-dEy*ytZ@!SiA6jJyJbXm={rED68pep@z zUL2hbQt-#ufE<&PZhPI9a%B1PkwnQa0e_hHej=(f)s&}4$0a6RZ@pN%fknUCU7Gze zpu2y2iuSDb2})D4Nv~&cv>VrI9cA58VJAV}65)S+p;k~U;#V#MZ1P-}xbIo`E&cv= z$Mf{yCOVyDSrZWh_ENX%UOJ&IIJvmSmls3X&C}atpPajrcvx~Ifz4*i4xW6dnjyifAjtGvA1Yr(tzUd>+y|d3HF9kle-Tm7FP5@i|YXb;07nx0&X;3(9*5X~X>_o^21_0~WdQG(A0? zaJalMb7Wo7cHIU1W@`ALw!5CfsSCz|n$#duF zxif6%jr;A|8|qS29lho*o=8yJcsiGu%E?~-S&r6u#2`IVaB7e9>wsNruj{QxO~ggM zMl@-gUoUdVeAxN(aot0~pNj7#o_4Og@BUS$3ozlG+J+bMr^>`L_cl)5jLCdBGrr%o zbiK?v!a7vx2<3KSkleEwlLDr5$NE98%x~V{)Uam#eI^h_mfxypok#qL&!qmh&s?d) z{;N#?gPq88{&Ptv;#Z1(QqohHp%JztfYD*F!n}M^_Og-LC}0iFU{@JBq7`5T40tLF zKxA>-{c1F0+e)T(y+aq>1P}hQx?!Yf-fkr8=ySyc%Z}!pLbpoab{ZpR+ z@Ml^tE>9*?pJRggTd=ZHiz>r)xfn_c6;cQnCH=72*8L)i8-+gbPz;zxt&P>%ym0^I z=8-GU!ncPDJVnJ-dYEHdxZPpXrv5dopB*=HQBu;!q%ZetE53F2ocpGIvfk$7uk$m* zxrK#=UUw#@a(~EOd=6+?hMotb3HSWmr9{Qe*XHK9!00x#gNC2Mw30t5MPz2p4IIO% z(ctU4Ja3Wknt+~l?M#7;)bavJKK}Kg#E&w*>XTzHH3rv@d)ED8r$gUeluvM-(trwH zO)@+ukfgp}Y2X{%XPRsfTV&@F2;qM_vuP(vPqq3k&4Ln)UBdMn=mE*_u;29c!&mUn z7*ro#`@!}1=d`9Hzrgewpg#ZyHG0f+>RPohr zh?S_PHMO;OA#X={zck;x4_zZFOm};>JFxEca)+FkhnE+YNO$epdCGl?h7w!}Jg9cz zV6iAbRTEvfb9T*YH(*V{kY~&$C`fdMBHc+aRx2!C))?t(;@TZe15d} z*dw5)1^uJjjRyiWT3^0&M#n@TBX>Yd3cAN?+iahty7H{nFrdnY8oKMwp-tnEt02?@ z%HA>#y~Hhri@jDi%-2vdmvzGCGteCw2|B~`U7l#sZX=U)7C%`ocX%GXOwtWWZTbjW zCSY0}ZEDQpmK5&sAv(awqp4Nc9h;#MlaF~qqDq(Dz^)5eWDT3TT4lEw|p%iejK zRRW8fUH{>Kr9GLi#L%r@LP*OSPC`@C z^NoiiXNM|JQe&3*ZZ%7=OnxsUPCQMi8zfB?c$t?U6HbEpHIFpRCIVjomvmKg5d=O_$$??T>>yFiqwuw%&T zb6~#+4CLo#OYZ^K_b);gA=TsAYuCornObLitSr=Q@u>Mezypc|rA{p64 zi`%D@#n>)n=S?q6m;>gk(N4%CEOM<tmD*tQUwvz_{G)Dps?uZqJNQM!d`3GXmZ$r>5|R9PYJO|tnqkt* z4c&H$x^mQY0fWm#Oj8$A9#By?pyF@Lg(rbJugDibS>V)TbTu^STggw(=1OsWokFka zxow=&Lb|FKwE`bncW1!FzW$RwzrH`PJQ3wH#0=!%*(+NE(k>2A0vV3Kc8wXnS{xic zDC##1WW28m3N5ZQaeHHo$n9vcE z&xq-`+t}#oSF4U1k`p*GPia~BKdXN_Rj={N($m%!2Y#Eq{yo6fY?3DGUYWj&WF=7C+AiW5+VyRZy2$IL;?LFb|N>;yzIG6Q5 ziOL5W5D&x;NwqQ5)WF%1H>5pl+eWx73hescyIB|VH4bSf)}2LA@IdepIpUEx@r)rw zE7d~Vk`BYd_=DG(W{M6hlAHk1BdFy4Y~Wl4S;O47z|DR^|^ z_{iCTjFw-YK2O%nzLdkSVCvQK;_Qj1R}BIgSH6nUF8phC=%zrUfp$&NA|v(g3_G7@ z>eTO`W9On+1Atu z?++gqj6~OmQu2MK&VG0)FO+Fr#rtMWVlS_A7WQS*<>M+5N+P6rNvp8c`|J{d7=F6D zc**zoC(EC;^n-NH1>10a>c>93nyHAz8}XbCj9brt$%3DiyJ@YgG(Li>v0$XtzcQAz z9E^4OBgL9giHcgO&wNQfgpGRI*IOW|&nqM*W{`qC5pP5qd^Izw*ry({JcRX2d;z7) zq{oqbTsVO6-&EZ11KfD2)dh%ngZm+QQDr!NIwi0|r zN^X1+j>gD<`F#pZy3P;oUHLyh3ZE2m;lE#v8(+Epzpm+jzL0p^y(;SJZhlcZNI=f5vMDgmViW!Q;R%yWd!wcS{m*rl z31W(&p@Qn@QWo7|ixCUR5IRN~2KUOlIpISy;)nk2whq5?6mmiH&st4t-{vT0r;{g` zGlY!w3oTzWJ9Xu>Lm&XMClLc6;CKT@KFoWHng8mReLmi>o4ClIH;uCC3*5uuWr$h~ zgFxGhFs0gHlBaR|Ei#@s$?*9+rp?!AgtHcCW_X1pE>j8=n=(`G!S73jacV*o%4sDp7!{+$=HWc?^ZV2V*jR0QpQXb_!#~LKzuw$^;4-?l{0tpB zRJMpKg@#oLCY_yZV-66w368t!4HW0VDU8IavbRW{3~~4IQJ4%i?8PV~2J~~YETma7 z={73-ulJ=qJ7w6>xq;8fDZeAPQ)j?^v9gd!rCshzO}aB$J?79?s!v-#4xp2H931i^ zpPU`;-172=_kP8V!J7)pyRY1L@_*iU<$#acH;q$nZk6sDl~ZlUsNAj>5F^ANb(5gs zcWW9)62rr}H_esDd0=hx4m|QOwtru}95c@)HH(fuZHd)1H}`YcX>>r`ktr<)F%4aJ zb+|6Hz3}kxG~U$~31sX*kJ;GkqJAKOnEy2nS%l%A1&B_Ea5ic|rOz3zvdDYTtu8L;EZ|QYGrR(VFMTdoHYHHrx z=c_Q&dV8N}*vi6=Q5p=djE#|dnM|(>>j%UebzR-}SV=y&_m-vQh!GV7%o2YdUfBhM zg6|`-{~5!K$;mjWBW2!K{&;@@fD!{I=a&)(LmK`h!{UbeF$8k1-_PQ!gc#V1ncxp?xwhAjtkni zRf%{gOZEO+>aA^?1`jALa&ppSLw2JiYC=3CD(6)>B{1$L(ZmN4f1tMmU{@Tc8x|rMxjktL zh;YfRc=_M1H*T6+X=-f_d`BJl4Y!FCL6*euwX%HiJPCJY8F$637;Az5*10sXx}iTY zbdmAPV}w9z&jVy?%yy`T^#S(ZnCD746k`=Gim&yV?D^LUiFxQx1X&y!u=DFt{}XDr zsYKvB`0A?nR~WNek2C3553?@P&$q)nUo^w=X`*+fcxKqZT(0ziIaxE!sqzSe?{uQezvl3aC*@G75))E z*Zvhp5&bFtItG-e-265LGIH|pt)~7HzyJF6+Iv?h`TpNt0^Y38|4%?4m{CK>{U2}m z|HJicV!yC(I`f3q_FdX;WSXV2!_`?e->xul7WH}DVAz};!Iq8QW{cHd+Cg3t=BKwU z0?Hu8lX@5>4)L!S%DK7wN9&_jb9^vLd~msNs8>VK3X7o|p=NAs426V1g@sjT9!$1S zH)-CspxUK9L+@!LJ6V<{nGYE<)T^&1`4xkM@laM%BJce?kZN5wpN8hzGUrn~NkJQh zT0$d?y}s#NSCbvh{0HpJKXq+J&VKALbRbqXq`WWj)hr5T` zJPykV);_E+C!~)7LlarA802DqqaXW9TpW5lbNUT%89QlN{Pi0LC>Kl;a zC>{*4k+Gc!EGsrz>>{ktKswGP!w!D@`;d<{B-_ZxLL(y!kUC=lGmk>S8+8$M5hiB- zn7rEx6M21MZ**&*#6WZ4e8rH9EtKS--k@59?Zq}JhpUFJ2B*&B2yA>0P*iSP6v!Z+ z-_XFR9ykE^0h9Is#{<>`YDbXj&CTh$?v!eRx*G^<1O+rPVj4SDtgu&hOYk7@#GEY~ zVbp+*Vhwjq+e_?2!dd&-AEQdit0#s7W5fo^(|x{6H9X@fs>xXwV@DFd8;Co0k!N~Y zd)HjRSoL=Z@NgMNF^;Owmu2g|M;al10-3^_TnT^9SM{JUX^ zdrPIq!5pGixwP-02@diBK7wzUK_ARIEtLe^5N<+ukVZYl_57yP25MuD&70Ss-;r_E zGrNIlC^xUiuE{Qb@Ty_W7}IkmhXr0fuCJ-CgUSJXT!L4v*#bM8Is2dQanCu%=oa7O z{pCUbQ?lYbp-7{&p>iZW0EuBR%o}hyjl6bsmH;Yi745Tck5{DsqS?}nw%jFn>V!#- z(QerA3#Nokg{Z{$!db^bgPp{TUUb5Ywu6W~F!*MGMI{VQ0rD7sg8~G54vA@!dS4QT?B_r8-rN&b@qd*IIfy^(sz|R&2a~9rS1 zDTK6)Z2alex*qZORy1A5nG#95=g*%fF@REHO4EvUZ%fndtZkV&adG#luEFK!Oy~0K}l=uvC2VW#>)DQTlqS|4wIjF1zW>Cv%5h@PRP;x6|ZAlicrenF%6X3 zlT`duT;5QcV!`^KLS=nm2|T50?dI%;ka65*$EI@{XQ2heG1>JoCM8Xu*qcO%<5r0_ zAV2u^`9)G^_6BLuBUSf7KDwM>TFcFqCQkp#pTGkWdAO9Rq+rh%8$HIkz!N6bB#;D{ zwCe>Zg7BDW04o3)ENi{*S7|jPxu=g5HY=q}jhE&$JPvMdwDop$M8xx#FAvm&YX`)G zx~>x7V$>bmx~sp=x30&y-8_V=yUH_5nVZ6Faq|LICC1*VY|CZa=r~l6yaS?pznojse)YV)JBB6kKw-H97a*b6=9eY4oK-Hxv&L60*nIQ$UKD?cd6Bm60O z(8(DJVV?f7yDU1-y!a%kZ>~R~8$N4nAnn{YR?MALrjT+12W5-^&s5X67>}IERGAf% zE}~b^aOX#)%KD0^e(CX}Dk0r$-GrlVc(D$Y^fFL{sSr z=Qao=Av|U{`+yX0qXlku7+Ml!(!#f%>eE2B52+ZupI{>(AS%%u$@UuW16*-0b~4h| zu+UJV9c=I(G&|uG5(8y0A3!$ouF_4}DX+3yuCpFUo}fXRiq6Jj&4Bw=?ZwjXy=clB zLSBH&?S?6T@SXpCi@wy%MtP~DDz*d-5L*-#s#-2_Ee*FM_z22+>>8#K9XZGLvV1j( zIDM0I=PGadLMj!!aUjEkHfcxOfK(C`qDnd8-lDp}f?3&l$p<6qscqHJJ}e!K(uxMh zTo@_cRrdVMM@d~J!QWq&Su!@Zl0#hryG7r@U5)+bms?Z8`Z!l^OEYwE2w_C(6Rd`L zC`V0C)kXl7X-$@^0ID`9ehah|)6L@utwpszcS-|ql}C&PW0E&g9$WH?)$RWNK8wtz zg7*n52VPKBH|glex~CsMRr5lSlo;|6!)b2HxZJvZTU0_v&hD=64FCdaod=ken>{vj z_LZ-{JbO|#46Sr)=ECC4-=~=;XtrmacwqFrkj(58aAHTOknV)hfi|`%i+aMeki^fk zjMhdsyMVY}%E@2_KWBQGEidE6*OaPE&J4PnrVtAzO;US+nO{l04P~8rY_V54tTZ*> z-jhM@Jb&f@zf6DLq1u`&v8l#6`Z3ZM?i>XufkI~*0ySm!taaQp-%Tmx}ys;j#-&z*x= zhVJ%eUx4A{?CmSNTbgxZZ>d-=%+8Q&{z7*K+MscSSeTFr`Vvk8hmvkM#z-^kLb9^U ztaKQy8nKTD^yBkfar;6yG!e>%ST=p3dZpZf%QD>m3Q>W`K9xn{cca6J$u&Dk~2n7-#k`EeblKBp%B6e zd;~5>FbAvyUlZ$PMN=|Mzfb)f1YixnOSEkhgm$O z#`Rmb=qmL76hw6Nhg`(!ST)M1q@>P#)HWKRgq|X7rFj{%Japp46q$98CeiVh8g=Um zJ!Yhoc5J0$aH>4KL`8iLSjc>@CPR!VS(-ws@f3p8#GN}~>}|AEdi|0$ozWzth~8=e zziOV+AD_RF-wZ>!v*BL$#l|mW_`#Xg`BQFW!O@^dL8NWR-6t`7qjv6<1wvNHPQv5Y z+azw#JIJrjO=I$mZD8EsqF^>;jpJu7HekJAuQQKsk{$5ol&}e@RoPEe{&I4!hoh{C zOOsy7*9)vMJKF|xG5Pq=63pqv3%_hmW5%>9^L5BT3Pf#FFjWV~=MtdN+>hrDU;FY^ zIqq_ToJdmG6+BcVLGY5qTyu8*+Dg4vy$|OV!&W`^2mmlL$l>ZV$G~`%K}jGa66!+K zan=~pTC`HEQN1Gn4pUc;8BiW*45rf9F9Eg(?8#FC5)nh3Cz5k+zo$sEe$9P4lzw2N z#~xzVgOwEz%eB`iUSjq)a(-6jAASiwCuG#d)>g*xm9||r(qhbKz4VD@1zQ2XqoRJ? zKy<$e6pP~GvArw25Z_neX^oEfogaWjOqZj;iJ)c@ZvvJj8qBpCi(TPQ4Xu|%PVHC3 zr!iR^Cel@Tz(PgS9>aX3zbc~2MS9}%|HhecR{;+-?j7sF8(xMq`_FCor#vAR1Bshj z{ht@0vipM|0dL#?7dS@6ErKgtsX_xtoB#EKkPZLy*Qd*`Avv>Zc1ABH`X&onJ+9oi zG3rY8S)5k;O}7zAk^}M#%#a1;@a@-b)3QNcNM#Y>7}(g@wr(ZL3%z);M^(HM?(9S_ z!m}uO27%lYQ;qoQH-UB zu)pPzxXYVFnDs$1J#l8EX&KKD;QDS`v(wNkz>_QV8}b=tB_)*ae)=CrZ~MN-9zf|s z49R;NT^v~G4?lzb$v1!R*dK?ePIw{UXl2~vsYyOgdt!ht0%NP^u~L1JwL{?xxZDAx z$WNufhf3sR`SXT4iI;QHAp5@3E!{_r`|B}|sSkxWsVPXp}+*d4j)k7?`h+R`xsr=?OAHyxuYrHzt?s6PsQ-iv>U)UT!h($ z?5?ys!2Y?I3KaZ~FHd?2=1kSC;C-&~q!~ z-W{EtfRBpEkpaa_!LVIouh)nS(-t;ICqzm$$b(3g+m#nKxo1T)%V!HjZfi zg8Q2C>n@)s6XK_gKT1~JEsvRZUf|8B#NG?JFyB%_ZygvA;Eg?@ep}8H&Sa-}EPrfw zhVbZ?_l!njoG`JP0#5UcmI%v~WEDh&@H2ab}VCJZHLD}Vo zdKIjk)f2UlqJz+y+yYfapUQXeH$e`eQsME%Le2S#$sNX84vOCpon>|hyRIKN4QZLG z!cC|vSzhO}h#U@)Rjn=buWF+_&REZ7+5_mc!1Z!D3IPQrp-B_x&AO$!_Q3-1?6P@s z1?FdpTL*Oitz4on4Uu4BZ;wi)@`M=>i3%3(`T?&&&WuCkeQ6q1etFOCa{Z6{N^{F9 z2LpPMVaS8Uq$-9ZeSI?eDl$HsBt6|JDQWah-|<;*a~JWT;-eZM zzOIC*a2T`w*n)I0euC+GIn}Revj9DHb>4Yen|ot7@;N#WoKQ%Yv)B+CeSM!&7(Jq} zcSsB+pTg-$AXGeGkCfU_ryV_2E6oBwrG072yCSX5!1}>~B-rq&UgJUG_L*r*C!Tvy znUef|fo_8j4B;||y^0wOCWkc*iQyb{@?PI^q>V07t*TZTqpn?Ey5JOcj!LH$-h{)v zN~*OXw{0GgDCn-D&9kfpMQ6I~K_&8|onx6W`4ZSVVAC%6j>Ii%R(xzV)2_56XfY`* zT2xGzQRfO3%BDr7_G|;#uB3EX+Q@CIS>fS50Y+jU3adW@Z>?>)VJ0xk`Nr6sivJqm znn%q)e|+IwF@6p!YjJs%Pe9%(TQ904e|_Fhcc%NI%Zv1UN9hM$Oh&W2R_^&=!L-ba zm3!VKwsnHf>sYj)0P=Ju4@MsWk*$tut!csu(e|CCB6rm6VK)yfc(_&A#-0(-`un(aE4L#yu^Tk&J1McF%CbEC%g`?t!1N{c18vu@Uw;K5Ypl!h)ZFEktd1yG z!=%>s7q|AZ4Kbo^?Pl`Tt7WbOO0mR<8z93Gy*+8a-rrO+d+p?vE131s%-FE;=F5;>S-Q6WU#Bjd?A`tF%39??&A^>+8&8NNyHb$&fMXpDj8n~a zSN=AheVa#ni?mZbxfspKsMmAN%K=E-<+Wlwu7imVPA#pihi1zwCLT?q9R$uxhfB)z zQ-}rXFeH8C^{NSCR#Pu9nk`!vexV-;U6QX9&bZVJ@;S2@mc03`^b*VS`-85K-198g zm`V<@PQRK@{P;}Pxi`m(b>ll`{)$QQwSn9^-2_dNgL|O4tM|%6ytMGAQo*dDOV+7< zDd!tE`T(ZBbc?B4+rl{MyWY*+8X3&lZ&NHwy|md(h82e134udJ3Q%0b9e zMr|;HkoT5_*q@l)MR&E(QS`w#h8=fcumGi(Uye#MVsp85)O(`%EMDKSnQ^C*tlT@% zVfm+3AN_9RwLFhAAr^Yrp~sfjR~Gu?O+p?F@5-C69(WnP6TDM%Hil+^bqc6&kGz#H zyn(Yb>%q;@ zEpfA@v7uqHU1APz_>qbpjIqMagS~iKdOF;(x-1IrIlnW_ggXvo{2yUyK1D>xH(MO5vxvKQ`>aiOKv} z>%{+YV7{}n`;*=b0DDe1SKp99=&e3Fs_DJD9=H;}6Rl+F5C{W{-eZy+Q0kj74q+d# z=@m8Gn-%%g`(HPHlzFAhI86Tj^qQfEO-cBCJbVsm!<24o$y4O5nl7pE+45MJ9Q`Ol zmy2HR+z%&@l0|w3%6YM%Ax9yN{e$%-!d znXPjhlhk-pTxV!SZxx8(WWKzL^RcV^BlUm#eN6O)V<9Iqvk4%4sJ`Yhw`QChm^&Y^ z>duK{kSP8=_hl0tlktN>I~eLYUwOq7y59+Ta7u4Ugo*n96mZdAtzU_faFU#_xb)P z$v!ucsBCO<-+bX_sk2E7PkqpxCUw@zDzSH+--SRL=B>N!;3Xp!dnP-8ah=(Yq(;4U z(X8SW*1ymr39oxwlzoKzTJ2$jfhLXSd5lY4gcJsl%d@9XFFW--xKL3T5%|lDT*J-} zIj=2LE!~#n?o$qg+41Sqr4>g%IZ@`(_7Zis?10U222|ZVK}ZRF2QZUYiE;}bEVO#i zU;!JXNx)1N-1kHHE^sC5gT*4mB_Z`8#ZVT&oAqG-6VMnj3(qB6F(Ko_S2zoWCAOEB zq|Z$^5rZ;HD2SML5XH?rf-e!Vx{s)C;uRyL`TTgq4aI3ocP5D(t?2=(j7jy- zV)EJqAF}hUHsbO*(Vcd(Q}H^88)NY^T0=cmNp!033%_Ukq#ogBYh26nt|k7U7)&o>m~3NS+0g}84$-8KOgto>>+LHH|W3Zysuxogk7944+GO>##yBfnn{ z%1Z91j%o)E7L+&K0jl{BHNMyPNx_cCI?QEP5)uSEBr2|!^E{KW+L>hY=Z^i2hjqq}CExw=t$ma50Hw*Q2fNWFK;P6nF1`=^lUY4!OAw)d;2%(=U%cPvCuOr}h{~c!OhS>HAQf94Ud_ zvC__Ypi`U(?&Q?O_0oej3lo0k+JSM;`IN#~IfZ=#n0Wt28BxLF%!s(8(dV6cIpjWu zluvxbrlj|SU6DYAh)&2Nl1OgO4)xgP*qoifU|$e9d_#~cGL%@a7KDdXN{5w9)|j+V z3L0`q#|#GdAh8oLXWxPMDoXP8C3hTCIGkezM(d9J;sC$vHtwmUe4goe^vXWsEf z*g>#|^N}nE5~5)LH*#x8*A%qY7i% zcNLQCSyMY-PIHPK+Ybfx=NqTWd94i!o)45`L8yp7HwVXI?(b~09fvkFMH5-=E8D0$ zuPmO1KS3jaNnF8viAwOm3i_yU+G)DGwsMoEs&{*#&!ObR#7}MBzAjsXo(`(JHECS@ zpmMm8w8AWeP^c~;HVm~<@t0GFtX3(cA~gvgKmxjdY}Z-rkc(Nw%CK++0s?3C>w-oH z0$vkX`3_}f-4xXIE6FwEjLG=}G~8qlaFDgsGs@QnHmXHJDbbc5>t8g!x46Uh~kj_&jP zysh4jN#7+VE-;nF%?3yyHt`Pt4@r<+W9x%q8Al`f1Z1d#i#(k6!3nOizyP55j^^AK zwVTeREyZtV(qF7d>_KOj_9H`)Tq^I2kFR;k^&33`08Nnpt{j!c`T6;H__>KY%?AR+ zMZu9_7}C!OBLo10=t>>BIs8bwreFKe9*Qx@eX(<(GG$5Z2_)t{bW=$1M%Gg&>Sq(a z%qZf!u^>uiRnCcQAc>3vl>}&#zA=7(ZuU>x^72> zpoAq z{T+k48JJpz0SRHcUOzq;EV)PTqAd^MrLTY`I^b^1LqV4%XK=_J8Q?9brZ<`g+`PHx zOjfQd1=6!7KQwy!f*0A(B3@PdXgeH-;Nzth7duuXX|lJ%PJlj;Sv_!xGto+7oE+~ znq@yeJc-!Ntjn_(FTp9Av+4UoPFc^-vbdUV7BnD*L1ys5k7fZ-W_cnRHA4 zb-Ui5%}Ew~m-Q6KaaYB}lRjd3S#Hqw=!@!ATiH*w7T8ZBK&c^6f?74m+82tiN8h_t zkRa?-qM{G6)Tr`aC>_sYQMa;F?f)GWN4p`8YMKV;kSUrh=WCzed}xZdpap0XK&rDs zB)Nb8e%S2#WJD8W#%ZF8>M`v=zNS2c-(^KX$N(32&D)=>^L2O{g`k@*0HjY$>Z#1o zrivv9;NoH=1JD}g5*(6G~)+-UQ`TvYSwh09Z~Cw+>3)pbypP zU%H}4bY(`6mI2qGU7)0I!e6nj!1frC5>7Pc_f5Rm-Ki~!KUT*Doec{QzvI0j0+JC1b96p+s@gRr}jfhWF(2FwI za&fO-7trY#XAyhrAHCHIYX0YwiPs1Gn{pW6Xz+i&;a?xOay=A&P!}yFAmLvw z0doCYO`1F79KrB#T`GSAmZotT)0#_{coRv6@sXrcu|2CP7uiJqicm+6O7r z0{&MJ_VRs#f6Tx8;uE|Bej(}o#~=D=7y*VIb}a>Bxv2E|Z1eOv2lt_4HXt2(e)U*K zhrS>*98f0!Z&p5uHdSX)HY%n}{&Vt<->02DcRAecVOAe|d84dscLr3IMWX!N4+G+S zu~L|}6WcO|rybJuc1?RplDXxkS%oRjs>7U#UM zx^Z!_U)g>18QPd*AOT6JO$u1W_y@nCSbd{D7r6C&on3pHw?A?0e1PRU{g&XMAnb|F z)6jE7Cev!S%yKnDXi$$5<7| z?!39?eM{3wPSb?xoxt9oK@b)(%A!pZIW-R&OCST4RnCDa^Rn&jBnLVuz*a`#y
w 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" -skinparam sequenceArrowThickness 2 - -== libocpp wants to connect to network connection profile == - -{start} libocpp -> core: std::future(configure_network_connection_profile_callback(\nconfiguration_slot, NetworkConnectionProfile)) -activate core #DarkSalmon -core -> core: Setup network, e.g. setup modem -{end} core -> libocpp: promise.set_value(status,\nip_address, etc) - -deactivate core -{start} <-> {end}: ... possible delay ... - -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, \ne.g. disable modem -else timeout reached, next network connection profile selected - libocpp --> core: std::future(configure_network_connection_profile_callback(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) -end - - -== 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(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) - - -== 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(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) - - -== 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(\nconfiguration_slot, NetworkConnectionProfile)) (see 1) - - -@enduml \ No newline at end of file