Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

408 extend network connection profiles #769

Merged
merged 10 commits into from
Oct 30, 2024
18 changes: 18 additions & 0 deletions config/v201/component_config/standardized/InternalCtrlr.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
57 changes: 57 additions & 0 deletions doc/networkconnectivity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Network connection profile interface

Check notice on line 1 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L1

Expected: [None]; Actual: # Network connection profile interface

libocpp automatically tries to connect using the given network connection profiles.

Check notice on line 3 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L3

Expected: 0 or 2; Actual: 1
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.

Check notice on line 7 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L7

Expected: 80; Actual: 84

## 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

Check notice on line 11 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L11

Expected: 0 or 2; Actual: 1
be activated before it is possible to connect to this interface. To do this, you can implement the callback
`std::future<ConfigNetworkResult>(configure_network_connection_profile_callback(configuration_slot, NetworkConnectionProfile))`
WilcodenBesten marked this conversation as resolved.
Show resolved Hide resolved

In the implementation of this callback, you have to create a promise and return the future to the promise:

Check notice on line 15 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L15

Expected: 80; Actual: 106
```cpp

Check notice on line 16 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L16

Fenced code blocks should be surrounded by blank lines
std::promise<ocpp::v201::ConfigNetworkResult> promise();
std::future<ocpp::v201::ConfigNetworkResult> future = promise.get_future();
return future;
```

If the network was setup successfully, you can set the values in the promise with

Check notice on line 22 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L22

Expected: 0 or 2; Actual: 1

Check notice on line 22 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L22

Expected: 80; Actual: 82
```cpp
promise.set_value(configNetworkResult);
```

Check notice on line 25 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L25

Fenced code blocks should be surrounded by blank lines
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.

Check notice on line 27 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L27

Expected: 80; Actual: 111

### Bind to a specific interface

In some cases there are multiple network interfaces available and you may want to connect to a specific one.

Check notice on line 31 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L31

Expected: 80; Actual: 108
In `ConfigNetworkResult` you can specify which interface you want the websocket to bind to.

Check notice on line 32 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L32

Expected: 80; Actual: 92
Sometimes an interface has more than one IP address (in the case of a local/auto link for example).

Check notice on line 33 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L33

Expected: 0 or 2; Actual: 1
In this case you want the websocket to bind to a specific IP address. The `interface_address` in ConfigNetworkResult supports both.

Check notice on line 34 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L34

Expected: 0 or 2; Actual: 1
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).

Check notice on line 35 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L35

Expected: 80; Actual: 178

## Connect to higher network connection profile priority (optional)

Normally, when libocpp is connected with a network connection profile, it will not disconnect.

Check notice on line 39 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L39

Expected: 0 or 2; Actual: 1

Check notice on line 39 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L39

Expected: 80; Actual: 95
WilcodenBesten marked this conversation as resolved.
Show resolved Hide resolved
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.

Check notice on line 40 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L40

Expected: 0 or 2; Actual: 1
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.

Check notice on line 41 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L41

Expected: 0 or 2; Actual: 1
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.

Check notice on line 42 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L42

Expected: 80; Actual: 159
A call is implemented for exactly this reason: `bool on_try_switch_network_connection_profile(const int32_t configuration_slot)`.

Check notice on line 43 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L43

Expected: 80; Actual: 132
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).

Check notice on line 44 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L44

Expected: 80; Actual: 261

## 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).

Check notice on line 49 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L49

Expected: 80; Actual: 165

## 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.

Check notice on line 55 in doc/networkconnectivity/README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/networkconnectivity/README.md#L55

Expected: 80; Actual: 115

![Sequence diagram](networkconnectivity_libocpp.png)
WilcodenBesten marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions doc/networkconnectivity/networkconnectivity_libocpp.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@startuml
'https://plantuml.com/sequence-diagram
!pragma teoz true
participant csms
autonumber "<b><font color=red>"
skinparam sequenceArrowThickness 2

== libocpp wants to connect to network connection profile ==

