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

OCPP 2.1 messages #845

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/v2/component_config/custom/Connector_1_1.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
}
],
"description": "This variable reports current availability state for the Connector. Optional, because already reported in StatusNotification.",
"type": "string"
"type": "string",
"default": "Available"
},
"ConnectorAvailable": {
"variable_name": "Available",
Expand Down
3 changes: 2 additions & 1 deletion config/v2/component_config/custom/Connector_2_1.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
}
],
"description": "This variable reports current availability state for the Connector. Optional, because already reported in StatusNotification.",
"type": "string"
"type": "string",
"default": "Available"
},
"ConnectorAvailable": {
"variable_name": "Available",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
],
"description": "This variable reports current availability state for the ChargingStation",
"type": "string",
"default": ""
"default": "Available"
},
"ChargingStationPhaseRotation": {
"variable_name": "PhaseRotation",
Expand Down
21 changes: 20 additions & 1 deletion config/v2/component_config/standardized/InternalCtrlr.json
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,24 @@
"description": "If enabled we allow connections using security level 0. This does pose a security risk and is not allowed according to the OCPP spec",
"default": false,
"type": "boolean"
},
"SupportedOcppVersions": {
"variable_name": "SupportedOcppVersions",
"characteristics": {
"supportsMonitoring": true,
"dataType": "SequenceList",
"valuesList": "ocpp2.0.1,ocpp2.1"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly",
"value": "ocpp2.1,ocpp2.0.1"
}
],
"description": "List of supported OCPP versions in order of preference",
"default": "ocpp2.1,ocpp2.0.1",
"type": "string"
}
},
"required": [
Expand All @@ -847,6 +865,7 @@
"NetworkConnectionProfiles",
"NumberOfConnectors",
"SupportedCiphers12",
"SupportedCiphers13"
"SupportedCiphers13",
"SupportedOcppVersions"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE VARIABLE
ADD REQUIRED INTEGER DEFAULT FALSE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE VARIABLE
DROP COLUMN REQUIRED;
14 changes: 13 additions & 1 deletion doc/v2/ocpp_201_device_model_initialization.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# OCPP 2.0.1: Device model initialization and inserting of config values
# OCPP 2.0.1 and 2.1: Device model initialization and inserting of config values

If there is no custom database used for the device model, and 'initialize_device_model' is set to true in the
constructor of ChargePoint, the device model will be created or updated when ChargePoint is created. This document will
Expand Down Expand Up @@ -43,3 +43,15 @@ addition of a new one.
Note: OCPP requires EVSE and Connector numbering starting from 1 counting upwards.

Note: There should be no duplicate components or variables in the component config files.

## Required variables

There are some required Variables, which can be found in the OCPP spec.
Some `Variables` are only required if the `Component` is `Available`, for example `Reservation` and `Smart Charging`.
There are some Components that are always required because that is how libocpp works: `AlignedDataCtrlr` and
`SampledDataCtrlr`.
When libocpp is started and initialized, all required Variables will be checked and an DeviceModelError is thrown if
one of the required Variables is not there.

This also implies, that if you write code that needs a required `Variable`, when trying to get that variable with
`DeviceModel::get_value(...)`, you should first check if the Component that Variable belongs to is `Available`.
7 changes: 5 additions & 2 deletions include/ocpp/common/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,8 @@ enum class CaCertificateType {
V2G,
MO,
CSMS,
MF
MF,
OEM
};

namespace conversions {
Expand Down Expand Up @@ -577,6 +578,7 @@ enum class CertificateType {
CSMSRootCertificate,
V2GCertificateChain,
MFRootCertificate,
OEMRootCertificate,
};

namespace conversions {
Expand Down Expand Up @@ -633,7 +635,8 @@ std::ostream& operator<<(std::ostream& os, const OcppProtocolVersion& ocpp_proto
enum class CertificateSigningUseEnum {
ChargingStationCertificate,
V2GCertificate,
ManufacturerCertificate
ManufacturerCertificate,
V2G20Certificate
};

namespace conversions {
Expand Down
9 changes: 6 additions & 3 deletions include/ocpp/v2/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ class ChargePointInterface {
/// \param certificate
/// \param ocsp_request_data
/// \return AuthorizeResponse containing the result of the validation
virtual AuthorizeResponse validate_token(const IdToken id_token, const std::optional<CiString<5500>>& certificate,
virtual AuthorizeResponse validate_token(const IdToken id_token, const std::optional<CiString<10000>>& certificate,
const std::optional<std::vector<OCSPRequestData>>& ocsp_request_data) = 0;

/// \brief Data transfer mechanism initiated by charger
Expand Down Expand Up @@ -363,6 +363,8 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa

// states
std::atomic<RegistrationStatusEnum> registration_status;
std::atomic<OcppProtocolVersion> ocpp_version =
OcppProtocolVersion::Unknown; // version that is currently in use, selected by CSMS in websocket handshake
std::atomic<UploadLogStatusEnum> upload_log_status;
std::atomic<int32_t> upload_log_status_id;
BootReasonEnum bootreason;
Expand Down Expand Up @@ -400,7 +402,8 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa
// internal helper functions
void initialize(const std::map<int32_t, int32_t>& evse_connector_structure, const std::string& message_log_path);
void websocket_connected_callback(const int configuration_slot,
const NetworkConnectionProfile& network_connection_profile);
const NetworkConnectionProfile& network_connection_profile,
const OcppProtocolVersion ocpp_version);
void websocket_disconnected_callback(const int configuration_slot,
const NetworkConnectionProfile& network_connection_profile);
void websocket_connection_failed(ConnectionFailedReason reason);
Expand Down Expand Up @@ -562,7 +565,7 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa

std::optional<std::string> get_evse_transaction_id(int32_t evse_id) override;

AuthorizeResponse validate_token(const IdToken id_token, const std::optional<CiString<5500>>& certificate,
AuthorizeResponse validate_token(const IdToken id_token, const std::optional<CiString<10000>>& certificate,
const std::optional<std::vector<OCSPRequestData>>& ocsp_request_data) override;

void on_event(const std::vector<EventData>& events) override;
Expand Down
7 changes: 4 additions & 3 deletions include/ocpp/v2/charge_point_callbacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ struct Callbacks {
/// \brief Check if the current reservation for the given evse id is made for the id token / group id token.
/// \return The reservation check status of this evse / id token.
///
std::function<ocpp::ReservationCheckStatus(const int32_t evse_id, const CiString<36> idToken,
const std::optional<CiString<36>> groupIdToken)>
std::function<ocpp::ReservationCheckStatus(const int32_t evse_id, const CiString<255> idToken,
const std::optional<CiString<255>> groupIdToken)>
is_reservation_for_token_callback;
std::function<UpdateFirmwareResponse(const UpdateFirmwareRequest& request)> update_firmware_request_callback;
// callback to be called when a variable has been changed by the CSMS
Expand Down Expand Up @@ -145,7 +145,8 @@ struct Callbacks {

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

/// \brief Callback functions called for get / set / clear display messages
Expand Down
11 changes: 9 additions & 2 deletions include/ocpp/v2/connectivity_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@

class DeviceModel;

using WebsocketConnectionCallback = std::function<void(
int configuration_slot, const NetworkConnectionProfile& network_connection_profile, OcppProtocolVersion version)>;
/// \brief The result of a configuration of a network profile.
struct ConfigNetworkResult {
std::optional<std::string> interface_address; ///< ip address or interface string
bool success; ///< true if the configuration was successful

Check notice on line 21 in include/ocpp/v2/connectivity_manager.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v2/connectivity_manager.hpp#L21

struct member 'ConfigNetworkResult::success' is never used.
};

using WebsocketConnectionCallback =
std::function<void(int configuration_slot, const NetworkConnectionProfile& network_connection_profile,
const OcppProtocolVersion version)>;
using WebsocketConnectionFailedCallback = std::function<void(ConnectionFailedReason reason)>;
using ConfigureNetworkConnectionProfileCallback = std::function<std::future<ConfigNetworkResult>(
const int32_t configuration_slot, const NetworkConnectionProfile& network_connection_profile)>;
Expand Down
3 changes: 2 additions & 1 deletion include/ocpp/v2/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ namespace v2 {

/// \brief OCPP 2.0.1 defines this as 5600 but it can be set to a higher value, which we do here, if it's reported via
/// the device model, which we do as well
constexpr std::size_t ISO15118_GET_EV_CERTIFICATE_EXI_RESPONSE_SIZE = 7500;
/// 17000 is the minimum value from OCPP 2.1
constexpr std::size_t ISO15118_GET_EV_CERTIFICATE_EXI_RESPONSE_SIZE = 17000;

} // namespace v2
} // namespace ocpp
54 changes: 54 additions & 0 deletions include/ocpp/v2/ctrlr_component_variables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,63 @@
#ifndef OCPP_V2_CTRLR_COMPONENT_VARIABLES
#define OCPP_V2_CTRLR_COMPONENT_VARIABLES

#include <set>

#include <ocpp/v2/ocpp_types.hpp>

namespace ocpp {
namespace v2 {
///
/// \brief Required ComponentVariable.
///
struct RequiredComponentVariable : ComponentVariable {
/// \brief Constructor
RequiredComponentVariable() : required_for({OcppProtocolVersion::v201, OcppProtocolVersion::v21}){};

///
/// \brief RequiredComponentVariable
/// \param component Component
/// \param variable Variable
/// \param custom_data Custom data (default nullopt)
/// \param required_for Required for which version. Multiple versions can be given.
///
RequiredComponentVariable(const Component component, const std::optional<Variable> variable,
const std::optional<CustomData> custom_data = std::nullopt,
const std::set<OcppProtocolVersion>& required_for = {OcppProtocolVersion::v201,
OcppProtocolVersion::v21}) :
ComponentVariable(), required_for(required_for) {
this->component = component;
this->variable = variable;
this->customData = custom_data;
};

/// \brief For which ocpp protocol version(s) this component variable is required.
std::set<OcppProtocolVersion> required_for;
};

///
/// \brief Required variables per component.
///
/// First value is the 'available' variable from the specific component. Second value is a set of required variables.
/// This makes it possible to check only for the required variables if a component is available.
///
extern const std::vector<std::pair<ComponentVariable, std::vector<RequiredComponentVariable>>>
required_component_available_variables;

///
/// \brief Required variables that should always exist, regardless of any available or not available controller.
///
extern const std::vector<RequiredComponentVariable> required_variables;

///
/// \brief Required variables of an EVSE.
///
extern const std::vector<Variable> required_evse_variables;

///
/// \brief Required variables of a connector.
///
extern const std::vector<Variable> required_connector_variables;

namespace ControllerComponents {
extern const Component InternalCtrlr;
Expand Down Expand Up @@ -88,6 +141,7 @@ extern const ComponentVariable MessageQueueSizeThreshold;
extern const ComponentVariable MaxMessageSize;
extern const ComponentVariable ResumeTransactionsOnBoot;
extern const ComponentVariable AllowSecurityLevelZeroConnections;
extern const RequiredComponentVariable SupportedOcppVersions;
extern const ComponentVariable AlignedDataCtrlrEnabled;
extern const ComponentVariable AlignedDataCtrlrAvailable;
extern const RequiredComponentVariable AlignedDataInterval;
Expand Down
8 changes: 4 additions & 4 deletions include/ocpp/v2/database_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class DatabaseHandlerInterface {
/// \brief Inserts or updates the given \p profile to CHARGING_PROFILES table
virtual void insert_or_update_charging_profile(
const int evse_id, const v2::ChargingProfile& profile,
const ChargingLimitSourceEnum charging_limit_source = ChargingLimitSourceEnum::CSO) = 0;
const CiString<20> charging_limit_source = ChargingLimitSourceEnumStringType::CSO) = 0;

/// \brief Deletes the profile with the given \p profile_id
virtual bool delete_charging_profile(const int profile_id) = 0;
Expand Down Expand Up @@ -196,7 +196,7 @@ class DatabaseHandlerInterface {
/// \brief Retrieves all ChargingProfiles grouped by EVSE ID
virtual std::map<int32_t, std::vector<v2::ChargingProfile>> get_all_charging_profiles_group_by_evse() = 0;

virtual ChargingLimitSourceEnum get_charging_limit_source_for_profile(const int profile_id) = 0;
virtual CiString<20> get_charging_limit_source_for_profile(const int profile_id) = 0;

virtual std::unique_ptr<common::SQLiteStatementInterface> new_statement(const std::string& sql) = 0;
};
Expand Down Expand Up @@ -270,7 +270,7 @@ class DatabaseHandler : public DatabaseHandlerInterface, public common::Database
/// charging profiles
void insert_or_update_charging_profile(
const int evse_id, const v2::ChargingProfile& profile,
const ChargingLimitSourceEnum charging_limit_source = ChargingLimitSourceEnum::CSO) override;
const CiString<20> charging_limit_source = ChargingLimitSourceEnumStringType::CSO) override;
bool delete_charging_profile(const int profile_id) override;
void delete_charging_profile_by_transaction_id(const std::string& transaction_id) override;
bool clear_charging_profiles() override;
Expand All @@ -282,7 +282,7 @@ class DatabaseHandler : public DatabaseHandlerInterface, public common::Database
std::vector<v2::ChargingProfile> get_charging_profiles_for_evse(const int evse_id) override;
std::vector<v2::ChargingProfile> get_all_charging_profiles() override;
virtual std::map<int32_t, std::vector<v2::ChargingProfile>> get_all_charging_profiles_group_by_evse() override;
ChargingLimitSourceEnum get_charging_limit_source_for_profile(const int profile_id) override;
CiString<20> get_charging_limit_source_for_profile(const int profile_id) override;

std::unique_ptr<common::SQLiteStatementInterface> new_statement(const std::string& sql) override;
};
Expand Down
31 changes: 31 additions & 0 deletions include/ocpp/v2/device_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <everest/logging.hpp>

#include <ocpp/v2/ctrlr_component_variables.hpp>
#include <ocpp/v2/device_model_storage_interface.hpp>

namespace ocpp {
Expand Down Expand Up @@ -135,6 +136,36 @@ class DeviceModel {
bool component_variables_match(const std::vector<ComponentVariable>& component_variables,
const ocpp::v2::Component& component_, const struct ocpp::v2::Variable& variable_);

///
/// \brief Helper function to check if a variable has a value.
/// \param component_variable Component variable to check.
/// \param attribute Attribute to check.
///
/// \throws DeviceModelError if variable has no value or value is an empty string.
///
void check_variable_has_value(const ComponentVariable& component_variable,
const AttributeEnum attribute = AttributeEnum::Actual);

///
/// \brief Helper function to check if a required variable has a value.
/// \param required_variable Required component variable to check.
/// \param supported_versions The current supported ocpp versions.
/// \throws DeviceModelError if variable has no value or value is an empty string.
///
void check_required_variable(const RequiredComponentVariable& required_variable,
const std::vector<OcppProtocolVersion>& supported_versions);

///
/// \brief Loop over all required variables to check if they have a value.
///
/// This will check for all required variables from `ctrlr_component_variables.cpp` `required_variables`.
/// It will also check for specific required variables that belong to a specific controller. If a controller is not
/// available, the 'required' variables of that component are not required at this point.
///
/// \throws DeviceModelError if one of the variables does not have a value or value is an empty string.
///
void check_required_variables();

public:
/// \brief Constructor for the device model
/// \param device_model_storage_interface pointer to a device model interface class
Expand Down
10 changes: 5 additions & 5 deletions include/ocpp/v2/evse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class EvseInterface {
/// \param connector_type The connector type to check.
/// \return True if connector type is unknown or this evse has the given connector type.
///
virtual bool does_connector_exist(ConnectorEnum connector_type) const = 0;
virtual bool does_connector_exist(CiString<20> connector_type) const = 0;

///
/// \brief Get connector status.
Expand All @@ -54,7 +54,7 @@ class EvseInterface {
/// \param connector_type The connector type to filter on (optional).
/// \return Connector status. If connector type is given and does not exist, std::nullopt.
///
virtual std::optional<ConnectorStatusEnum> get_connector_status(std::optional<ConnectorEnum> connector_type) = 0;
virtual std::optional<ConnectorStatusEnum> get_connector_status(std::optional<CiString<20>> connector_type) = 0;

/// \brief Opens a new transaction
/// \param transaction_id id of the transaction
Expand Down Expand Up @@ -228,7 +228,7 @@ class Evse : public EvseInterface {
/// \param connector_id Connector id
/// \return The connector type. If evse or connector id is not correct: std::nullopt.
///
std::optional<ConnectorEnum> get_evse_connector_type(const uint32_t connector_id) const;
std::optional<CiString<20>> get_evse_connector_type(const uint32_t connector_id) const;

public:
/// \brief Construct a new Evse object
Expand All @@ -252,8 +252,8 @@ class Evse : public EvseInterface {
int32_t get_id() const;

uint32_t get_number_of_connectors() const;
bool does_connector_exist(const ConnectorEnum connector_type) const override;
std::optional<ConnectorStatusEnum> get_connector_status(std::optional<ConnectorEnum> connector_type) override;
bool does_connector_exist(const CiString<20> connector_type) const override;
std::optional<ConnectorStatusEnum> get_connector_status(std::optional<CiString<20>> connector_type) override;

void open_transaction(const std::string& transaction_id, const int32_t connector_id, const DateTime& timestamp,
const MeterValue& meter_start, const std::optional<IdToken>& id_token,
Expand Down
Loading