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

Support OCPP 2.1 variables #988

Merged
Merged
Show file tree
Hide file tree
Changes from 10 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/v201/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": ""
},
"ConnectorAvailable": {
"variable_name": "Available",
Expand Down
3 changes: 2 additions & 1 deletion config/v201/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": ""
},
"ConnectorAvailable": {
"variable_name": "Available",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE VARIABLE

Check failure on line 1 in config/v201/device_model_migrations/3_down-variable_required.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/3_down-variable_required.sql#L1

Expected SET ANSI_NULLS ON near top of file

Check failure on line 1 in config/v201/device_model_migrations/3_down-variable_required.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/3_down-variable_required.sql#L1

Expected SET QUOTED_IDENTIFIER ON near top of file

Check failure on line 1 in config/v201/device_model_migrations/3_down-variable_required.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/3_down-variable_required.sql#L1

Expected SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED near top of file

Check failure on line 1 in config/v201/device_model_migrations/3_down-variable_required.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/3_down-variable_required.sql#L1

Object name not schema qualified
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;
53 changes: 53 additions & 0 deletions include/ocpp/v201/ctrlr_component_variables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,63 @@
#ifndef OCPP_V201_CTRLR_COMPONENT_VARIABLES
#define OCPP_V201_CTRLR_COMPONENT_VARIABLES

#include <set>

#include <ocpp/v201/ocpp_types.hpp>

namespace ocpp {
namespace v201 {
///
/// \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
31 changes: 31 additions & 0 deletions include/ocpp/v201/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/v201/ctrlr_component_variables.hpp>
#include <ocpp/v201/device_model_storage_interface.hpp>

namespace ocpp {
Expand Down Expand Up @@ -136,6 +137,36 @@ class DeviceModel {
const ocpp::v201::Component& component_,
const struct ocpp::v201::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
5 changes: 5 additions & 0 deletions include/ocpp/v201/functional_blocks/diagnostics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@

/// \brief Callback function that can be called to clear customer information based on the given arguments
std::optional<ClearCustomerInformationCallback> clear_customer_information_callback;
const bool is_monitoring_available;

Check notice on line 74 in include/ocpp/v201/functional_blocks/diagnostics.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v201/functional_blocks/diagnostics.hpp#L74

class member 'Diagnostics::is_monitoring_available' is never used.

private: // Functions
/* OCPP message requests */
Expand Down Expand Up @@ -109,5 +110,9 @@
void clear_customer_information(const std::optional<CertificateHashDataType> customer_certificate,
const std::optional<IdToken> id_token,
const std::optional<CiString<64>> customer_identifier);

/// \brief Check if monitoring is available and if not, throw.
/// \param type Message type to include in MessageTypeNotImplementedException when thrown.
void throw_when_monitoring_not_available(const MessageType type);
};
} // namespace ocpp::v201
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
RemoteTransactionControl(MessageDispatcherInterface<MessageType>& message_dispatcher, DeviceModel& device_model,
ConnectivityManagerInterface& connectivity_manager, EvseManagerInterface& evse_manager,
ComponentStateManagerInterface& component_state_manager, TransactionInterface& transaction,
SmartChargingInterface& smart_charging, MeterValuesInterface& meter_values,
SmartChargingInterface* smart_charging, MeterValuesInterface& meter_values,
AvailabilityInterface& availability, FirmwareUpdateInterface& firmware_update,
SecurityInterface& security, ReservationInterface* reservation,
ProvisioningInterface& provisioning, UnlockConnectorCallback unlock_connector_callback,
Expand All @@ -66,7 +66,7 @@
ComponentStateManagerInterface& component_state_manager;

TransactionInterface& transaction;
SmartChargingInterface& smart_charging;
SmartChargingInterface* smart_charging;

Check notice on line 69 in include/ocpp/v201/functional_blocks/remote_transaction_control.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v201/functional_blocks/remote_transaction_control.hpp#L69

class member 'RemoteTransactionControl::smart_charging' is never used.
MeterValuesInterface& meter_values;
AvailabilityInterface& availability;
FirmwareUpdateInterface& firmware_update;
Expand Down
6 changes: 3 additions & 3 deletions include/ocpp/v201/functional_blocks/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
ConnectivityManagerInterface& connectivity_manager, EvseManagerInterface& evse_manager,
MessageQueue<v201::MessageType>& message_queue, DatabaseHandlerInterface& database_handler,
AuthorizationInterface& authorization, AvailabilityInterface& availability,
SmartChargingInterface& smart_charging, TariffAndCostInterface& tariff_and_cost,
SmartChargingInterface* smart_charging, TariffAndCostInterface* tariff_and_cost,
StopTransactionCallback stop_transaction_callback, PauseChargingCallback pause_charging_callback,
std::optional<TransactionEventCallback> transaction_event_callback,
std::optional<TransactionEventResponseCallback> transaction_event_response_callback,
Expand Down Expand Up @@ -129,8 +129,8 @@
DatabaseHandlerInterface& database_handler;
AuthorizationInterface& authorization;
AvailabilityInterface& availability;
SmartChargingInterface& smart_charging;
TariffAndCostInterface& tariff_and_cost;
SmartChargingInterface* smart_charging;

Check notice on line 132 in include/ocpp/v201/functional_blocks/transaction.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v201/functional_blocks/transaction.hpp#L132

class member 'TransactionBlock::smart_charging' is never used.
TariffAndCostInterface* tariff_and_cost;

Check notice on line 133 in include/ocpp/v201/functional_blocks/transaction.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v201/functional_blocks/transaction.hpp#L133

class member 'TransactionBlock::tariff_and_cost' is never used.
StopTransactionCallback stop_transaction_callback;
PauseChargingCallback pause_charging_callback;
std::optional<TransactionEventCallback> transaction_event_callback;
Expand Down
8 changes: 1 addition & 7 deletions include/ocpp/v201/init_device_model_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,13 @@ namespace ocpp::v201 {
/// \brief Class that holds a component.
///
/// When the component is read from the database, the component id will be set.
/// When the component is read from the component config file, the 'required' vector will be filled.
///
struct ComponentKey {
std::optional<uint64_t> db_id; ///< \brief Component id in the database.
std::string name; ///< \brief Component name.
std::optional<std::string> instance; ///< \brief Component instance.
std::optional<int32_t> evse_id; ///< \brief Component evse id.
std::optional<int32_t> connector_id; ///< \brief Component connector id.
std::vector<std::string> required; ///< \brief List of required variables.

///
/// \brief operator <, needed to add this class as key in a map.
Expand Down Expand Up @@ -85,8 +83,6 @@ struct DeviceModelVariable {
VariableCharacteristics characteristics;
/// \brief Variable attributes
std::vector<DbVariableAttribute> attributes;
/// \brief True if variable is required
bool required;
/// \brief Variable instance
std::optional<std::string> instance;
/// \brief Default value, if this is set in the component config json
Expand Down Expand Up @@ -221,11 +217,9 @@ class InitDeviceModelDb : public common::DatabaseHandlerCommon {
///
/// \brief Get all component properties (variables) from the given (component) json.
/// \param component_properties The json component properties
/// \param required_properties The vector of required properties of this component.
/// \return A vector with all Variables belonging to this component.
///
std::vector<DeviceModelVariable> get_all_component_properties(const json& component_properties,
std::vector<std::string> required_properties);
std::vector<DeviceModelVariable> get_all_component_properties(const json& component_properties);

///
/// \brief Insert variable characteristics
Expand Down
4 changes: 1 addition & 3 deletions include/ocpp/v201/ocpp_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2235,11 +2235,9 @@ void to_json(json& j, const Firmware& k);
/// \brief Conversion from a given json object \p j to a given Firmware \p k
void from_json(const json& j, Firmware& k);

// \brief Writes the string representation of the given Firmware \p k to the given output stream \p os
/// \brief Writes the string representation of the given Firmware \p k to the given output stream \p os
/// \returns an output stream with the Firmware written to
std::ostream& operator<<(std::ostream& os, const Firmware& k);

struct RequiredComponentVariable : ComponentVariable {};
} // namespace v201
} // namespace ocpp

Expand Down
65 changes: 48 additions & 17 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ void ChargePoint::start(BootReasonEnum bootreason, bool start_connecting) {
this->message_queue->get_persisted_messages_from_db();
this->provisioning->boot_notification_req(bootreason);
// call clear_invalid_charging_profiles when system boots
this->clear_invalid_charging_profiles();
if (this->smart_charging != nullptr) {
this->clear_invalid_charging_profiles();
}

if (start_connecting) {
this->connectivity_manager->connect();
}
Expand Down Expand Up @@ -533,13 +536,21 @@ void ChargePoint::initialize(const std::map<int32_t, int32_t>& evse_connector_st
this->callbacks.configure_network_connection_profile_callback.value());
}

this->smart_charging = std::make_unique<SmartCharging>(
*this->device_model, *this->evse_manager, *this->connectivity_manager, *this->message_dispatcher,
*this->database_handler, this->callbacks.set_charging_profiles_callback);
if (device_model->get_optional_value<bool>(ControllerComponentVariables::SmartChargingCtrlrAvailable)
.value_or(false)) {
this->smart_charging = std::make_unique<SmartCharging>(
*this->device_model, *this->evse_manager, *this->connectivity_manager, *this->message_dispatcher,
*this->database_handler, this->callbacks.set_charging_profiles_callback);
}

this->tariff_and_cost = std::make_unique<TariffAndCost>(
*this->message_dispatcher, *this->device_model, *this->evse_manager, *this->meter_values,
this->callbacks.set_display_message_callback, this->callbacks.set_running_cost_callback, this->io_service);
if (device_model->get_optional_value<bool>(ControllerComponentVariables::TariffCostCtrlrAvailableCost)
.value_or(false) ||
device_model->get_optional_value<bool>(ControllerComponentVariables::TariffCostCtrlrAvailableTariff)
.value_or(false)) {
this->tariff_and_cost = std::make_unique<TariffAndCost>(
*this->message_dispatcher, *this->device_model, *this->evse_manager, *this->meter_values,
this->callbacks.set_display_message_callback, this->callbacks.set_running_cost_callback, this->io_service);
}

this->firmware_update = std::make_unique<FirmwareUpdate>(
*this->message_dispatcher, *this->device_model, *this->evse_manager, *this->evse_security, *this->availability,
Expand All @@ -548,10 +559,10 @@ void ChargePoint::initialize(const std::map<int32_t, int32_t>& evse_connector_st

this->transaction = std::make_unique<TransactionBlock>(
*this->message_dispatcher, *this->device_model, *this->connectivity_manager, *this->evse_manager,
*this->message_queue, *this->database_handler, *this->authorization, *this->availability, *this->smart_charging,
*this->tariff_and_cost, this->callbacks.stop_transaction_callback, this->callbacks.pause_charging_callback,
this->callbacks.transaction_event_callback, this->callbacks.transaction_event_response_callback,
this->callbacks.reset_callback);
*this->message_queue, *this->database_handler, *this->authorization, *this->availability,
this->smart_charging.get(), this->tariff_and_cost.get(), this->callbacks.stop_transaction_callback,
this->callbacks.pause_charging_callback, this->callbacks.transaction_event_callback,
this->callbacks.transaction_event_response_callback, this->callbacks.reset_callback);

this->provisioning = std::make_unique<Provisioning>(
*this->device_model, *this->message_dispatcher, *this->message_queue, *this->connectivity_manager,
Expand All @@ -564,7 +575,7 @@ void ChargePoint::initialize(const std::map<int32_t, int32_t>& evse_connector_st

this->remote_transaction_control = std::make_unique<RemoteTransactionControl>(
*this->message_dispatcher, *this->device_model, *this->connectivity_manager, *this->evse_manager,
*this->component_state_manager, *this->transaction, *this->smart_charging, *this->meter_values,
*this->component_state_manager, *this->transaction, this->smart_charging.get(), *this->meter_values,
*this->availability, *this->firmware_update, *this->security, this->reservation.get(), *this->provisioning,
this->callbacks.unlock_connector_callback, this->callbacks.remote_start_transaction_callback,
this->callbacks.stop_transaction_callback, this->registration_status, this->upload_log_status,
Expand Down Expand Up @@ -644,7 +655,11 @@ void ChargePoint::handle_message(const EnhancedMessage<v201::MessageType>& messa
case MessageType::ClearChargingProfile:
case MessageType::GetChargingProfiles:
case MessageType::GetCompositeSchedule:
this->smart_charging->handle_message(message);
if (this->smart_charging != nullptr) {
this->smart_charging->handle_message(message);
} else {
send_not_implemented_error(message.uniqueId, message.messageTypeId);
}
break;
case MessageType::GetDisplayMessages:
case MessageType::SetDisplayMessage:
Expand All @@ -656,7 +671,12 @@ void ChargePoint::handle_message(const EnhancedMessage<v201::MessageType>& messa
}
break;
case MessageType::CostUpdated:
this->tariff_and_cost->handle_message(message);
if (this->tariff_and_cost != nullptr) {
this->tariff_and_cost->handle_message(message);
} else {
send_not_implemented_error(message.uniqueId, message.messageTypeId);
}

break;
default:
send_not_implemented_error(message.uniqueId, message.messageTypeId);
Expand Down Expand Up @@ -798,7 +818,6 @@ void ChargePoint::message_callback(const std::string& message) {
bool ChargePoint::is_offline() {
return !this->connectivity_manager->is_websocket_connected();
}

std::optional<DataTransferResponse> ChargePoint::data_transfer_req(const CiString<255>& vendorId,
const std::optional<CiString<50>>& messageId,
const std::optional<json>& data) {
Expand Down Expand Up @@ -917,8 +936,9 @@ void ChargePoint::clear_invalid_charging_profiles() {
for (const auto& [evse_id, profiles] : evses) {
for (auto profile : profiles) {
try {
if (this->smart_charging->conform_and_validate_profile(profile, evse_id) !=
ProfileValidationResultEnum::Valid) {
if (this->smart_charging != nullptr &&
this->smart_charging->conform_and_validate_profile(profile, evse_id) !=
ProfileValidationResultEnum::Valid) {
this->database_handler->delete_charging_profile(profile.id);
}
} catch (const QueryExecutionException& e) {
Expand All @@ -943,16 +963,27 @@ ChargePoint::set_variables(const std::vector<SetVariableData>& set_variable_data
}

GetCompositeScheduleResponse ChargePoint::get_composite_schedule(const GetCompositeScheduleRequest& request) {
if (this->smart_charging == nullptr) {
GetCompositeScheduleResponse response;
response.status = GenericStatusEnum::Rejected;
return response;
}
return this->smart_charging->get_composite_schedule(request);
}

std::optional<CompositeSchedule> ChargePoint::get_composite_schedule(int32_t evse_id, std::chrono::seconds duration,
ChargingRateUnitEnum unit) {
if (this->smart_charging == nullptr) {
return std::nullopt;
}
return this->smart_charging->get_composite_schedule(evse_id, duration, unit);
}

std::vector<CompositeSchedule> ChargePoint::get_all_composite_schedules(const int32_t duration_s,
const ChargingRateUnitEnum& unit) {
if (this->smart_charging == nullptr) {
return {};
}
return this->smart_charging->get_all_composite_schedules(duration_s, unit);
}

Expand Down
Loading