{start} libocpp -> core: std::future<ConfigNetworkResult>(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<ConfigNetworkResult>(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<ConfigNetworkResult>(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<ConfigNetworkResult>(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<ConfigNetworkResult>(configure_network_connection_profile_callback(\nconfiguration_slot, NetworkConnectionProfile)) (see 1)


@enduml
60 changes: 57 additions & 3 deletions include/ocpp/v201/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include <ocpp/v201/average_meter_values.hpp>
#include <ocpp/v201/charge_point_callbacks.hpp>
#include <ocpp/v201/connectivity_manager.hpp>
#include <ocpp/v201/ctrlr_component_variables.hpp>
#include <ocpp/v201/database_handler.hpp>
#include <ocpp/v201/device_model.hpp>
Expand Down Expand Up @@ -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<int32_t> configuration_slot,
WilcodenBesten marked this conversation as resolved.
Show resolved Hide resolved
const std::optional<OCPPInterfaceEnum> ocpp_interface) = 0;

/// \brief Switch to a specific network connection profile given the configuration slot.
///
/// Switch will only be done when the configuration slot has a higher priority.
///
/// \param configuration_slot Slot in which the configuration is stored
/// \return true if the switch is possible.
virtual bool on_try_switch_network_connection_profile(const int32_t configuration_slot) = 0;

/// \brief Chargepoint notifies about new firmware update status firmware_update_status. This function should be
/// called during a Firmware Update to indicate the current firmware_update_status.
/// \param request_id The request_id. When it is -1, it will not be included in the request.
Expand Down Expand Up @@ -316,6 +337,26 @@ class ChargePointInterface {
/// \return vector of composite schedules, one for each evse_id including 0.
virtual std::vector<CompositeSchedule> 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<NetworkConnectionProfile>
WilcodenBesten marked this conversation as resolved.
Show resolved Hide resolved
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<int> 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<std::string>& get_network_connection_priorities() const = 0;
WilcodenBesten marked this conversation as resolved.
Show resolved Hide resolved
};

/// \brief Class implements OCPP2.0.1 Charging Station
Expand Down Expand Up @@ -413,8 +454,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);
Expand Down Expand Up @@ -820,6 +863,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<int32_t> configuration_slot,
const std::optional<OCPPInterfaceEnum> ocpp_interface) override;

bool on_try_switch_network_connection_profile(const int32_t configuration_slot) override;

void on_firmware_update_status_notification(int32_t request_id,
const FirmwareStatusEnum& firmware_update_status) override;

Expand Down Expand Up @@ -904,6 +952,12 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa
std::vector<CompositeSchedule> get_all_composite_schedules(const int32_t duration,
const ChargingRateUnitEnum& unit) override;

std::optional<NetworkConnectionProfile> get_network_connection_profile(const int32_t configuration_slot) override;
WilcodenBesten marked this conversation as resolved.
Show resolved Hide resolved

std::optional<int> get_configuration_slot_priority(const int configuration_slot) override;

const std::vector<std::string>& 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
Expand Down
8 changes: 5 additions & 3 deletions include/ocpp/v201/charge_point_callbacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cstdint>
#include <memory>

#include <ocpp/v201/connectivity_manager.hpp>
#include <ocpp/v201/device_model.hpp>

#include <ocpp/v201/messages/BootNotification.hpp>
Expand Down Expand Up @@ -84,8 +85,7 @@ struct Callbacks {
std::optional<std::function<SetNetworkProfileStatusEnum(
const int32_t configuration_slot, const NetworkConnectionProfile& network_connection_profile)>>
validate_network_profile_callback;
std::optional<std::function<bool(const NetworkConnectionProfile& network_connection_profile)>>
configure_network_connection_profile_callback;
std::optional<ConfigureNetworkConnectionProfileCallback> configure_network_connection_profile_callback;
std::optional<std::function<void(const ocpp::DateTime& currentTime)>> time_sync_callback;

/// \brief callback to be called to congfigure ocpp message logging
Expand Down Expand Up @@ -135,7 +135,9 @@ struct Callbacks {
transaction_event_response_callback;

/// \brief Callback function is called when the websocket connection status changes
std::optional<std::function<void(const bool is_connected)>> connection_state_changed_callback;
std::optional<std::function<void(const bool is_connected, const int configuration_slot,
const NetworkConnectionProfile& network_connection_profile)>>
connection_state_changed_callback;

/// \brief Callback functions called for get / set / clear display messages
std::optional<std::function<std::vector<DisplayMessage>(const GetDisplayMessagesRequest& request)>>
Expand Down
Loading