Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,105 @@
load("//modules:module.bzl", "cc_everest_module")
IMPLS = [
"auth_validator",
"auth_provider",
"data_transfer",
"ocpp_generic",
"session_cost",
]
# Copy libocpp configuration files to the expected runtime structure
genrule(
name = "ocpp201_config_files",
outs = [
# Component configuration files
"share/everest/modules/OCPP201/component_config/standardized/AlignedDataCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/AuthCacheCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/AuthCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/ChargingStation.json",
"share/everest/modules/OCPP201/component_config/standardized/ChargingStatusIndicator.json",
"share/everest/modules/OCPP201/component_config/standardized/ClockCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/CustomizationCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/DeviceDataCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/DisplayMessageCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/ISO15118Ctrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/LocalAuthListCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/MonitoringCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/OCPPCommCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/ReservationCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/SampledDataCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/SecurityCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/SmartChargingCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/TariffCostCtrlr.json",
"share/everest/modules/OCPP201/component_config/standardized/TxCtrlr.json",
"share/everest/modules/OCPP201/component_config/custom/.gitkeep",
# Core migration files (all available)
"share/everest/modules/OCPP201/core_migrations/1_up-initial.sql",
"share/everest/modules/OCPP201/core_migrations/2_down-auth_cache_management.sql",
"share/everest/modules/OCPP201/core_migrations/2_up-auth_cache_management.sql",
"share/everest/modules/OCPP201/core_migrations/3_down-persist-normal-messages.sql",
"share/everest/modules/OCPP201/core_migrations/3_up-persist-normal-messages.sql",
"share/everest/modules/OCPP201/core_migrations/4_down-transactions_db.sql",
"share/everest/modules/OCPP201/core_migrations/4_up-transactions_db.sql",
"share/everest/modules/OCPP201/core_migrations/5_down-charging_profiles_db.sql",
"share/everest/modules/OCPP201/core_migrations/5_up-charging_profiles_db.sql",
"share/everest/modules/OCPP201/core_migrations/6_down-charging_profiles_source_tx_id.sql",
"share/everest/modules/OCPP201/core_migrations/6_up-charging_profiles_source_tx_id.sql",
# Device model migration files (all available)
"share/everest/modules/OCPP201/device_model_migrations/1_up-initial.sql",
"share/everest/modules/OCPP201/device_model_migrations/2_down-variable_source.sql",
"share/everest/modules/OCPP201/device_model_migrations/2_up-variable_source.sql",
"share/everest/modules/OCPP201/device_model_migrations/3_down-variable_required.sql",
"share/everest/modules/OCPP201/device_model_migrations/3_up-variable_required.sql",
# Logging configuration
"share/everest/modules/OCPP201/logging.ini",
],
cmd = """
set -euo pipefail
LIBOCPP_CONFIG_PATH="$(location //lib/everest/ocpp:config)"
mkdir -p $(RULEDIR)/share/everest/modules/OCPP201/component_config/standardized
mkdir -p $(RULEDIR)/share/everest/modules/OCPP201/component_config/custom
mkdir -p $(RULEDIR)/share/everest/modules/OCPP201/core_migrations
mkdir -p $(RULEDIR)/share/everest/modules/OCPP201/device_model_migrations
cp $$LIBOCPP_CONFIG_PATH/common/component_config/standardized/*.json $(RULEDIR)/share/everest/modules/OCPP201/component_config/standardized/
cp $$LIBOCPP_CONFIG_PATH/v2/core_migrations/*.sql $(RULEDIR)/share/everest/modules/OCPP201/core_migrations/
cp $$LIBOCPP_CONFIG_PATH/common/device_model_migrations/*.sql $(RULEDIR)/share/everest/modules/OCPP201/device_model_migrations/
cp $$LIBOCPP_CONFIG_PATH/logging.ini $(RULEDIR)/share/everest/modules/OCPP201/logging.ini
touch $(RULEDIR)/share/everest/modules/OCPP201/component_config/custom/.gitkeep
""",
srcs = [
"//lib/everest/ocpp:config",
],
visibility = ["//visibility:public"],
)
# OCPP201 module
cc_everest_module(
name = "OCPP201",
srcs = [
"conversions.cpp",
"conversions.hpp",
"transaction_handler.cpp",
"transaction_handler.hpp",
"error_handling.hpp",
"device_model/everest_device_model_storage.cpp",
"device_model/everest_device_model_storage.hpp",
"device_model/composed_device_model_storage.cpp",
"device_model/composed_device_model_storage.hpp",
"device_model/definitions.cpp",
"device_model/definitions.hpp",
],
deps = [
"//third-party/bazel/openssl:ssl",
"//third-party/bazel/openssl:crypto",
"@libcap//:libcap",
"@everest-core//lib:util",
"@everest-core//lib:ocpp",
"@everest-core//lib:ocpp_evse_security",
"@everest-core//lib:ocpp_conversions",
"@everest-core//lib:external_energy_limits",
],
impls = IMPLS,
data = [":ocpp201_config_files"],
)

View File

@@ -0,0 +1,55 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
# insert your custom targets and additional config variables here
find_package(OpenSSL)
target_link_libraries(${MODULE_NAME}
PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
everest::util
everest::ocpp
everest::ocpp_evse_security
everest::ocpp_conversions
everest::external_energy_limits
)
target_compile_options(${MODULE_NAME}
PRIVATE
-Wimplicit-fallthrough
-Werror=switch-enum
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
"auth_validator/auth_token_validatorImpl.cpp"
"auth_provider/auth_token_providerImpl.cpp"
"data_transfer/ocpp_data_transferImpl.cpp"
"ocpp_generic/ocppImpl.cpp"
"session_cost/session_costImpl.cpp"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
target_sources(${MODULE_NAME}
PRIVATE
"conversions.cpp"
"transaction_handler.cpp"
"device_model/everest_device_model_storage.cpp"
"device_model/composed_device_model_storage.cpp"
"device_model/definitions.cpp"
)
if(EVEREST_CORE_BUILD_TESTING)
add_subdirectory(tests)
endif()
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP201_HPP
#define OCPP201_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for provided interface implementations
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
#include <generated/interfaces/auth_token_validator/Implementation.hpp>
#include <generated/interfaces/ocpp/Implementation.hpp>
#include <generated/interfaces/ocpp_data_transfer/Implementation.hpp>
#include <generated/interfaces/session_cost/Implementation.hpp>
// headers for required interface implementations
#include <generated/interfaces/auth/Interface.hpp>
#include <generated/interfaces/display_message/Interface.hpp>
#include <generated/interfaces/evse_manager/Interface.hpp>
#include <generated/interfaces/evse_security/Interface.hpp>
#include <generated/interfaces/external_energy_limits/Interface.hpp>
#include <generated/interfaces/iso15118_extensions/Interface.hpp>
#include <generated/interfaces/ocpp_data_transfer/Interface.hpp>
#include <generated/interfaces/reservation/Interface.hpp>
#include <generated/interfaces/system/Interface.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
#include <queue>
#include <tuple>
#include <variant>
#include <device_model/everest_device_model_storage.hpp>
#include <everest/util/async/monitor.hpp>
#include <generated/types/evse_board_support.hpp>
#include <ocpp/v2/charge_point.hpp>
#include <transaction_handler.hpp>
using EventQueue =
std::map<int32_t,
std::queue<std::variant<types::evse_manager::SessionEvent, Everest::error::Error, ocpp::v2::MeterValue,
types::system::FirmwareUpdateStatus, types::system::LogStatus>>>;
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {
std::string MessageLogPath;
std::string CoreDatabasePath;
std::string DeviceModelDatabasePath;
std::string EverestDeviceModelDatabasePath;
std::string DeviceModelDatabaseMigrationPath;
std::string DeviceModelConfigPath;
bool EnableExternalWebsocketControl;
int MessageQueueResumeDelay;
int CompositeScheduleIntervalS;
int RequestCompositeScheduleDurationS;
std::string RequestCompositeScheduleUnit;
int DelayOcppStart;
int ResetStopDelay;
};
class OCPP201 : public Everest::ModuleBase {
public:
OCPP201() = delete;
OCPP201(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider,
std::unique_ptr<auth_token_validatorImplBase> p_auth_validator,
std::unique_ptr<auth_token_providerImplBase> p_auth_provider,
std::unique_ptr<ocpp_data_transferImplBase> p_data_transfer, std::unique_ptr<ocppImplBase> p_ocpp_generic,
std::unique_ptr<session_costImplBase> p_session_cost,
std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager, std::unique_ptr<systemIntf> r_system,
std::unique_ptr<evse_securityIntf> r_security,
std::vector<std::unique_ptr<ocpp_data_transferIntf>> r_data_transfer, std::unique_ptr<authIntf> r_auth,
std::vector<std::unique_ptr<external_energy_limitsIntf>> r_evse_energy_sink,
std::vector<std::unique_ptr<display_messageIntf>> r_display_message,
std::vector<std::unique_ptr<reservationIntf>> r_reservation,
std::vector<std::unique_ptr<iso15118_extensionsIntf>> r_extensions_15118, Conf& config) :
ModuleBase(info),
mqtt(mqtt_provider),
p_auth_validator(std::move(p_auth_validator)),
p_auth_provider(std::move(p_auth_provider)),
p_data_transfer(std::move(p_data_transfer)),
p_ocpp_generic(std::move(p_ocpp_generic)),
p_session_cost(std::move(p_session_cost)),
r_evse_manager(std::move(r_evse_manager)),
r_system(std::move(r_system)),
r_security(std::move(r_security)),
r_data_transfer(std::move(r_data_transfer)),
r_auth(std::move(r_auth)),
r_evse_energy_sink(std::move(r_evse_energy_sink)),
r_display_message(std::move(r_display_message)),
r_reservation(std::move(r_reservation)),
r_extensions_15118(std::move(r_extensions_15118)),
config(config){};
Everest::MqttProvider& mqtt;
const std::unique_ptr<auth_token_validatorImplBase> p_auth_validator;
const std::unique_ptr<auth_token_providerImplBase> p_auth_provider;
const std::unique_ptr<ocpp_data_transferImplBase> p_data_transfer;
const std::unique_ptr<ocppImplBase> p_ocpp_generic;
const std::unique_ptr<session_costImplBase> p_session_cost;
const std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager;
const std::unique_ptr<systemIntf> r_system;
const std::unique_ptr<evse_securityIntf> r_security;
const std::vector<std::unique_ptr<ocpp_data_transferIntf>> r_data_transfer;
const std::unique_ptr<authIntf> r_auth;
const std::vector<std::unique_ptr<external_energy_limitsIntf>> r_evse_energy_sink;
const std::vector<std::unique_ptr<display_messageIntf>> r_display_message;
const std::vector<std::unique_ptr<reservationIntf>> r_reservation;
const std::vector<std::unique_ptr<iso15118_extensionsIntf>> r_extensions_15118;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
std::unique_ptr<ocpp::v2::ChargePoint> charge_point;
void charging_schedules_timer_callback();
void charging_schedules_timer_start();
void charging_schedules_timer_stop();
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
std::shared_ptr<device_model::EverestDeviceModelStorage> everest_device_model_storage;
std::unique_ptr<TransactionHandler> transaction_handler;
Everest::SteadyTimer charging_schedules_timer;
std::filesystem::path ocpp_share_path;
std::string source_ext_limit;
// key represents evse_id, value indicates if ready
everest::lib::util::monitor<std::map<int32_t, bool>> evse_ready_map;
everest::lib::util::monitor<std::map<int32_t, std::optional<float>>> evse_soc_map;
std::map<int32_t, types::evse_board_support::HardwareCapabilities> evse_hardware_capabilities_map;
std::map<int32_t, std::vector<types::iso15118::EnergyTransferMode>> evse_supported_energy_transfer_modes;
std::map<int32_t, bool> evse_service_renegotiation_supported;
everest::lib::util::monitor<std::map<int32_t, std::string>> evse_evcc_id;
std::atomic<ocpp::OcppProtocolVersion> ocpp_protocol_version{ocpp::OcppProtocolVersion::Unknown};
int32_t event_id_counter{0};
std::mutex session_event_mutex;
std::atomic_bool started{false};
EventQueue event_queue;
void init_evse_maps();
void init_evse_subscriptions();
void init_module_configuration();
std::map<int32_t, int32_t> get_connector_structure();
void process_session_event(const int32_t evse_id, const types::evse_manager::SessionEvent& session_event);
void process_tx_event_effect(const int32_t evse_id, const TxEventEffect tx_event_effect,
const types::evse_manager::SessionEvent& session_event);
void process_session_started(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_session_finished(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_transaction_started(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_transaction_finished(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_session_resumed(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_charging_started(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_charging_resumed(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_charging_paused_ev(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_charging_paused_evse(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_enabled(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_disabled(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_authorized(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_deauthorized(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event);
void process_reserved(const int32_t evse_id, const int32_t connector_id);
void process_reservation_end(const int32_t evse_id, const int32_t connector_id);
/// \brief This function publishes the given \p composite_schedules via the ocpp interface
void publish_charging_schedules(const std::vector<ocpp::v2::EnhancedCompositeSchedule>& composite_schedules);
/// \brief This function applies given \p composite_schedules for each connected evse_energy_sink
void set_external_limits(const std::vector<ocpp::v2::EnhancedCompositeSchedule>& composite_schedules);
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // OCPP201_HPP

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "auth_token_providerImpl.hpp"
namespace module {
namespace auth_provider {
void auth_token_providerImpl::init() {
}
void auth_token_providerImpl::ready() {
}
} // namespace auth_provider
} // namespace module

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef AUTH_PROVIDER_AUTH_TOKEN_PROVIDER_IMPL_HPP
#define AUTH_PROVIDER_AUTH_TOKEN_PROVIDER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
#include "../OCPP201.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace auth_provider {
struct Conf {};
class auth_token_providerImpl : public auth_token_providerImplBase {
public:
auth_token_providerImpl() = delete;
auth_token_providerImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<OCPP201>& mod, Conf& config) :
auth_token_providerImplBase(ev, "auth_provider"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// no commands defined for this interface
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<OCPP201>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace auth_provider
} // namespace module
#endif // AUTH_PROVIDER_AUTH_TOKEN_PROVIDER_IMPL_HPP

View File

@@ -0,0 +1,67 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <conversions.hpp>
#include <generated/interfaces/ISO15118_charger/Implementation.hpp>
#include <generated/types/session_cost.hpp>
#include <ocpp/v2/messages/Authorize.hpp>
#include "auth_token_validatorImpl.hpp"
namespace module {
namespace auth_validator {
void auth_token_validatorImpl::init() {
}
void auth_token_validatorImpl::ready() {
}
types::authorization::ValidationResult
auth_token_validatorImpl::handle_validate_token(types::authorization::ProvidedIdToken& provided_token) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle validate token command";
types::authorization::ValidationResult validation_result;
validation_result.authorization_status = types::authorization::AuthorizationStatus::Unknown;
return validation_result;
}
types::authorization::ValidationResult validation_result;
try {
const auto id_token = conversions::to_ocpp_id_token(provided_token.id_token);
std::optional<ocpp::CiString<10000>> certificate_opt;
if (provided_token.certificate.has_value()) {
certificate_opt.emplace(provided_token.certificate.value());
}
std::optional<std::vector<ocpp::v2::OCSPRequestData>> ocsp_request_data_opt;
if (provided_token.iso15118CertificateHashData.has_value()) {
ocsp_request_data_opt =
conversions::to_ocpp_ocsp_request_data_vector(provided_token.iso15118CertificateHashData.value());
}
// request response
const auto response = this->mod->charge_point->validate_token(id_token, certificate_opt, ocsp_request_data_opt);
validation_result = conversions::to_everest_validation_result(response);
// Publish tariff message on the session_cost interface
if (!validation_result.tariff_messages.empty()) {
types::session_cost::TariffMessage tariff_message;
tariff_message.messages = validation_result.tariff_messages;
tariff_message.identifier_id = provided_token.id_token.value;
tariff_message.identifier_type = types::display_message::IdentifierType::IdToken;
this->mod->p_session_cost->publish_tariff_message(tariff_message);
}
} catch (const ocpp::StringConversionException& e) {
EVLOG_warning << "Error converting id token to validate: " << e.what();
validation_result.authorization_status = types::authorization::AuthorizationStatus::Unknown;
} catch (const std::exception& e) {
EVLOG_warning << "Unknown error during validation of id token: " << e.what();
validation_result.authorization_status = types::authorization::AuthorizationStatus::Unknown;
}
return validation_result;
};
} // namespace auth_validator
} // namespace module

View File

@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef AUTH_VALIDATOR_AUTH_TOKEN_VALIDATOR_IMPL_HPP
#define AUTH_VALIDATOR_AUTH_TOKEN_VALIDATOR_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/auth_token_validator/Implementation.hpp>
#include "../OCPP201.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace auth_validator {
struct Conf {};
class auth_token_validatorImpl : public auth_token_validatorImplBase {
public:
auth_token_validatorImpl() = delete;
auth_token_validatorImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<OCPP201>& mod, Conf& config) :
auth_token_validatorImplBase(ev, "auth_validator"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual types::authorization::ValidationResult
handle_validate_token(types::authorization::ProvidedIdToken& provided_token) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<OCPP201>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace auth_validator
} // namespace module
#endif // AUTH_VALIDATOR_AUTH_TOKEN_VALIDATOR_IMPL_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,306 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_V2_CONVERSIONS_HPP
#define OCPP_V2_CONVERSIONS_HPP
#include "ocpp/v2/messages/ChangeAvailability.hpp"
#include "ocpp/v2/ocpp_enums.hpp"
#include "ocpp/v2/types.hpp"
#include <generated/types/display_message.hpp>
#include <generated/types/evse_manager.hpp>
#include <generated/types/iso15118.hpp>
#include <generated/types/ocpp.hpp>
#include <generated/types/reservation.hpp>
#include <generated/types/system.hpp>
#include <ocpp/v2/messages/Authorize.hpp>
#include <ocpp/v2/messages/BootNotification.hpp>
#include <ocpp/v2/messages/ClearDisplayMessage.hpp>
#include <ocpp/v2/messages/DataTransfer.hpp>
#include <ocpp/v2/messages/FirmwareStatusNotification.hpp>
#include <ocpp/v2/messages/Get15118EVCertificate.hpp>
#include <ocpp/v2/messages/GetDisplayMessages.hpp>
#include <ocpp/v2/messages/GetLog.hpp>
#include <ocpp/v2/messages/NotifyEVChargingNeeds.hpp>
#include <ocpp/v2/messages/SetDisplayMessage.hpp>
#include <ocpp/v2/messages/TransactionEvent.hpp>
#include <ocpp/v2/messages/UpdateFirmware.hpp>
namespace module {
namespace conversions {
/// \brief Converts a given types::system::FirmwareUpdateStatusEnum \p status to a ocpp::v2::FirmwareStatusEnum.
ocpp::v2::FirmwareStatusEnum to_ocpp_firmware_status_enum(const types::system::FirmwareUpdateStatusEnum status);
/// \brief Converts a given types::ocpp::DataTransferStatus \p status to a ocpp::v2::DataTransferStatusEnum.
ocpp::v2::DataTransferStatusEnum to_ocpp_data_transfer_status_enum(types::ocpp::DataTransferStatus status);
/// \brief Converts a given types::ocpp::DataTransferRequest \p status to a ocpp::v2::DataTransferRequest.
ocpp::v2::DataTransferRequest to_ocpp_data_transfer_request(types::ocpp::DataTransferRequest request);
/// \brief Converts a given types::ocpp::DataTransferResponse \p status to a ocpp::v2::DataTransferResponse.
ocpp::v2::DataTransferResponse to_ocpp_data_transfer_response(types::ocpp::DataTransferResponse response);
/// \brief Converts the provided parameters to an ocpp::v2::SampledValue.
ocpp::v2::SampledValue to_ocpp_sampled_value(const ocpp::v2::ReadingContextEnum& reading_context,
const ocpp::v2::MeasurandEnum& measurand, const std::string& unit,
const std::optional<ocpp::v2::PhaseEnum> phase,
ocpp::v2::LocationEnum location = ocpp::v2::LocationEnum::Outlet);
/// \brief Converts the given types::units_signed::SignedMeterValue \p signed_meter_value to an
/// ocpp::v2::SignedMeterValue.
ocpp::v2::SignedMeterValue to_ocpp_signed_meter_value(const types::units_signed::SignedMeterValue& signed_meter_value);
/// \brief Converts the provided parameters to an ocpp::v2::MeterValue.
ocpp::v2::MeterValue to_ocpp_meter_value(const types::powermeter::Powermeter& power_meter,
const ocpp::v2::ReadingContextEnum& reading_context,
const std::optional<types::units_signed::SignedMeterValue> signed_meter_value);
/// \brief Converts a given types::system::UploadLogsStatus \p log_status to an ocpp::v2::LogStatusEnum.
ocpp::v2::LogStatusEnum to_ocpp_log_status_enum(types::system::UploadLogsStatus log_status);
/// \brief Converts a given types::system::UploadLogsResponse \p response to an ocpp::v2::GetLogResponse.
ocpp::v2::GetLogResponse to_ocpp_get_log_response(const types::system::UploadLogsResponse& response);
/// \brief Converts a given types::system::UpdateFirmwareResponse \p response to an
/// ocpp::v2::UpdateFirmwareStatusEnum.
ocpp::v2::UpdateFirmwareStatusEnum
to_ocpp_update_firmware_status_enum(const types::system::UpdateFirmwareResponse& response);
/// \brief Converts a given types::system::UpdateFirmwareResponse \p response to an ocpp::v2::UpdateFirmwareResponse.
ocpp::v2::UpdateFirmwareResponse
to_ocpp_update_firmware_response(const types::system::UpdateFirmwareResponse& response);
/// \brief Converts a given types::system::LogStatusEnum \p status to an ocpp::v2::UploadLogStatusEnum.
ocpp::v2::UploadLogStatusEnum to_ocpp_upload_logs_status_enum(types::system::LogStatusEnum status);
/// \brief Converts a given types::system::BootReason \p reason to an ocpp::v2::BootReasonEnum.
ocpp::v2::BootReasonEnum to_ocpp_boot_reason(types::system::BootReason reason);
/// \brief Converts a given types::evse_manager::StopTransactionReason \p reason to an ocpp::v2::ReasonEnum.
ocpp::v2::ReasonEnum to_ocpp_reason(types::evse_manager::StopTransactionReason reason);
/// \brief Converts a given types::authorization::IdToken \p id_token to an ocpp::v2::IdToken.
ocpp::v2::IdToken to_ocpp_id_token(const types::authorization::IdToken& id_token);
/// \brief Converts a given types::iso15118::CertificateActionEnum \p action to an
/// ocpp::v2::CertificateActionEnum.
ocpp::v2::CertificateActionEnum to_ocpp_certificate_action_enum(const types::iso15118::CertificateActionEnum& action);
/// \brief Converts a vector of types::iso15118::CertificateHashDataInfo to a vector of
/// ocpp::v2::OCSPRequestData.
std::vector<ocpp::v2::OCSPRequestData> to_ocpp_ocsp_request_data_vector(
const std::vector<types::iso15118::CertificateHashDataInfo>& certificate_hash_data_info);
/// \brief Converts a given types::iso15118::HashAlgorithm \p hash_algorithm to an
/// ocpp::v2::HashAlgorithmEnum.
ocpp::v2::HashAlgorithmEnum to_ocpp_hash_algorithm_enum(const types::iso15118::HashAlgorithm hash_algorithm);
/// \brief Converts a given types::ocpp::GetVariableRequest \p get_variable_request_vector to an
/// std::vector<ocpp::v2::GetVariableData>
std::vector<ocpp::v2::GetVariableData>
to_ocpp_get_variable_data_vector(const std::vector<types::ocpp::GetVariableRequest>& get_variable_request_vector);
/// \brief Converts a given types::ocpp::SetVariableRequest \p set_variable_request_vector to an
/// std::vector<ocpp::v2::SetVariableData>
std::vector<ocpp::v2::SetVariableData>
to_ocpp_set_variable_data_vector(const std::vector<types::ocpp::SetVariableRequest>& set_variable_request_vector);
/// \brief Converts a given types::ocpp::Component \p component to a ocpp::v2::Component
ocpp::v2::Component to_ocpp_component(const types::ocpp::Component& component);
/// \brief Converts a given types::ocpp::Variable \p variable to a ocpp::v2::Variable
ocpp::v2::Variable to_ocpp_variable(const types::ocpp::Variable& variable);
/// \brief Converts a given types::ocpp::EVSE \p evse to a ocpp::v2::EVSE
ocpp::v2::EVSE to_ocpp_evse(const types::ocpp::EVSE& evse);
/// \brief Converts a given types::ocpp::AttributeEnum to ocpp::v2::AttributeEnum
ocpp::v2::AttributeEnum to_ocpp_attribute_enum(const types::ocpp::AttributeEnum attribute_enum);
/// \brief Converts a given types::types::iso15118::RequestExiStreamSchema to
/// ocpp::v2::Get15118EVCertificateRequest
ocpp::v2::Get15118EVCertificateRequest
to_ocpp_get_15118_certificate_request(const types::iso15118::RequestExiStreamSchema& request);
/// \brief Converts a given types::types::iso15118::ChargingNeeds to
/// ocpp::v2::ChargingNeeds
ocpp::v2::ChargingNeeds to_ocpp_charging_needs(const types::iso15118::ChargingNeeds& charging_needs);
/// \brief Converts a given types::reservation::ReservationResult to ocpp::v2::ReserveNowStatusEnum
ocpp::v2::ReserveNowStatusEnum to_ocpp_reservation_status(const types::reservation::ReservationResult result);
/// \brief Converts a given types::reservation::Reservation_status to ocpp::v2::ReservationUpdateStatusEnum
/// \warning This function can throw when there is no existing ocpp::v2::ReservationUpdateStatusEnum that is equal to
/// types::reservation::Reservation_status.
ocpp::v2::ReservationUpdateStatusEnum
to_ocpp_reservation_update_status_enum(const types::reservation::Reservation_status status);
/// \brief Converts a given ocpp::v2::ReasonEnum \p stop_reason to a types::evse_manager::StopTransactionReason.
types::evse_manager::StopTransactionReason to_everest_stop_transaction_reason(const ocpp::v2::ReasonEnum& stop_reason);
/// \brief Converts a given ocpp::v2::GetLogRequest \p request to a types::system::UploadLogsRequest.
types::system::UploadLogsRequest to_everest_upload_logs_request(const ocpp::v2::GetLogRequest& request);
/// \brief Converts a given ocpp::v2::UpdateFirmwareRequest \p request to a types::system::FirmwareUpdateRequest.
types::system::FirmwareUpdateRequest to_everest_firmware_update_request(const ocpp::v2::UpdateFirmwareRequest& request);
/// \brief Converts a given ocpp::v2::Iso15118EVCertificateStatusEnum \p status to a types::iso15118::Status.
types::iso15118::Status to_everest_iso15118_status(const ocpp::v2::Iso15118EVCertificateStatusEnum& status);
/// \brief Converts a given ocpp::v2::DataTransferStatusEnum \p status to a types::ocpp::DataTransferStatus.
types::ocpp::DataTransferStatus to_everest_data_transfer_status(ocpp::v2::DataTransferStatusEnum status);
/// \brief Converts a given ocpp::v2::DataTransferRequest \p status to a types::ocpp::DataTransferRequest.
types::ocpp::DataTransferRequest to_everest_data_transfer_request(ocpp::v2::DataTransferRequest request);
/// \brief Converts a given ocpp::v2::DataTransferResponse \p status to a types::ocpp::DataTransferResponse.
types::ocpp::DataTransferResponse to_everest_data_transfer_response(ocpp::v2::DataTransferResponse response);
/// \brief Converts a given ocpp::v2::IdTokenInfo \p idTokenInfo to a types::authorization::ValidationResult.
types::authorization::ValidationResult to_everest_validation_result(const ocpp::v2::IdTokenInfo& idTokenInfo);
/// \brief Converts a given ocpp::v2::AuthorizeResponse \p response to a types::authorization::ValidationResult.
types::authorization::ValidationResult to_everest_validation_result(const ocpp::v2::AuthorizeResponse& response);
/// \brief Converts a given ocpp::v2::AuthorizationStatusEnum \p status to a
/// types::authorization::AuthorizationStatus.
types::authorization::AuthorizationStatus
to_everest_authorization_status(const ocpp::v2::AuthorizationStatusEnum status);
/// \brief Converts a given ocpp::v2::IdToken \p id_token to a types::authorization::IdToken.
types::authorization::IdToken to_everest_id_token(const ocpp::v2::IdToken& id_token);
/// \brief Converts a given ocpp::v2::AuthorizeCertificateStatusEnum \p status to a
/// types::authorization::CertificateStatus.
types::authorization::CertificateStatus
to_everest_certificate_status(const ocpp::v2::AuthorizeCertificateStatusEnum status);
/// \brief Converts a given ocpp::v2::TransactionEventRequest \p transaction_event to a
/// types::ocpp::OcppTransactionEvent.
types::ocpp::OcppTransactionEvent
to_everest_ocpp_transaction_event(const ocpp::v2::TransactionEventRequest& transaction_event);
/// \brief Converts a given ocpp::v2::MessageFormat \p message_format to a
/// types::ocpp::MessageFormat
types::text_message::MessageFormat to_everest_message_format(const ocpp::v2::MessageFormatEnum& message_format);
/// \brief Converts a given ocpp::v2::MessageContent \p message_content to a
/// types::ocpp::MessageContent
types::text_message::MessageContent to_everest_message_content(const ocpp::v2::MessageContent& message_content);
/// \brief Converts a given ocpp::v2::TransactionEventResponse \p transaction_event_response to a
/// types::ocpp::OcppTransactionEventResponse
types::ocpp::OcppTransactionEventResponse
to_everest_transaction_event_response(const ocpp::v2::TransactionEventResponse& transaction_event_response);
/// \brief Converts a given ocpp::v2::BootNotificationResponse \p boot_notification_response to a
/// types::ocpp::BootNotificationResponse
types::ocpp::BootNotificationResponse
to_everest_boot_notification_response(const ocpp::v2::BootNotificationResponse& boot_notification_response);
/// \brief Converts a given ocpp::v2::RegistrationStatusEnum \p registration_status to a
/// types::ocpp::RegistrationStatus
types::ocpp::RegistrationStatus
to_everest_registration_status(const ocpp::v2::RegistrationStatusEnum& registration_status);
/// \brief Converts a given ocpp::v2::StatusInfo \p status_info to a
/// types::ocpp::StatusInfoType
types::ocpp::StatusInfoType to_everest_status_info_type(const ocpp::v2::StatusInfo& status_info);
/// \brief Converts a given ocpp::v2::GetVariableResult \p get_variable_result_vector to a
/// std::vector<types::ocpp::GetVariableResult>
std::vector<types::ocpp::GetVariableResult>
to_everest_get_variable_result_vector(const std::vector<ocpp::v2::GetVariableResult>& get_variable_result_vector);
/// \brief Converts a given ocpp::v2::SetVariableResult \p set_variable_result_vector to a
/// std::vector<types::ocpp::SetVariableResult>
std::vector<types::ocpp::SetVariableResult>
to_everest_set_variable_result_vector(const std::vector<ocpp::v2::SetVariableResult>& set_variable_result_vector);
/// \brief Converts a given ocpp::v2::Component \p component to a types::ocpp::Component.
types::ocpp::Component to_everest_component(const ocpp::v2::Component& component);
/// \brief Converts a given ocpp::v2::Variable \p variable to a types::ocpp::Variable.
types::ocpp::Variable to_everest_variable(const ocpp::v2::Variable& variable);
/// \brief Converts a given ocpp::v2::EVSE \p evse to a types::ocpp::EVSE.
types::ocpp::EVSE to_everest_evse(const ocpp::v2::EVSE& evse);
/// \brief Converts a given ocpp::v2::AttributeEnum \p attribute_enum to a types::ocpp::AttributeEnum.
types::ocpp::AttributeEnum to_everest_attribute_enum(const ocpp::v2::AttributeEnum attribute_enum);
/// \brief Converts a given ocpp::v2::GetVariableStatusEnum \p get_variable_status to a
/// types::ocpp::GetVariableStatusEnumType
types::ocpp::GetVariableStatusEnumType
to_everest_get_variable_status_enum_type(const ocpp::v2::GetVariableStatusEnum get_variable_status);
/// \brief Converts a given ocpp::v2::SetVariableStatusEnum \p set_variable_status to a
/// types::ocpp::SetVariableStatusEnumType
types::ocpp::SetVariableStatusEnumType
to_everest_set_variable_status_enum_type(const ocpp::v2::SetVariableStatusEnum set_variable_status);
/// \brief Converts a given vector of ocpp::v2::EnhancedCompositeSchedule \p composite_schedules to a
/// types::ocpp::ChargingSchedules
types::ocpp::ChargingSchedules
to_everest_charging_schedules(const std::vector<ocpp::v2::EnhancedCompositeSchedule>& composite_schedules);
/// \brief Converts a given ocpp::v2::EnhancedCompositeSchedule \p composite_schedule to a types::ocpp::ChargingSchedule
types::ocpp::ChargingSchedule
to_everest_charging_schedule(const ocpp::v2::EnhancedCompositeSchedule& composite_schedule);
/// \brief Converts a given ocpp::v2::OperationModeEnum to a types::ocpp::Operation_mode enum.
types::ocpp::Operation_mode to_everest_operation_mode(const ocpp::v2::OperationModeEnum operation_mode);
/// \brief Convert a given ocpp::v2::ChargingSchedulePeriod \p period to a types::ocpp::ChargingSchedulePeriod
types::ocpp::ChargingSchedulePeriod to_everest_charging_schedule_period(const ocpp::v2::ChargingSchedulePeriod& period);
/// \brief Convert a given ocpp::v2::EnhancedChargingSchedulePeriod \p period to a types::ocpp::ChargingSchedulePeriod
types::ocpp::ChargingSchedulePeriod
to_everest_charging_schedule_period(const ocpp::v2::EnhancedChargingSchedulePeriod& period);
ocpp::v2::DisplayMessageStatusEnum
to_ocpp_display_message_status_enum(const types::display_message::DisplayMessageStatusEnum& from);
ocpp::v2::SetDisplayMessageResponse
to_ocpp_set_display_message_response(const types::display_message::SetDisplayMessageResponse& response);
types::display_message::MessagePriorityEnum
to_everest_display_message_priority_enum(const ocpp::v2::MessagePriorityEnum& priority);
types::display_message::MessageStateEnum
to_everest_display_message_state_enum(const ocpp::v2::MessageStateEnum& message_state);
types::display_message::GetDisplayMessageRequest
to_everest_display_message_request(const ocpp::v2::GetDisplayMessagesRequest& request);
types::display_message::ClearDisplayMessageRequest
to_everest_clear_display_message_request(const ocpp::v2::ClearDisplayMessageRequest& request);
ocpp::v2::ClearMessageStatusEnum
to_ocpp_clear_message_response_enum(const types::display_message::ClearMessageResponseEnum& response_enum);
ocpp::v2::ClearDisplayMessageResponse
to_ocpp_clear_display_message_response(const types::display_message::ClearDisplayMessageResponse& response);
/// \brief Converst a given ocpp::v2::EnergyTransferModeEnum \p to a types::iso15118::EnergyTransferMode
types::iso15118::EnergyTransferMode
to_everest_allowed_energy_transfer_mode(const ocpp::v2::EnergyTransferModeEnum& allowed_energy_transfer_mode);
/// \brief Converst a given std::vector<ocpp::v2::EnergyTransferModeEnum> \p to a
/// std::vector<types::iso15118::EnergyTransferMode>
std::vector<types::iso15118::EnergyTransferMode> to_everest_allowed_energy_transfer_modes(
const std::vector<ocpp::v2::EnergyTransferModeEnum>& allowed_energy_transfer_modes);
ocpp::v2::OperationalStatusEnum to_ocpp_operational_status(types::ocpp::OperationalStatusEnumType value);
types::ocpp::ChangeAvailabilityStatusEnumType
to_everest_change_availability_status(ocpp::v2::ChangeAvailabilityStatusEnum value);
types::ocpp::StatusInfoType to_everest_status_info(const ocpp::v2::StatusInfo& value);
ocpp::v2::ChangeAvailabilityRequest
to_ocpp_change_availability_request(const types::ocpp::ChangeAvailabilityRequest& request);
types::ocpp::ChangeAvailabilityResponse
to_everest_change_availability_response(const ocpp::v2::ChangeAvailabilityResponse& response);
} // namespace conversions
} // namespace module
#endif // OCPP_V2_CONVERSIONS_HPP

View File

@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "ocpp_data_transferImpl.hpp"
#include <conversions.hpp>
namespace module {
namespace data_transfer {
void ocpp_data_transferImpl::init() {
}
void ocpp_data_transferImpl::ready() {
}
types::ocpp::DataTransferResponse
ocpp_data_transferImpl::handle_data_transfer(types::ocpp::DataTransferRequest& request) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot data transfer command";
types::ocpp::DataTransferResponse response;
response.status = types::ocpp::DataTransferStatus::Offline;
return response;
}
ocpp::v2::DataTransferRequest ocpp_request = conversions::to_ocpp_data_transfer_request(request);
auto ocpp_response = mod->charge_point->data_transfer_req(ocpp_request);
types::ocpp::DataTransferResponse response;
if (ocpp_response.has_value()) {
response = conversions::to_everest_data_transfer_response(ocpp_response.value());
} else {
response.status = types::ocpp::DataTransferStatus::Offline;
}
return response;
}
} // namespace data_transfer
} // namespace module

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP
#define DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ocpp_data_transfer/Implementation.hpp>
#include "../OCPP201.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace data_transfer {
struct Conf {};
class ocpp_data_transferImpl : public ocpp_data_transferImplBase {
public:
ocpp_data_transferImpl() = delete;
ocpp_data_transferImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<OCPP201>& mod, Conf& config) :
ocpp_data_transferImplBase(ev, "data_transfer"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual types::ocpp::DataTransferResponse handle_data_transfer(types::ocpp::DataTransferRequest& request) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<OCPP201>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace data_transfer
} // namespace module
#endif // DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP

View File

@@ -0,0 +1,162 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <device_model/composed_device_model_storage.hpp>
static constexpr auto VARIABLE_SOURCE_OCPP = "OCPP";
namespace module::device_model {
bool ComposedDeviceModelStorage::register_device_model_storage(
std::string device_model_storage_id, std::shared_ptr<ocpp::v2::DeviceModelStorageInterface> device_model_storage) {
if (this->device_model_storages.find(device_model_storage_id) != this->device_model_storages.end()) {
return false;
}
const auto device_model_map = device_model_storage->get_device_model();
// store the sources of each variable to be able to lookup requests to the device model storage
for (const auto& [component, variable_map] : device_model_map) {
for (const auto& [variable, variable_meta] : variable_map) {
// check if component variable source is already exist in the map
if (this->component_variable_source_map.find(component) != this->component_variable_source_map.end() &&
this->component_variable_source_map.at(component).find(variable) !=
this->component_variable_source_map.at(component).end()) {
EVLOG_warning << "Component variable source already exists for component: " << component.name
<< ", variable: " << variable.name << ". Fix your device model configuration.";
}
// Note: Source should not be optional, should be changed in libocpp
this->component_variable_source_map[component][variable] =
variable_meta.source.value_or(VARIABLE_SOURCE_OCPP);
}
}
this->device_model_storages[device_model_storage_id] = device_model_storage;
return true;
}
ocpp::v2::DeviceModelMap ComposedDeviceModelStorage::get_device_model() {
ocpp::v2::DeviceModelMap device_model_map;
for (const auto& [name, device_model_storage] : this->device_model_storages) {
device_model_map.merge(device_model_storage->get_device_model());
}
return device_model_map;
}
std::optional<ocpp::v2::VariableAttribute>
ComposedDeviceModelStorage::get_variable_attribute(const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum) {
const auto variable_source = get_variable_source(component_id, variable_id);
if (this->device_model_storages.find(variable_source) == this->device_model_storages.end()) {
return std::nullopt;
}
return this->device_model_storages.at(variable_source)
->get_variable_attribute(component_id, variable_id, attribute_enum);
}
std::vector<ocpp::v2::VariableAttribute>
ComposedDeviceModelStorage::get_variable_attributes(const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id,
const std::optional<ocpp::v2::AttributeEnum>& attribute_enum) {
const auto variable_source = get_variable_source(component_id, variable_id);
if (this->device_model_storages.find(variable_source) == this->device_model_storages.end()) {
return {};
}
return this->device_model_storages.at(variable_source)
->get_variable_attributes(component_id, variable_id, attribute_enum);
}
ocpp::v2::SetVariableStatusEnum ComposedDeviceModelStorage::set_variable_attribute_value(
const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum, const std::string& value, const std::string& source) {
// the "source" parameter is the VALUE_SOURCE
const auto variable_source = get_variable_source(component_id, variable_id);
if (this->device_model_storages.find(variable_source) == this->device_model_storages.end()) {
return ocpp::v2::SetVariableStatusEnum::Rejected;
}
return this->device_model_storages.at(variable_source)
->set_variable_attribute_value(component_id, variable_id, attribute_enum, value, source);
}
std::optional<ocpp::v2::VariableMonitoringMeta>
ComposedDeviceModelStorage::set_monitoring_data(const ocpp::v2::SetMonitoringData& data,
const ocpp::v2::VariableMonitorType type) {
if (this->device_model_storages.find(VARIABLE_SOURCE_OCPP) == this->device_model_storages.end()) {
EVLOG_error << "OCPP device model storage not registered, cannot set monitoring data";
return std::nullopt;
}
return this->device_model_storages.at(VARIABLE_SOURCE_OCPP)->set_monitoring_data(data, type);
}
bool ComposedDeviceModelStorage::update_monitoring_reference(const int32_t monitor_id,
const std::string& reference_value) {
if (this->device_model_storages.find(VARIABLE_SOURCE_OCPP) == this->device_model_storages.end()) {
EVLOG_error << "OCPP device model storage not registered, cannot update monitoring reference";
return false;
}
return this->device_model_storages.at(VARIABLE_SOURCE_OCPP)
->update_monitoring_reference(monitor_id, reference_value);
}
std::vector<ocpp::v2::VariableMonitoringMeta>
ComposedDeviceModelStorage::get_monitoring_data(const std::vector<ocpp::v2::MonitoringCriterionEnum>& criteria,
const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id) {
const auto variable_source = get_variable_source(component_id, variable_id);
if (this->device_model_storages.find(variable_source) == this->device_model_storages.end()) {
return {};
}
return this->device_model_storages.at(variable_source)->get_monitoring_data(criteria, component_id, variable_id);
}
ocpp::v2::ClearMonitoringStatusEnum ComposedDeviceModelStorage::clear_variable_monitor(int monitor_id,
bool allow_protected) {
if (this->device_model_storages.find(VARIABLE_SOURCE_OCPP) == this->device_model_storages.end()) {
EVLOG_error << "OCPP device model storage not registered, cannot clear variable monitor";
return ocpp::v2::ClearMonitoringStatusEnum::Rejected;
}
return this->device_model_storages.at(VARIABLE_SOURCE_OCPP)->clear_variable_monitor(monitor_id, allow_protected);
}
int32_t ComposedDeviceModelStorage::clear_custom_variable_monitors() {
if (this->device_model_storages.find(VARIABLE_SOURCE_OCPP) == this->device_model_storages.end()) {
EVLOG_error << "OCPP device model storage not registered, cannot clear custom variable monitors";
return 0;
}
return this->device_model_storages.at(VARIABLE_SOURCE_OCPP)->clear_custom_variable_monitors();
}
void ComposedDeviceModelStorage::check_integrity() {
for (const auto& [name, device_model_storage] : this->device_model_storages) {
device_model_storage->check_integrity();
}
}
bool ComposedDeviceModelStorage::create_network_configuration_slot_from_default_schema(std::int32_t new_slot) {
// NetworkConfiguration_<N> components live in the OCPP-source storage (the SQLite-backed
// EverestDeviceModelStorage). Without this dispatch, libocpp's blob-migration fallback hits
// DeviceModelStorageInterface's default virtual (returns false) and the operator's
// NetworkConnectionProfiles blob ends up cleared without ever populating the per-slot
// device-model rows on targets that ship no NetworkConfiguration_<N>.json.
const auto it = this->device_model_storages.find(VARIABLE_SOURCE_OCPP);
if (it == this->device_model_storages.end()) {
EVLOG_error << "OCPP device model storage not registered, cannot create NetworkConfiguration_" << new_slot;
return false;
}
return it->second->create_network_configuration_slot_from_default_schema(new_slot);
}
std::string module::device_model::ComposedDeviceModelStorage::get_variable_source(const ocpp::v2::Component& component,
const ocpp::v2::Variable& variable) {
if (this->component_variable_source_map.find(component) == this->component_variable_source_map.end()) {
return VARIABLE_SOURCE_OCPP; // default source
}
const auto& variable_map = this->component_variable_source_map.at(component);
if (variable_map.find(variable) == variable_map.end()) {
return VARIABLE_SOURCE_OCPP; // default source
}
return variable_map.at(variable);
}
} // namespace module::device_model

View File

@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#pragma once
#include <device_model/everest_device_model_storage.hpp>
#include <ocpp/v2/device_model_storage_interface.hpp>
#include <ocpp/v2/device_model_storage_sqlite.hpp>
namespace module::device_model {
using ComponentVariableSourceMap = std::map<ocpp::v2::Component, std::map<ocpp::v2::Variable, std::string>>;
class ComposedDeviceModelStorage : public ocpp::v2::DeviceModelStorageInterface {
private:
std::map<std::string, std::shared_ptr<ocpp::v2::DeviceModelStorageInterface>>
device_model_storages; // key is identifier for the device model storage
ComponentVariableSourceMap component_variable_source_map;
public:
ComposedDeviceModelStorage() = default;
/// \brief Register a device model storage.
/// \param device_model_storage_id The id of the device model storage. Component variable combinations can be
/// used to map to this id to identify which device model is adressed for certain requests.
/// \param device_model_storage The device model storage to register.
/// \return True if the device model storage name is not yet registered, false otherwise.
bool register_device_model_storage(std::string device_model_storage_id,
std::shared_ptr<ocpp::v2::DeviceModelStorageInterface> device_model_storage);
virtual ~ComposedDeviceModelStorage() override = default;
virtual ocpp::v2::DeviceModelMap get_device_model() override;
virtual std::optional<ocpp::v2::VariableAttribute>
get_variable_attribute(const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum) override;
virtual std::vector<ocpp::v2::VariableAttribute>
get_variable_attributes(const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id,
const std::optional<ocpp::v2::AttributeEnum>& attribute_enum) override;
virtual ocpp::v2::SetVariableStatusEnum set_variable_attribute_value(const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum,
const std::string& value,
const std::string& source) override;
virtual std::optional<ocpp::v2::VariableMonitoringMeta>
set_monitoring_data(const ocpp::v2::SetMonitoringData& data, const ocpp::v2::VariableMonitorType type) override;
virtual bool update_monitoring_reference(const int32_t monitor_id, const std::string& reference_value) override;
virtual std::vector<ocpp::v2::VariableMonitoringMeta>
get_monitoring_data(const std::vector<ocpp::v2::MonitoringCriterionEnum>& criteria,
const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id) override;
virtual ocpp::v2::ClearMonitoringStatusEnum clear_variable_monitor(int monitor_id, bool allow_protected) override;
virtual int32_t clear_custom_variable_monitors() override;
virtual void check_integrity() override;
virtual bool create_network_configuration_slot_from_default_schema(std::int32_t new_slot) override;
private:
///
/// \brief Get variable source of given variable.
/// \param component Component the variable belongs to.
/// \param variable The variable to get the source from.
/// \return The variable source. Defaults to 'OCPP'.
///
std::string get_variable_source(const ocpp::v2::Component& component, const ocpp::v2::Variable& variable);
};
} // namespace module::device_model

View File

@@ -0,0 +1,224 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <device_model/definitions.hpp>
#include <optional>
#include <ocpp/v2/ocpp_types.hpp>
using ocpp::CiString;
using ocpp::v2::DataEnum;
namespace EvseDefinitions {
EVSE get_evse(const int32_t evse_id, const std::optional<int32_t>& connector_id) {
EVSE evse;
evse.id = evse_id;
evse.connectorId = connector_id;
return evse;
}
namespace Characteristics {
const VariableCharacteristics AllowReset = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics AvailabilityState = [] {
VariableCharacteristics var;
var.dataType = DataEnum::OptionList;
var.supportsMonitoring = false;
var.valuesList = CiString<1000>("Available,Occupied,Reserved,Unavailable,Faulted");
return var;
}();
const VariableCharacteristics Available = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics EvseId = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
VariableCharacteristics EVSEPower = [] {
VariableCharacteristics var;
var.dataType = DataEnum::decimal;
var.supportsMonitoring = false;
var.unit = CiString<16>("W");
var.maxLimit = 0.0f; // will be updated at runtime
return var;
}();
const VariableCharacteristics SupplyPhases = [] {
VariableCharacteristics var;
var.dataType = DataEnum::integer;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics ISO15118EvseId = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
var.minLimit = 7.0f;
var.maxLimit = 37.0f;
return var;
}();
} // namespace Characteristics
} // namespace EvseDefinitions
namespace ConnectorDefinitions {
namespace Characteristics {
const VariableCharacteristics AvailabilityState = [] {
VariableCharacteristics var;
var.dataType = DataEnum::OptionList;
var.supportsMonitoring = false;
var.valuesList = CiString<1000>("Available,Occupied,Reserved,Unavailable,Faulted");
return var;
}();
const VariableCharacteristics Available = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics ConnectorType = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics SupplyPhases = [] {
VariableCharacteristics var;
var.dataType = DataEnum::integer;
var.supportsMonitoring = false;
return var;
}();
} // namespace Characteristics
} // namespace ConnectorDefinitions
namespace V2XDefinitions {
namespace Characteristics {
const VariableCharacteristics Available = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics Enabled = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics SupportedEnergyTransferModes = [] {
VariableCharacteristics var;
var.dataType = DataEnum::MemberList;
var.supportsMonitoring = false;
var.valuesList =
"AC_single_phase,AC_two_phase,AC_three_phase,DC,AC_BPT,AC_BPT_DER,AC_DER,DC_BPT,DC_ACDP,DC_ACDP_BPT,WPT";
return var;
}();
const VariableCharacteristics SupportedOperationModes = [] {
VariableCharacteristics var;
var.dataType = DataEnum::MemberList;
var.supportsMonitoring = false;
var.valuesList = "Idle,ChargingOnly,CentralSetpoint,ExternalSetpoint,ExternalLimits,CentralFrequency,"
"LocalFrequency,LocalLoadBalancing";
return var;
}();
} // namespace Characteristics
} // namespace V2XDefinitions
namespace ISO15118Definitions {
namespace Characteristics {
const VariableCharacteristics Enabled = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics ServiceRenegotiationSupport = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics ProtocolSupported = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
} // namespace Characteristics
} // namespace ISO15118Definitions
namespace ConnectedEVDefinitions {
namespace Characteristics {
const VariableCharacteristics Available = [] {
VariableCharacteristics var;
var.dataType = DataEnum::boolean;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics VehicleId = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics ProtocolAgreed = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics VehicleCertificateLeaf = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics VehicleCertificateSubCa1 = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics VehicleCertificateSubCa2 = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics VehicleCertificateRoot = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
const VariableCharacteristics ProtocolSupportedByEV = [] {
VariableCharacteristics var;
var.dataType = DataEnum::string;
var.supportsMonitoring = false;
return var;
}();
} // namespace Characteristics
} // namespace ConnectedEVDefinitions

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <ocpp/v2/ctrlr_component_variables.hpp>
using ocpp::v2::EVSE;
using ocpp::v2::VariableCharacteristics;
namespace EvseDefinitions {
EVSE get_evse(const int32_t evse_id, const std::optional<int32_t>& connector_id = std::nullopt);
namespace Characteristics {
extern const VariableCharacteristics AllowReset;
extern const VariableCharacteristics AvailabilityState;
extern const VariableCharacteristics Available;
extern const VariableCharacteristics EvseId;
extern VariableCharacteristics EVSEPower;
extern const VariableCharacteristics SupplyPhases;
extern const VariableCharacteristics ISO15118EvseId;
} // namespace Characteristics
} // namespace EvseDefinitions
namespace ConnectorDefinitions {
namespace Characteristics {
extern const VariableCharacteristics AvailabilityState;
extern const VariableCharacteristics Available;
extern const VariableCharacteristics ConnectorType;
extern const VariableCharacteristics SupplyPhases;
} // namespace Characteristics
} // namespace ConnectorDefinitions
namespace V2XDefinitions {
namespace Characteristics {
extern const VariableCharacteristics Available;
extern const VariableCharacteristics Enabled;
extern const VariableCharacteristics SupportedEnergyTransferModes;
extern const VariableCharacteristics SupportedOperationModes;
} // namespace Characteristics
} // namespace V2XDefinitions
namespace ISO15118Definitions {
namespace Characteristics {
extern const VariableCharacteristics Enabled;
extern const VariableCharacteristics ServiceRenegotiationSupport;
extern const VariableCharacteristics ProtocolSupported;
} // namespace Characteristics
} // namespace ISO15118Definitions
namespace ConnectedEVDefinitions {
namespace Characteristics {
extern const VariableCharacteristics Available;
extern const VariableCharacteristics VehicleId;
extern const VariableCharacteristics ProtocolAgreed;
extern const VariableCharacteristics VehicleCertificateLeaf;
extern const VariableCharacteristics VehicleCertificateSubCa1;
extern const VariableCharacteristics VehicleCertificateSubCa2;
extern const VariableCharacteristics VehicleCertificateRoot;
extern const VariableCharacteristics ProtocolSupportedByEV;
} // namespace Characteristics
} // namespace ConnectedEVDefinitions

View File

@@ -0,0 +1,781 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <algorithm>
#include <everest/logging.hpp>
#include <device_model/definitions.hpp>
#include <device_model/everest_device_model_storage.hpp>
#include <ocpp/v2/init_device_model_db.hpp>
#include <ocpp/v2/ocpp_types.hpp>
using ocpp::v2::Component;
using ocpp::v2::DataEnum;
using ocpp::v2::EVSE;
using ocpp::v2::Variable;
using ocpp::v2::VariableAttribute;
using ocpp::v2::VariableCharacteristics;
using ocpp::v2::VariableMap;
using ocpp::v2::VariableMetaData;
static constexpr auto VARIABLE_SOURCE_EVEREST = "EVEREST";
static constexpr auto NUMBER_OF_CONNECTED_EV_PROTOCOLS = 20;
using ocpp::v2::ComponentKey;
using ocpp::v2::DbVariableAttribute;
using ocpp::v2::DeviceModelVariable;
namespace module::device_model {
ocpp::v2::DataEnum to_ocpp_data_enum(const everest::config::Datatype& data_type) {
switch (data_type) {
case everest::config::Datatype::Unknown:
throw std::out_of_range("Could not convert Datatype::Unknown to DataEnum");
case everest::config::Datatype::String:
return ocpp::v2::DataEnum::string;
case everest::config::Datatype::Decimal:
return ocpp::v2::DataEnum::decimal;
case everest::config::Datatype::Integer:
return ocpp::v2::DataEnum::integer;
case everest::config::Datatype::Boolean:
return ocpp::v2::DataEnum::boolean;
}
throw std::out_of_range("Could not convert Datatype to DataEnum");
}
ocpp::v2::MutabilityEnum to_ocpp_mutability_enum(const everest::config::Mutability& mutability) {
switch (mutability) {
case everest::config::Mutability::ReadOnly:
return ocpp::v2::MutabilityEnum::ReadOnly;
case everest::config::Mutability::ReadWrite:
return ocpp::v2::MutabilityEnum::ReadWrite;
case everest::config::Mutability::WriteOnly:
return ocpp::v2::MutabilityEnum::WriteOnly;
}
throw std::out_of_range("Could not convert Mutability to MutabilityEnum");
}
namespace {
Component get_evse_component(const int32_t evse_id) {
Component component;
component.name = "EVSE";
component.evse = EvseDefinitions::get_evse(evse_id);
return component;
}
Component get_connector_component(const int32_t evse_id, const int32_t connector_id) {
Component component;
component.name = "Connector";
component.evse = EvseDefinitions::get_evse(evse_id, connector_id);
return component;
}
Component get_v2x_component(const int32_t evse_id) {
Component component;
component.name = "V2XChargingCtrlr";
component.evse = EvseDefinitions::get_evse(evse_id);
return component;
}
Component get_iso15118_component(const int32_t evse_id) {
Component component;
component.name = "ISO15118Ctrlr";
component.evse = EvseDefinitions::get_evse(evse_id);
return component;
}
Component get_connected_ev_component(const int32_t evse_id) {
Component component;
component.name = "ConnectedEV";
component.evse = EvseDefinitions::get_evse(evse_id);
return component;
}
ComponentKey get_evse_component_key(const int32_t evse_id) {
ComponentKey component;
component.name = "EVSE";
component.evse_id = evse_id;
return component;
}
ComponentKey get_connector_component_key(const int32_t evse_id, const int32_t connector_id) {
ComponentKey component;
component.name = "Connector";
component.evse_id = evse_id;
component.connector_id = connector_id;
return component;
}
ComponentKey get_v2x_component_key(const int32_t evse_id) {
ComponentKey component;
component.name = "V2XChargingCtrlr";
component.evse_id = evse_id;
return component;
}
ComponentKey get_iso15118_component_key(const int32_t evse_id) {
ComponentKey component;
component.name = "ISO15118Ctrlr";
component.evse_id = evse_id;
return component;
}
ComponentKey get_connected_ev_component_key(const int32_t evse_id) {
ComponentKey component;
component.name = "ConnectedEV";
component.evse_id = evse_id;
return component;
}
// Helper function to construct DeviceModelVariable with common structure
DeviceModelVariable make_variable(const std::string& name, const ocpp::v2::VariableCharacteristics& characteristics,
const std::string& value = "",
ocpp::v2::MutabilityEnum mutability = ocpp::v2::MutabilityEnum::ReadOnly) {
DeviceModelVariable var_data;
var_data.name = name;
var_data.characteristics = characteristics;
var_data.source = VARIABLE_SOURCE_EVEREST;
DbVariableAttribute db_attr;
VariableAttribute attr;
attr.type = ocpp::v2::AttributeEnum::Actual;
attr.value = value;
attr.mutability = mutability;
db_attr.variable_attribute = attr;
var_data.attributes.push_back(db_attr);
return var_data;
}
// Helper function to construct DeviceModelVariable with common structure
DeviceModelVariable
make_variable_with_instance(const std::string& name, const std::string& instance,
const ocpp::v2::VariableCharacteristics& characteristics, const std::string& value = "",
ocpp::v2::MutabilityEnum mutability = ocpp::v2::MutabilityEnum::ReadOnly) {
DeviceModelVariable var_data;
var_data.name = name;
var_data.instance = instance;
var_data.characteristics = characteristics;
var_data.source = VARIABLE_SOURCE_EVEREST;
DbVariableAttribute db_attr;
VariableAttribute attr;
attr.type = ocpp::v2::AttributeEnum::Actual;
attr.value = value;
attr.mutability = mutability;
db_attr.variable_attribute = attr;
var_data.attributes.push_back(db_attr);
return var_data;
}
// Populates EVSE variables
std::vector<DeviceModelVariable> build_evse_variables(const float max_power) {
std::vector<DeviceModelVariable> variables;
auto evse_power_characteristics = EvseDefinitions::Characteristics::EVSEPower;
evse_power_characteristics.maxLimit = max_power;
return {make_variable(ocpp::v2::EvseComponentVariables::Available.name, EvseDefinitions::Characteristics::Available,
"true"),
make_variable(ocpp::v2::EvseComponentVariables::AvailabilityState.name,
EvseDefinitions::Characteristics::AvailabilityState, "Available"),
make_variable(ocpp::v2::EvseComponentVariables::Power.name, evse_power_characteristics),
make_variable(ocpp::v2::EvseComponentVariables::SupplyPhases.name,
EvseDefinitions::Characteristics::SupplyPhases),
make_variable(ocpp::v2::EvseComponentVariables::AllowReset.name,
EvseDefinitions::Characteristics::AllowReset, "false"),
make_variable(ocpp::v2::EvseComponentVariables::ISO15118EvseId.name,
EvseDefinitions::Characteristics::ISO15118EvseId, "DEFAULT_EVSE_ID")};
}
// Populates Connector variables
std::vector<DeviceModelVariable> build_connector_variables() {
return {make_variable(ocpp::v2::ConnectorComponentVariables::Available.name,
ConnectorDefinitions::Characteristics::Available, "true"),
make_variable(ocpp::v2::ConnectorComponentVariables::AvailabilityState.name,
ConnectorDefinitions::Characteristics::AvailabilityState, "Available"),
make_variable(ocpp::v2::ConnectorComponentVariables::Type.name,
ConnectorDefinitions::Characteristics::ConnectorType),
make_variable(ocpp::v2::ConnectorComponentVariables::SupplyPhases.name,
ConnectorDefinitions::Characteristics::SupplyPhases)};
}
// Populates V2X variables
std::vector<DeviceModelVariable> build_v2x_variables(const bool v2x_supported,
const std::string& supported_energy_transfers,
const std::string& supported_operation_modes) {
std::string v2x_supported_string = v2x_supported ? "true" : "false";
return {make_variable(ocpp::v2::V2xComponentVariables::Available.name, V2XDefinitions::Characteristics::Available,
v2x_supported_string),
make_variable(ocpp::v2::V2xComponentVariables::Enabled.name, V2XDefinitions::Characteristics::Enabled,
v2x_supported_string),
make_variable(ocpp::v2::V2xComponentVariables::SupportedEnergyTransferModes.name,
V2XDefinitions::Characteristics::SupportedEnergyTransferModes, supported_energy_transfers),
make_variable(ocpp::v2::V2xComponentVariables::SupportedOperationModes.name,
V2XDefinitions::Characteristics::SupportedOperationModes, supported_operation_modes)};
}
// Populates ISO15118 variables
std::vector<DeviceModelVariable> build_iso15118_variables(const bool iso_supported,
const bool service_renegotiation_supported,
const std::string& supported_protocols) {
return {make_variable(ocpp::v2::ISO15118ComponentVariables::Enabled.name,
ISO15118Definitions::Characteristics::Enabled, iso_supported ? "true" : "false"),
make_variable(ocpp::v2::ISO15118ComponentVariables::ServiceRenegotiationSupport.name,
ISO15118Definitions::Characteristics::ServiceRenegotiationSupport,
service_renegotiation_supported ? "true" : "false"),
make_variable(ocpp::v2::ISO15118ComponentVariables::ProtocolSupported.name,
ISO15118Definitions::Characteristics::ProtocolSupported, supported_protocols)};
}
// Populates ConnectedEV variables
std::vector<DeviceModelVariable> build_connected_ev_variables() {
std::vector<DeviceModelVariable> connected_ev_variables{
make_variable(ocpp::v2::ConnectedEvComponentVariables::Available.name,
ConnectedEVDefinitions::Characteristics::Available, "false"),
make_variable(ocpp::v2::ConnectedEvComponentVariables::VehicleId.name,
ConnectedEVDefinitions::Characteristics::VehicleId, ""),
make_variable(ocpp::v2::ConnectedEvComponentVariables::ProtocolAgreed.name,
ConnectedEVDefinitions::Characteristics::ProtocolAgreed, ""),
make_variable(ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateLeaf.name,
ConnectedEVDefinitions::Characteristics::VehicleCertificateLeaf, ""),
make_variable(ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateSubCa1.name,
ConnectedEVDefinitions::Characteristics::VehicleCertificateSubCa1, ""),
make_variable(ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateSubCa2.name,
ConnectedEVDefinitions::Characteristics::VehicleCertificateSubCa2, ""),
make_variable(ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateRoot.name,
ConnectedEVDefinitions::Characteristics::VehicleCertificateRoot, "")};
const int number_of_variables = NUMBER_OF_CONNECTED_EV_PROTOCOLS + connected_ev_variables.size();
connected_ev_variables.resize(number_of_variables);
std::string variable_name = ocpp::v2::ConnectedEvComponentVariables::get_protocol_supported_by_ev(1).name;
for (int i = 1; i <= NUMBER_OF_CONNECTED_EV_PROTOCOLS; ++i) {
connected_ev_variables.emplace_back(make_variable_with_instance(
variable_name, std::to_string(i), ConnectedEVDefinitions::Characteristics::ProtocolSupportedByEV));
}
return connected_ev_variables;
}
std::string get_everest_config_value(const everest::config::ModuleConfigurationParameters& module_config,
const std::string& impl, const std::string& config_key) {
const auto& config = module_config.at(impl);
for (const auto& config_param : config) {
if (config_param.name == config_key) {
return everest::config::config_entry_to_string(config_param.value);
}
}
throw std::out_of_range("Could not find requested config key: " + config_key);
}
// Populate EVerest module config variables
std::vector<DeviceModelVariable>
build_everest_config_variables(const everest::config::ModuleConfigurationParameters& module_config) {
std::vector<DeviceModelVariable> component_config;
for (const auto& [impl, config_params] : module_config) {
std::string prefix;
if (impl != Everest::config::MODULE_IMPLEMENTATION_ID) {
// prefix variable name with impl + .
prefix = impl + ".";
}
for (const auto& config_param : config_params) {
try {
const auto variable_name = prefix + config_param.name;
ocpp::v2::VariableCharacteristics characteristics;
characteristics.dataType = to_ocpp_data_enum(config_param.characteristics.datatype);
characteristics.supportsMonitoring = false; // TODO: can we enable monitoring support?
// TODO: add unit if/once available?
auto device_model_variable = make_variable(
variable_name, characteristics, get_everest_config_value(module_config, impl, config_param.name),
to_ocpp_mutability_enum(config_param.characteristics.mutability));
component_config.push_back(device_model_variable);
} catch (const std::exception& e) {
EVLOG_error << "Could not add EVerest config entry to OCPP device model: " << e.what();
}
}
}
return component_config;
}
std::string supported_energy_transfer_modes_vector_to_string(
const std::vector<types::iso15118::EnergyTransferMode>& evse_supported_energy_transfers) {
std::string supported_string{};
for (const auto& supported_transfer : evse_supported_energy_transfers) {
supported_string += types::iso15118::energy_transfer_mode_to_string(supported_transfer) + ",";
}
if (!supported_string.empty()) {
supported_string.pop_back();
}
return supported_string;
}
std::string supported_operation_modes_vector_to_string(
const std::vector<ocpp::v2::OperationModeEnum>& evse_supported_operation_modes) {
std::string supported_string{};
for (const auto& operation_mode : evse_supported_operation_modes) {
supported_string += ocpp::v2::conversions::operation_mode_enum_to_string(operation_mode) + ",";
}
if (!supported_string.empty()) {
supported_string.pop_back();
}
return supported_string;
}
std::string build_supported_protocol_string(const std::string& uri, const int32_t major, const int32_t minor) {
return uri + ',' + std::to_string(major) + ',' + std::to_string(minor);
}
} // anonymous namespace
EverestDeviceModelStorage::EverestDeviceModelStorage(
const std::vector<std::unique_ptr<evse_managerIntf>>& r_evse_manager,
const std::vector<std::unique_ptr<iso15118_extensionsIntf>>& r_extensions_15118,
const std::map<int32_t, types::evse_board_support::HardwareCapabilities>& evse_hardware_capabilities_map,
const std::map<int32_t, std::vector<types::iso15118::EnergyTransferMode>>& evse_supported_energy_transfers,
const std::map<int32_t, bool>& evse_service_renegotiation_supported, const std::filesystem::path& db_path,
const std::filesystem::path& migration_files_path,
std::shared_ptr<Everest::config::ConfigServiceClient> config_service_client) :
r_evse_manager(r_evse_manager),
r_extensions_15118(r_extensions_15118),
config_service_client(config_service_client) {
this->module_configs = config_service_client->get_module_configs();
this->mappings = config_service_client->get_mappings();
std::map<ComponentKey, std::vector<DeviceModelVariable>> component_configs;
for (const auto& evse_manager : r_evse_manager) {
const auto evse_info = evse_manager->call_get_evse();
const auto& hw_capabilities = evse_hardware_capabilities_map.at(evse_info.id);
ComponentKey evse_component_key = get_evse_component_key(evse_info.id);
const auto max_power = hw_capabilities.max_current_A_import * 230.0F * hw_capabilities.max_phase_count_import;
component_configs[evse_component_key] = build_evse_variables(max_power);
for (const auto& connector : evse_info.connectors) {
ComponentKey connector_component_key = get_connector_component_key(evse_info.id, connector.id);
component_configs[connector_component_key] = build_connector_variables();
}
const auto v2x_component_key = get_v2x_component_key(evse_info.id);
const auto& supported_energy_transfer_modes = evse_supported_energy_transfers.at(evse_info.id);
// TODO(mlitre): Update dynamically operation mode, depends on future implementation
const bool supports_v2x =
std::find_if(supported_energy_transfer_modes.cbegin(), supported_energy_transfer_modes.cend(),
[](const types::iso15118::EnergyTransferMode& mode) {
return mode == types::iso15118::EnergyTransferMode::AC_BPT or
mode == types::iso15118::EnergyTransferMode::AC_BPT_DER or
mode == types::iso15118::EnergyTransferMode::DC_BPT or
mode == types::iso15118::EnergyTransferMode::DC_ACDP_BPT;
}) != supported_energy_transfer_modes.cend();
component_configs[v2x_component_key] = build_v2x_variables(
supports_v2x, supported_energy_transfer_modes_vector_to_string(supported_energy_transfer_modes),
supported_operation_modes_vector_to_string(
std::vector<ocpp::v2::OperationModeEnum>{ocpp::v2::OperationModeEnum::ChargingOnly}));
const auto connected_ev_component_key = get_connected_ev_component_key(evse_info.id);
component_configs[connected_ev_component_key] = build_connected_ev_variables();
}
for (const auto& extension : r_extensions_15118) {
auto mapping = extension->get_mapping();
if (!mapping.has_value()) {
continue;
}
int evse_id = mapping->evse;
// TODO(mlitre): Correctly fill iso supported protocols
const auto iso15118_component_key = get_iso15118_component_key(evse_id);
component_configs[iso15118_component_key] =
build_iso15118_variables(true, evse_service_renegotiation_supported.at(evse_id), "");
}
// build OCPP2.x device model components from EVerest config // This is our mapping strategy:
// Component.name = module_type
// Component.instance = module_id
// Component.evse.id/connector = mapping of module
// impl mappings are not taken into account at the moment
for (const auto& [module_id_type, module_config] : this->module_configs) {
ComponentKey component_key;
component_key.name = module_id_type.module_type;
component_key.instance = module_id_type.module_id;
const auto& mapping = this->mappings.at(module_id_type.module_id);
if (mapping.module.has_value()) {
const auto& module_mapping = mapping.module.value();
// in OCPP2.x the id and connectorId of the EVSEType must be > 0
if (module_mapping.evse > 0) {
component_key.evse_id = module_mapping.evse;
if (module_mapping.connector.has_value()) {
const auto connector_id = module_mapping.connector.value();
if (connector_id > 0) {
component_key.connector_id = module_mapping.connector;
}
}
}
}
component_configs[component_key] = build_everest_config_variables(module_config);
}
ocpp::v2::InitDeviceModelDb init_device_model_db(db_path, migration_files_path);
init_device_model_db.initialize_database(component_configs, false);
init_device_model_db.close_connection();
this->device_model_storage = std::make_unique<ocpp::v2::DeviceModelStorageSqlite>(db_path);
this->init_evse_components_and_variables(evse_hardware_capabilities_map, evse_supported_energy_transfers);
this->init_everest_config();
}
void EverestDeviceModelStorage::init_evse_components_and_variables(
const std::map<int32_t, types::evse_board_support::HardwareCapabilities>& evse_hardware_capabilities_map,
const std::map<int32_t, std::vector<types::iso15118::EnergyTransferMode>>& evse_supported_energy_transfers) {
for (const auto& evse_manager : r_evse_manager) {
const auto evse_info = evse_manager->call_get_evse();
Component evse_component = get_evse_component(evse_info.id);
if (evse_hardware_capabilities_map.find(evse_info.id) != evse_hardware_capabilities_map.end()) {
this->update_hw_capabilities(evse_component, evse_hardware_capabilities_map.at(evse_info.id));
} else {
EVLOG_error << "No hardware capabilities found for EVSE with ID " << evse_info.id;
}
evse_manager->subscribe_hw_capabilities(
[this, evse_component](const types::evse_board_support::HardwareCapabilities hw_capabilities) {
this->update_hw_capabilities(evse_component, hw_capabilities);
});
Component v2x_component = get_v2x_component(evse_info.id);
if (evse_supported_energy_transfers.find(evse_info.id) != evse_supported_energy_transfers.end()) {
this->update_supported_energy_transfers(v2x_component, evse_supported_energy_transfers.at(evse_info.id));
} else {
EVLOG_error << "No supported energy transfer modes found for EVSE with ID " << evse_info.id;
}
evse_manager->subscribe_supported_energy_transfer_modes(
[this,
v2x_component](const std::vector<types::iso15118::EnergyTransferMode>& supported_energy_transfer_modes) {
this->update_supported_energy_transfers(v2x_component, supported_energy_transfer_modes);
});
// TODO(mlitre): Dynamic update of OperationModeEnum via energy manger conf or subscribed var
for (const auto& connector : evse_info.connectors) {
if (connector.type.has_value()) {
const auto component = get_connector_component(evse_info.id, connector.id);
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
component, ocpp::v2::ConnectorComponentVariables::Type, ocpp::v2::AttributeEnum::Actual,
types::evse_manager::connector_type_enum_to_string(connector.type.value()),
VARIABLE_SOURCE_EVEREST);
}
}
}
for (const auto& extension : r_extensions_15118) {
const auto mapping = extension->get_mapping();
if (!mapping.has_value()) {
continue;
}
const auto evse_id = mapping->evse;
Component iso15118_component = get_iso15118_component(evse_id);
extension->subscribe_service_renegotiation_supported(
[this, iso15118_component](const bool service_renegotiation_supported) {
this->update_service_renegotiation_supported(iso15118_component, service_renegotiation_supported);
});
const auto connected_ev_component = get_connected_ev_component(evse_id);
extension->subscribe_ev_info(
[this, connected_ev_component](const types::iso15118::EvInformation& ev_information) {
this->update_connected_ev_information(connected_ev_component, ev_information);
});
}
}
void EverestDeviceModelStorage::init_everest_config() {
for (const auto& [module_id_type, module_config] : this->module_configs) {
for (const auto& [impl, config_params] : module_config) {
std::string prefix;
if (impl != Everest::config::MODULE_IMPLEMENTATION_ID) {
// prefix variable name with impl + .
prefix = impl + ".";
}
for (const auto& config_param : config_params) {
try {
const auto variable_name = prefix + config_param.name;
Component component;
component.name = module_id_type.module_type;
component.instance = module_id_type.module_id;
Variable variable;
variable.name = variable_name;
ocpp::v2::ComponentVariable component_variable;
component_variable.component = component;
component_variable.variable = variable;
// allows to differentiate variables backed by the EVerest config from other device model variables
this->stored_in_everest_config_service.insert(component_variable);
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
component, variable, ocpp::v2::AttributeEnum::Actual,
get_everest_config_value(module_config, impl, config_param.name), VARIABLE_SOURCE_EVEREST);
} catch (const std::exception& e) {
EVLOG_error << "Could not initialize EVerest config entry in OCPP device model: " << e.what();
}
}
}
}
}
void EverestDeviceModelStorage::update_hw_capabilities(
const Component& evse_component, const types::evse_board_support::HardwareCapabilities& hw_capabilities) {
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
evse_component, ocpp::v2::EvseComponentVariables::SupplyPhases, ocpp::v2::AttributeEnum::Actual,
std::to_string(hw_capabilities.max_phase_count_import), VARIABLE_SOURCE_EVEREST);
// TODO: update EVSE.Power maxLimit value once device model storage interface supports it
}
void EverestDeviceModelStorage::update_supported_energy_transfers(
const ocpp::v2::Component& evse_component,
const std::vector<types::iso15118::EnergyTransferMode>& evse_supported_energy_transfers) {
std::string supported_string = supported_energy_transfer_modes_vector_to_string(evse_supported_energy_transfers);
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
evse_component, ocpp::v2::V2xComponentVariables::SupportedEnergyTransferModes, ocpp::v2::AttributeEnum::Actual,
supported_string, VARIABLE_SOURCE_EVEREST);
}
void EverestDeviceModelStorage::update_supported_operation_modes(
const ocpp::v2::Component& evse_component,
const std::vector<ocpp::v2::OperationModeEnum>& evse_supported_operation_modes) {
std::string supported_string = supported_operation_modes_vector_to_string(evse_supported_operation_modes);
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
evse_component, ocpp::v2::V2xComponentVariables::SupportedOperationModes, ocpp::v2::AttributeEnum::Actual,
supported_string, VARIABLE_SOURCE_EVEREST);
}
void EverestDeviceModelStorage::update_service_renegotiation_supported(const ocpp::v2::Component& iso15118_component,
const bool& service_renegotiation_supported) {
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
iso15118_component, ocpp::v2::ISO15118ComponentVariables::ServiceRenegotiationSupport,
ocpp::v2::AttributeEnum::Actual, service_renegotiation_supported ? "true" : "false", VARIABLE_SOURCE_EVEREST);
}
void EverestDeviceModelStorage::update_connected_ev_information(const ocpp::v2::Component& connected_ev_component,
const types::iso15118::EvInformation& ev_information) {
std::lock_guard<std::mutex> lock(device_model_mutex);
// We update to true even though it should already be true as a precaution
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::Available, ocpp::v2::AttributeEnum::Actual,
"true", VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleId, ocpp::v2::AttributeEnum::Actual,
ev_information.evcc_id, VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::ProtocolAgreed,
ocpp::v2::AttributeEnum::Actual,
build_supported_protocol_string(ev_information.selected_protocol.protocol_namespace,
ev_information.selected_protocol.version_number_major,
ev_information.selected_protocol.version_number_minor),
VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateLeaf,
ocpp::v2::AttributeEnum::Actual,
ev_information.tls_leaf_certificate.has_value() ? ev_information.tls_leaf_certificate.value() : "",
VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateSubCa1,
ocpp::v2::AttributeEnum::Actual,
ev_information.tls_sub_ca_1_certificate.has_value() ? ev_information.tls_sub_ca_1_certificate.value() : "",
VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateSubCa2,
ocpp::v2::AttributeEnum::Actual,
ev_information.tls_sub_ca_2_certificate.has_value() ? ev_information.tls_sub_ca_2_certificate.value() : "",
VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateRoot,
ocpp::v2::AttributeEnum::Actual,
ev_information.tls_root_certificate.has_value() ? ev_information.tls_root_certificate.value() : "",
VARIABLE_SOURCE_EVEREST);
for (const auto& protocol : ev_information.supported_protocols.Protocols) {
this->device_model_storage->set_variable_attribute_value(
connected_ev_component,
ocpp::v2::ConnectedEvComponentVariables::get_protocol_supported_by_ev(protocol.priority),
ocpp::v2::AttributeEnum::Actual,
build_supported_protocol_string(protocol.protocol_namespace, protocol.version_number_major,
protocol.version_number_minor),
VARIABLE_SOURCE_EVEREST);
}
}
void EverestDeviceModelStorage::update_connected_ev_available(const int32_t evse_id, const bool connected) {
const auto connected_ev_component = get_connected_ev_component(evse_id);
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::Available, ocpp::v2::AttributeEnum::Actual,
connected ? "true" : "false", VARIABLE_SOURCE_EVEREST);
if (!connected) {
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleId, ocpp::v2::AttributeEnum::Actual,
"", VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::ProtocolAgreed,
ocpp::v2::AttributeEnum::Actual, "", VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateLeaf,
ocpp::v2::AttributeEnum::Actual, "", VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateSubCa1,
ocpp::v2::AttributeEnum::Actual, "", VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateSubCa2,
ocpp::v2::AttributeEnum::Actual, "", VARIABLE_SOURCE_EVEREST);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleCertificateRoot,
ocpp::v2::AttributeEnum::Actual, "", VARIABLE_SOURCE_EVEREST);
for (int i = 1; i <= NUMBER_OF_CONNECTED_EV_PROTOCOLS; ++i) {
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::get_protocol_supported_by_ev(i),
ocpp::v2::AttributeEnum::Actual, "", VARIABLE_SOURCE_EVEREST);
}
}
}
void EverestDeviceModelStorage::update_connected_ev_vehicle_id(const int32_t evse_id, const std::string& vehicle_id) {
const auto connected_ev_component = get_connected_ev_component(evse_id);
std::lock_guard<std::mutex> lock(device_model_mutex);
this->device_model_storage->set_variable_attribute_value(
connected_ev_component, ocpp::v2::ConnectedEvComponentVariables::VehicleId, ocpp::v2::AttributeEnum::Actual,
vehicle_id, VARIABLE_SOURCE_EVEREST);
}
void EverestDeviceModelStorage::update_power(const int32_t evse_id, const float total_power_active_import) {
std::lock_guard<std::mutex> lock(device_model_mutex);
Component evse_component = get_evse_component(evse_id);
this->device_model_storage->set_variable_attribute_value(
evse_component, ocpp::v2::EvseComponentVariables::Power, ocpp::v2::AttributeEnum::Actual,
std::to_string(total_power_active_import), VARIABLE_SOURCE_EVEREST);
}
ocpp::v2::DeviceModelMap EverestDeviceModelStorage::get_device_model() {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->get_device_model();
}
std::optional<ocpp::v2::VariableAttribute>
EverestDeviceModelStorage::get_variable_attribute(const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum) {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->get_variable_attribute(component_id, variable_id, attribute_enum);
}
std::vector<ocpp::v2::VariableAttribute>
EverestDeviceModelStorage::get_variable_attributes(const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id,
const std::optional<ocpp::v2::AttributeEnum>& attribute_enum) {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->get_variable_attributes(component_id, variable_id, attribute_enum);
}
ocpp::v2::SetVariableStatusEnum EverestDeviceModelStorage::set_variable_attribute_value(
const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum, const std::string& value, const std::string& source) {
std::lock_guard<std::mutex> lock(device_model_mutex);
int evse_id = 0;
if (component_id.evse.has_value()) {
evse_id = component_id.evse.value().id;
}
ocpp::v2::ComponentVariable component_variable;
component_variable.component = component_id;
component_variable.variable = variable_id;
auto stored_in_everest_config_service_it = this->stored_in_everest_config_service.find(component_variable);
if (stored_in_everest_config_service_it != this->stored_in_everest_config_service.end()) {
if (attribute_enum != ocpp::v2::AttributeEnum::Actual) {
return ocpp::v2::SetVariableStatusEnum::Rejected;
}
if (not component_id.instance.has_value()) {
return ocpp::v2::SetVariableStatusEnum::Rejected;
}
const auto module_id = component_id.instance.value();
everest::config::ConfigurationParameterIdentifier identifier;
identifier.module_id = module_id;
const std::string variable_name = variable_id.name;
const auto strpos = variable_name.find(".");
if (strpos != std::string::npos) {
identifier.module_implementation_id = variable_name.substr(0, strpos);
identifier.configuration_parameter_name = variable_name.substr(strpos + 1, variable_name.length());
} else {
identifier.module_implementation_id = Everest::config::MODULE_IMPLEMENTATION_ID;
identifier.configuration_parameter_name = variable_name;
}
const auto result = this->config_service_client->set_config_value(identifier, value);
if (result.set_status == everest::config::SetConfigStatus::Accepted) {
// immediately set it in the libocpp device model as well
const auto libocpp_result = this->device_model_storage->set_variable_attribute_value(
component_id, variable_id, attribute_enum, value, source);
if (libocpp_result != ocpp::v2::SetVariableStatusEnum::Accepted) {
EVLOG_error << "Device model set variable results disagree";
}
return libocpp_result; // FIXME: what to return, libocpp or EVerest result?
} else if (result.set_status == everest::config::SetConfigStatus::Rejected) {
return ocpp::v2::SetVariableStatusEnum::Rejected;
} else if (result.set_status == everest::config::SetConfigStatus::RebootRequired) {
return ocpp::v2::SetVariableStatusEnum::RebootRequired;
}
}
// FIXME: device_model_storage->set_variable_attribute_value does only return accepted or rejected, no other
// checks
// are performed. Since libocpp contains the full device model in memory and does these checks independently,
// it's currently only a minor issue.
return this->device_model_storage->set_variable_attribute_value(component_id, variable_id, attribute_enum, value,
source);
}
std::optional<ocpp::v2::VariableMonitoringMeta>
EverestDeviceModelStorage::set_monitoring_data(const ocpp::v2::SetMonitoringData& data,
const ocpp::v2::VariableMonitorType type) {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->set_monitoring_data(data, type);
}
bool EverestDeviceModelStorage::update_monitoring_reference(const int32_t monitor_id,
const std::string& reference_value) {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->update_monitoring_reference(monitor_id, reference_value);
}
std::vector<ocpp::v2::VariableMonitoringMeta>
EverestDeviceModelStorage::get_monitoring_data(const std::vector<ocpp::v2::MonitoringCriterionEnum>& criteria,
const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id) {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->get_monitoring_data(criteria, component_id, variable_id);
}
ocpp::v2::ClearMonitoringStatusEnum EverestDeviceModelStorage::clear_variable_monitor(int monitor_id,
bool allow_protected) {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->clear_variable_monitor(monitor_id, allow_protected);
}
int32_t EverestDeviceModelStorage::clear_custom_variable_monitors() {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->clear_custom_variable_monitors();
}
void EverestDeviceModelStorage::check_integrity() {
}
bool EverestDeviceModelStorage::create_network_configuration_slot_from_default_schema(std::int32_t new_slot) {
std::lock_guard<std::mutex> lock(device_model_mutex);
return this->device_model_storage->create_network_configuration_slot_from_default_schema(new_slot);
}
} // namespace module::device_model

View File

@@ -0,0 +1,88 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#pragma once
#include <mutex>
#include <generated/interfaces/evse_manager/Interface.hpp>
#include <generated/interfaces/iso15118_extensions/Interface.hpp>
#include <generated/types/evse_board_support.hpp>
#include <generated/types/powermeter.hpp>
#include <ocpp/v2/device_model_storage_interface.hpp>
#include <ocpp/v2/device_model_storage_sqlite.hpp>
#include <utils/config_service.hpp>
namespace module::device_model {
class EverestDeviceModelStorage : public ocpp::v2::DeviceModelStorageInterface {
public:
EverestDeviceModelStorage(
const std::vector<std::unique_ptr<evse_managerIntf>>& r_evse_manager,
const std::vector<std::unique_ptr<iso15118_extensionsIntf>>& r_extensions_15118,
const std::map<int32_t, types::evse_board_support::HardwareCapabilities>& evse_hardware_capabilities_map,
const std::map<int32_t, std::vector<types::iso15118::EnergyTransferMode>>& evse_supported_energy_transfers,
const std::map<int32_t, bool>& evse_service_renegotiation_supported, const std::filesystem::path& db_path,
const std::filesystem::path& migration_files_path,
std::shared_ptr<Everest::config::ConfigServiceClient> config_service_client);
virtual ~EverestDeviceModelStorage() override = default;
virtual ocpp::v2::DeviceModelMap get_device_model() override;
virtual std::optional<ocpp::v2::VariableAttribute>
get_variable_attribute(const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum) override;
virtual std::vector<ocpp::v2::VariableAttribute>
get_variable_attributes(const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id,
const std::optional<ocpp::v2::AttributeEnum>& attribute_enum) override;
virtual ocpp::v2::SetVariableStatusEnum set_variable_attribute_value(const ocpp::v2::Component& component_id,
const ocpp::v2::Variable& variable_id,
const ocpp::v2::AttributeEnum& attribute_enum,
const std::string& value,
const std::string& source) override;
virtual std::optional<ocpp::v2::VariableMonitoringMeta>
set_monitoring_data(const ocpp::v2::SetMonitoringData& data, const ocpp::v2::VariableMonitorType type) override;
virtual bool update_monitoring_reference(const int32_t monitor_id, const std::string& reference_value) override;
virtual std::vector<ocpp::v2::VariableMonitoringMeta>
get_monitoring_data(const std::vector<ocpp::v2::MonitoringCriterionEnum>& criteria,
const ocpp::v2::Component& component_id, const ocpp::v2::Variable& variable_id) override;
virtual ocpp::v2::ClearMonitoringStatusEnum clear_variable_monitor(int monitor_id, bool allow_protected) override;
virtual int32_t clear_custom_variable_monitors() override;
virtual void check_integrity() override;
virtual bool create_network_configuration_slot_from_default_schema(std::int32_t new_slot) override;
/// \brief Updates the actual value of the EVSE Power variable to the given \p total_power_active_import value
void update_power(const int32_t evse_id, const float total_power_active_import);
/// \bried Updates the Available variable for the ConnectedEV component
void update_connected_ev_available(const int32_t evse_id, const bool connected);
/// \bried Updates the VehicleId variable for the ConnectedEV component
void update_connected_ev_vehicle_id(const int32_t evse_id, const std::string& vehicle_id);
private:
const std::vector<std::unique_ptr<evse_managerIntf>>& r_evse_manager;
const std::vector<std::unique_ptr<iso15118_extensionsIntf>>& r_extensions_15118;
std::mutex device_model_mutex;
std::unique_ptr<ocpp::v2::DeviceModelStorageSqlite> device_model_storage;
std::set<ocpp::v2::ComponentVariable> stored_in_everest_config_service;
std::shared_ptr<Everest::config::ConfigServiceClient> config_service_client;
std::map<Everest::config::ModuleIdType, everest::config::ModuleConfigurationParameters> module_configs;
std::map<std::string, ModuleTierMappings> mappings;
void init_evse_components_and_variables(
const std::map<int32_t, types::evse_board_support::HardwareCapabilities>& evse_hardware_capabilities_map,
const std::map<int32_t, std::vector<types::iso15118::EnergyTransferMode>>& evse_supported_energy_transfers);
void update_hw_capabilities(const ocpp::v2::Component& evse_component,
const types::evse_board_support::HardwareCapabilities& hw_capabilities);
void update_supported_energy_transfers(
const ocpp::v2::Component& evse_component,
const std::vector<types::iso15118::EnergyTransferMode>& evse_supported_energy_transfers);
void
update_supported_operation_modes(const ocpp::v2::Component& evse_component,
const std::vector<ocpp::v2::OperationModeEnum>& evse_supported_operation_modes);
void update_service_renegotiation_supported(const ocpp::v2::Component& iso15118_component,
const bool& service_renegotiation_supported);
void update_connected_ev_information(const ocpp::v2::Component& connected_ev_component,
const types::iso15118::EvInformation& ev_information);
void init_everest_config();
};
} // namespace module::device_model

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,49 @@
@startuml
package libocpp {
class ChargePoint {
- device_model: DeviceModel
}
class DeviceModel {
- device_model: DeviceModelStorageInterface
+ get_device_model(): DeviceModelRepresentation
+ get_value(...): T
+ set_value(...): SetVariableStatusEnum
}
interface DeviceModelStorageInterface {
+ get_device_model(): DeviceModelRepresentation
+ get_variable_attribute(...): std::optional<VariableAttribute>
+ set_variable_attribute_value(...): bool
}
class DeviceModelStorageSqlite implements DeviceModelStorageInterface
}
package "OCPP201 Module" {
class EverestDeviceModelStorage implements libocpp.DeviceModelStorageInterface
class ComposedDeviceModelStorage implements libocpp.DeviceModelStorageInterface {
- everest_storage: EverestDeviceModelStorage
- libocpp_storage: DeviceModelStorageSqlite
}
}
note left of ChargePoint
ChargePoint and DeviceModel are
implemented within the library.
end note
note right of ComposedDeviceModelStorage
This implementation will be passed to libocpp's constructor
end note
ChargePoint *-- DeviceModel
DeviceModel *-- DeviceModelStorageInterface
ComposedDeviceModelStorage *-- EverestDeviceModelStorage
ComposedDeviceModelStorage *-- DeviceModelStorageSqlite
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -0,0 +1,46 @@
@startuml
'https://plantuml.com/sequence-diagram
!pragma teoz true
participant CSMS order 10
participant libocpp order 20
participant ComposedDeviceModel order 30
database DeviceModelStorageSqlite order 40
database EverestDeviceModelStorage order 50
autonumber "<b><font color=red>"
skinparam sequenceArrowThickness 2
== Get Device Model at startup ==
ComposedDeviceModel->ComposedDeviceModel: Initialize device model based on component config
libocpp->ComposedDeviceModel: get_device_model
loop For each variable defined in component config
alt internally managed variable
ComposedDeviceModel->InternalStorage: get_value
InternalStorage->ComposedDeviceModel: get_value response
else externally managed variable
ComposedDeviceModel->ExternalStorage: get_value
ExternalStorage->ComposedDeviceModel: get_value response
end
end
ComposedDeviceModel->libocpp: get_device_model response
== SetVariables.req by CSMS ==
CSMS->libocpp: SetVariables.req
loop For each SetVariable request
libocpp->libocpp: Logical internal validation
libocpp->libocpp: Device Model validation
alt request is valid
libocpp->ComposedDeviceModel: set_value
alt internally managed variable
ComposedDeviceModel->InternalStorage: set_value
InternalStorage->ComposedDeviceModel: set_value response
else externally managed variable
ComposedDeviceModel->ExternalStorage: set_value
ExternalStorage->ComposedDeviceModel: set_value response
end
ComposedDeviceModel->libocpp: set_value response
end
end
@enduml

View File

@@ -0,0 +1,458 @@
.. _everest_modules_handwritten_OCPP201:
.. *************************
.. OCPP 2.1 and 2.0.1 Module
.. *************************
This module implements and integrates OCPP 2.0.1 and OCPP 2.1 within EVerest. A connection to a Charging Station Management System (CSMS) can be
established by loading this module as part of the EVerest configuration. This module leverages `libocpp <https://github.com/EVerest/libocpp>`_,
EVerest's OCPP library.
The EVerest config ``config-sil-ocpp201.yaml`` serves as an example for how to add the OCPP201 module
to your EVerest config.
📌 **Note:**: This module can be used for OCPP2.0.1 and OCPP2.1 communication. The module name OCPP201 is kept for now to allow backwards
compatability with existing EVerest configurations. It will likely be renamed in the future. The following descriptions apply for both
OCPP2.0.1 and OCPP2.1, referred by using the term **OCPP2**.
Module configuration
====================
Like for every EVerest module, the configuration parameters are defined as part of the module ``manifest.yaml``. OCPP2 defines
a device model structure and a lot of standardized variables that are used within the functional requirements of the protocol. Please see
Part 1 - Architecture & Topology of the OCPP2.0.1 or OCPP2.1 specification for further information about the device model and how it is composed.
For this module, the device model is configured separately in a JSON format. This module initializes the device model based on the configuration
parameter **DeviceModelConfigPath**. It shall point to the directory where the component configuration files are located in two subdirectories:
* standardized
* custom
The `device model setup from libocpp <https://github.com/EVerest/libocpp/tree/main/config/v2/component_config>`_ serves as a good example.
The split between the directories only has semantic reasons. The **standardized** directory usually does not need to be modified since it contains
standardized components and variables that the specification refers to in its functional requirements. The **custom** directory is meant to be used
for components that are custom for your specific charging station. Especially the number of EVSE and Connector components, as well as their
variables and values, need to be in line with the physical setup of the charging station.
Each device model component is represented by a JSON component config file. This config specifies the component and all its variables,
characteristics, attributes, and monitors. Please see `the documentation for the device model initialization
<https://github.com/EVerest/libocpp/blob/main/doc/v2/ocpp_201_device_model_initialization.md>`_ for further information on how it is set up.
To add a custom component, you can simply add another JSON configuration file for it, and it will automatically be applied and reported.
Configuring the OCPP2 version
=============================
This module supports OCPP2.0.1 and OCPP2.1. The charging station and the CSMS agree on the protocol version to be used during the websocket
handshake. The charging station indicates which versions it supports in the Sec-WebSocket-Protocol header. This header is set based on
the device model configuration **SupportedOcppVersions** of the **InternalCtrlr**. The CSMS then selects the version to use and reports
it in the handshake response. Note that **SupportedOcppVersions** is a comma seperated list and allows you to specify the versions
in the order of preference.
Integration in EVerest
======================
This module leverages **libocpp** `<https://github.com/EVerest/libocpp>`_, EVerest's OCPP library. Libocpp's approach to implementing the OCPP
protocol is to do as much work as possible as part of the library. It therefore fulfills a large amount of protocol requirements internally.
OCPP is a protocol that affects, controls, and monitors many areas of a charging station's operation though. It is therefore required to
integrate libocpp with other parts of EVerest. This integration is done by this module and will be explained in this section.
For a detailed description of libocpp and its functionalities, please refer to `its documentation <https://github.com/EVerest/libocpp>`_.
The ``manifest.yaml`` of this module defines requirements and implementations of EVerest interfaces to integrate the OCPP communication
with other parts of EVerest. In order to describe how the responsibilities for functions and operations required by OCPP are divided between libocpp
and this module, the following sections pick up the requirements of this module and implementations one by one.
Provides: auth_validator
^^^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`auth_token_validator <everest_interfaces_auth_token_validator>`
This interface is implemented to forward authorization requests from EVerest to libocpp. Libocpp contains the business logic to either validate the
authorization request locally using the authorization cache and local authorization list or to forward the request to the CSMS using an
**Authorize.req**. The implementation also covers the validation of Plug&Charge authorization requests.
Provides: auth_provider
^^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`auth_token_provider <everest_interfaces_auth_token_provider>`
This interface is implemented to publish authorization requests from the CSMS within EVerest. An authorization request from the CSMS is implemented
by a **RequestStartTransaction.req**.
Provides: data_transfer
^^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`ocpp_data_transfer <everest_interfaces_ocpp_data_transfer>`
This interface is implemented to provide a command to initiate a **DataTransfer.req** from the charging station to the CSMS.
.. _handwritten_ocpp201_provides-ocpp_generic:
Provides: ocpp_generic
^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`ocpp <everest_interfaces_ocpp>`
This interface is implemented to provide an API to control an OCPP service and to set and get OCPP-specific data.
Provides: session_cost
^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`session_cost <everest_interfaces_session_cost>`
This interface is implemented to publish session costs received by the CSMS as part of the California Pricing whitepaper extension.
Requires: evse_manager
^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`evse_manager <everest_interfaces_evse_manager>`
Typically the :ref:`EvseManager <everest_modules_EvseManager>` module is used to fulfill this requirement.
This module requires (1-128) implementations of this interface in order to integrate with the charge control logic of EVerest. One connection represents
one EVSE. In order to manage multiple EVSEs via one OCPP connection, multiple connections need to be configured in the EVerest config file.
This module makes use of the following commands of this interface:
* **get_evse** to get the EVSE id of the module implementing the **evse_manager** interface at startup
* **pause_charging** to pause charging in case a **TransactionEvent.conf** indicates charging shall be paused
* **stop_transaction** to stop a transaction in case the CSMS stops a transaction by e.g. a **RequestStopTransaction.req**
* **force_unlock** to force the unlock of a connector in case the CSMS sends a **UnlockConnector.req**
* **enable_disable** to set the EVSE to operative or inoperative, e.g. in case the CSMS sends a **ChangeAvailability.req**. This command can be called from
different sources. It therefore contains an argument **priority** in order to override the status if required. OCPP2 uses a priority of 5000, which is
mid-range.
* **set_external_limits** to apply power or ampere limits at the EVSE received by the CSMS using the SmartCharging feature profile. Libocpp contains the
business logic to calculate the composite schedule for received charging profiles. This module gets notified in case charging profiles are added,
changed, or cleared. When notified, this module requests the composite schedule from libocpp and publishes the result via the provided
:ref:`ocpp_generic <handwritten_ocpp201_provides-ocpp_generic>` interface implementation. The duration of the composite schedule can be configured by the configuration parameter
**PublishChargingScheduleDurationS** of this module. The configuration parameter **PublishChargingScheduleIntervalS** defines the interval to use to
periodically retrieve and publish the composite schedules. The configuration parameter **RequestCompositeScheduleUnit** can be used to specify the unit in
which composite schedules are requested and shared within EVerest.
The interface is used to receive the following variables:
* **powermeter** to push powermeter values of an EVSE. Libocpp initiates **MeterValues.req** and **TransactionEvent.req** for meter values internally and is
responsible for complying with the configured intervals and measurands for clock-aligned and sampled meter values.
* **ev_info** to obtain the state of charge (SoC) of an EV. If present, this is reported as part of a **MeterValues.req**
* **limits** to obtain the current offered to the EV. If present, this is reported as part of a **MeterValues.req**
* **session_event** to trigger **StatusNotification.req** and **TransactionEvent.req** based on the reported event. This signal drives the state machine and
the transaction handling of libocpp.
* **waiting_for_external_ready** to obtain the information that a module implementing this interface is waiting for an external ready signal
* **ready** to obtain a ready signal from a module implementing this interface
Requires: connector_zero_sink
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`external_energy_limits <everest_interfaces_external_energy_limits>`
Typically the :ref:`EnergyNode <everest_modules_EnergyNode>` module is used to fulfill this requirement.
This module optionally requires the connection to a module implementing the **external_energy_limits** interface. This connection is used to apply power or
ampere limits at EVSE id zero received by the CSMS using the SmartCharging feature profile.
This module makes use of the following commands of this interface:
* **set_external_limits** to apply power or ampere limits at EVSE id zero received by the CSMS using the SmartCharging feature profile.
Requires: auth
^^^^^^^^^^^^^^
**Interface**: :ref:`auth <everest_interfaces_auth>`
Typically the :ref:`Auth <everest_modules_Auth>` module is used to fulfill this requirement.
This module requires a connection to a module implementing the **auth** interface. This connection is used to set the standardized **ConnectionTimeout**
configuration key if configured and/or changed by the CSMS.
This module makes use of the following commands of this interface:
* **set_connection_timeout** which is e.g., called in case the CSMS uses a **SetVariables.req(EVConnectionTimeout)**
* **set_master_pass_group_id** which is e.g., called in case the CSMS uses a **SetVariables.req(MastrPassGroupId)**
Requires: system
^^^^^^^^^^^^^^^^
**Interface**: :ref:`system <everest_interfaces_system>`
The :ref:`System <everest_modules_System>` module can be used to fulfill this requirement. Note that this module is not meant to be used in production systems without any
modification!
This module requires a connection to a module implementing the **system** interface. This connection is used to execute and control system-wide operations that
can be triggered by the CSMS, like log uploads, firmware updates, and resets.
This module makes use of the following commands of this interface:
* **update_firmware** to forward a **FirmwareUpdate.req** message from the CSMS
* **allow_firmware_installation** to notify the module that the installation of the firmware is now allowed. A prerequisite for this is that all EVSEs are set
to inoperative. This module and libocpp take care of setting the EVSEs to inoperative before calling this command.
* **upload_logs** to forward a **GetLog.req** message from the CSMS
* **is_reset_allowed** to check if a **Reset.req** message from the CSMS shall be accepted or rejected
* **reset** to perform a reset in case of a **Reset.req** message from the CSMS
* **set_system_time** to set the system time communicated by a **BootNotification.conf** or **Heartbeat.conf** messages from the CSMS
* **get_boot_reason** to obtain the boot reason to use it as part of the **BootNotification.req** at startup
The interface is used to receive the following variables:
* **log_status** to obtain the log update status. This triggers a **LogStatusNotification.req** message to inform the CSMS about the current status. This signal is
expected as a result of an **upload_logs** command.
* **firmware_update_status** to obtain the firmware update status. This triggers a **FirmwareStatusNotification.req** message to inform the CSMS about the current
status. This signal is expected as a result of an **update_firmware** command.
Requires: security
^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`evse_security <everest_interfaces_evse_security>`
This module requires a connection to a module implementing the **evse_security** interface. This connection is used to execute security-related operations and to
manage certificates and private keys.
Typically the :ref:`EvseSecurity <everest_modules_EvseSecurity>` module is used to fulfill this requirement.
This module makes use of the following commands of this interface:
* **install_ca_certificate** to handle an **InstallCertificate.req** message from the CSMS
* **delete_certificate** to handle a **DeleteCertificate.req** message from the CSMS
* **update_leaf_certificate** to handle a **CertificateSigned.req** message from the CSMS
* **verify_certificate** to verify certificates from the CSMS that are sent as part of **UpdateFirmware.req** or to validate the contract certificate used for
Plug&Charge.
* **get_installed_certificates** to handle a **GetInstalledCertificateIds.req** message from the CSMS
* **get_v2g_ocsp_request_data** to update the OCSP cache of V2G sub-CA certificates using **GetCertificateStatus.req**. Triggering this message is handled by
libocpp internally
* **get_mo_ocsp_request_data** to include the **iso15118CertificateHashData** as part of an **Authorize.req** for Plug&Charge if required
* **update_ocsp_cache** to update the OCSP cache, which is part of a **GetCertificateStatus** message from the CSMS
* **is_ca_certificate_installed** to verify if a certain CA certificate is installed
* **generate_certificate_signing_request** to generate a CSR that can be used as part of a **SignCertificate.req** message to the CSMS to generate or update the
SECC or CSMS leaf certificates
* **get_leaf_certificate_info** to get the certificate and private key path of the CSMS client certificate used for security profile 3
* **get_verify_file** to get the path to a CA bundle that can be used for verifying, e.g., the CSMS TLS server certificate
* **get_leaf_expiry_days_count** to determine when a leaf certificate expires. This information is used by libocpp in order to renew leaf certificates in case
they expire soon
Note that a lot of conversion between the libocpp types and the generated EVerest types are required for the given commands. Since the
conversion functionality is used by this OCPP2 module and the OCPP1.6 module, it is implemented as a
`separate library <../../../../../lib/everest/conversions/ocpp/>`_ .
Requires: data_transfer
^^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`ocpp_data_transfer <everest_interfaces_ocpp_data_transfer>`
This module optionally requires a connection to a module implementing the **ocpp_data_transfer** interface. This connection is used to handle **DataTransfer.req**
messages from the CSMS. A module implementing this interface can contain custom logic to handle the requests from the CSMS.
This module makes use of the following commands of this interface:
* **data_transfer** to forward **DataTransfer.req** messages from the CSMS
Requires: display_message
^^^^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`display_message <everest_interfaces_display_message>`
This module optionally requires a connection to a module implementing the **display_message** interface. This connection is used to allow the CSMS to display pricing
or other information on the display of a charging station. In order to fulfill the requirements of the California Pricing whitepaper, it is required to connect a
module implementing this interface.
This module makes use of the following commands of this interface:
* **set_display_message** to set a message on the charging station's display. This is executed when the CSMS sends a **SetDisplayMessage.req** message to the charging station.
* **get_display_messages** to forward a **GetDisplayMessage.req** from the CSMS
* **clear_display_message** to forward a **ClearDisplayMessage.req** from the CSMS
Requires: extensions_15118
^^^^^^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`iso15118_extensions <everest_interfaces_iso15118_extensions>`
This module optionally requires (0-128) implementations of this interface in order to share data between ISO15118 and OCPP modules. One
connection represents one ISO15118 module.
This module makes use of the following commands of this interface:
* **set_get_certificate_response** to report that the charging station received a **DataTransfer.conf(Get15118EVCertificateResponse)** from
the CSMS (EV Contract installation for Plug&Charge)
The interface is used to receive the following variables:
* **iso15118_certificate_request** to trigger a **DataTransfer.req(Get15118EVCertificateRequest)** as part of the Plug&Charge process
Error Handling
==============
The **enable_global_errors** flag for this module is true in its manifest. This module is
therefore able to retrieve and process all reported errors from other
modules that are loaded in the same EVerest configuration.
The error reporting via OCPP2 follows the Minimum Required Error Codes (MRECS): https://inl.gov/chargex/mrec/ . This proposes a unified methodology
to define and classify a minimum required set of error codes and how to report them via OCPP2.
StatusNotification
^^^^^^^^^^^^^^^^^^
In contrast to OCPP1.6, error information is not transmitted as part of the StatusNotification.req.
A **StatusNotification.req** with status **Faulted** will be set to faulted only in case the error received is of the special type **evse_manager/Inoperative**.
This indicates that the EVSE is inoperative (not ready for energy transfer).
In OCPP2 errors can be reported using the **NotifyEventRequest.req**. This message is used to report all other errros received.
Current Limitation
^^^^^^^^^^^^^^^^^^
In OCPP2 errors can be reported using the **NotifyEventRequest**
message. The **eventData** property carries the relevant information.
This format of reporting errors deviates from the mechanism used within
EVerest. This data structure forces to map an error to a
component-variable combination. This requires a mapping
mechanism between EVerest errors and component-variable
combination.
Currently this module maps the Error to one of these three Components:
* ChargingStation (if error.origin.mapping.evse is not set or 0)
* EVSE (error.origin.mapping.evse is set and error.origin.mapping.connector is not set)
* Connector (error.origin.mapping.evse is set and error.origin.mapping.connector is set)
The Variable used as part of the NotifyEventRequest is constantly defined to **Problem** for now.
The goal is to have a more advanced mapping of reported errors to the respective component-variable combinations in the future.
Certificate Management
======================
Two leaf certificates are managed by the OCPP communication enabled by this module:
* CSMS Leaf certificate (used for mTLS for SecurityProfile3)
* SECC Leaf certificate (Server certificate for ISO15118)
60 seconds after the first **BootNotification.req** message has been accepted by the CSMS, the charging station will check if the existing
certificates are not present or have been expired. If this is the case, the charging station initiates the process of requesting a new
certificate by sending a certificate signing request to CSMS.
For the CSMS Leaf certificate, this process is only triggered if SecurityProfile 3 is used.
For the SECC Leaf certificate, this process is only triggered if Plug&Charge is enabled by
setting the **ISO15118Ctrlr.V2GCertificateInstallationEnabled** to **true**.
If a certificate has expired is then periodically checked every 12 hours.
In addition to that, the charging station periodically updates the OCSP responses of the sub-CA certificates of the V2G certificate chain.
The OCSP response is cached and can be used as part of the ISO15118 TLS handshake with EVs. The OCSP update is by default performed
every seven days. The timestamp of the last update is stored persistently, so that this process is not necessarily performed
at every start up.
Energy Management and Smart Charging Integration
================================================
OCPP2 defines the SmartCharging feature profile to allow the CSMS to control or influence the power consumption of the charging station.
This module integrates the composite schedule(s) within EVerest's energy management. For further information about smart charging and the
composite schedule calculation please refer to the OCPP2.0.1 or OCPP2.1 specification.
The integration of the composite schedules is implemented through the optional requirement(s) `evse_energy_sink` (interface: `external_energy_limits`)
of this module. Depending on the number of EVSEs configured, each composite limit is communicated via a seperate sink, including the composite schedule
for EVSE with id 0 (representing the whole charging station). The easiest way to explain this is with an example. If your charging station
has two EVSEs you need to connect three modules that implement the `external_energy_limits` interface: One representing evse id 0 and
two representing your actual EVSEs.
📌 **Note:** You have to configure an evse mapping for each module connected via the evse_energy_sink connection. This allows the module to identify
which requirement to use when communicating the limits for the EVSEs. For more information about the module mapping please see
`3-tier module mappings </explanation/tier-module-mappings>`.
This module defines a callback that gets executed every time charging profiles are changed, added or removed by the CSMS. The callback retrieves
the composite schedules for all EVSEs (including evse id 0) and calls the `set_external_limits` command of the respective requirement that implements
the `external_energy_limits` interface. In addition, the config parameter `CompositeScheduleIntervalS` defines a periodic interval to retrieve
the composite schedule also in case no charging profiles have been changed. The configuration parameter `RequestCompositeScheduleDurationS` defines
the duration in seconds of the requested composite schedules starting now. The value configured for `RequestCompositeScheduleDurationS` shall be greater
than the value configured for `CompositeScheduleIntervalS` because otherwise time periods could be missed by the application.
Device model implementation details
===================================
For managing configuration and telemetry data of a charging station, the OCPP2 specification introduces
a device model that is very different to the design of OCPP1.6.
The specified device model comes with these high-level requirements:
* 3-tier model: Break charging station down into 3 main tiers: ChargingStation, EVSE and Connector
* Components and Variables: Break down charging station into components and variables for configuration and telemetry
* Complex data structure for reporting and configuration of variables
* Device model contains variables of the whole charging station, beyond OCPP business logic
The device model of OCPP2 can contain various physical or logical components and
variables. While in OCPP1.6 almost all of the standardized configuration keys are used to influence the control flow of
libocpp, in OCPP2 the configuration and telemetry variables that can be part of the device model go beyond the
control or reporting capabilities of only libocpp. Still there is a large share of standardized variables in OCPP2
that do influence the control flow of libocpp.
Internally and externally managed variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EVerest has multiple different data sources that control the values variables that OCPP requires to report to the CSMS.
It is therefore required to make a distinction between **internally** and **externally** managed variables of the device model.
We define **internally** and **externally** managed variables as follows:
* Internally Managed: Owned, stored and accessed in libocpp in device model storage
Examples: HeartbeatInterval, AuthorizeRemoteStart, SampledDataTxEndedMeasurands, AuthCacheStorage
* Externally Managed: Owned, stored and accessed via EVerest config service (not yet supported)
Examples: ConnectionTimeout, MasterPassGroupId
* For externally managed variables a mapping to the EVerest configuration parameter is defined (not yet supported)
Note that the EVerest config service is not yet implemented. Currently all components and variables are controlled
by the libocpp device model storage implementation.
Device Model Implementation of this module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This module provides an implementation of device model API provided as part of libocpp (it implements
`device_model_storage_interface.hpp`).
The implementation is designed to fullfill the requirements of the device model API even if the components and variables are
controlled by different sources (Internally, Externally).
Device Model Sources
^^^^^^^^^^^^^^^^^^^^
Device Model variables are defined in JSON component configs. For each variable a property `source` can be used to define
the source that controls it. This design allows for a single source of truth for each variable and it
allows the device model implementation of this module to address the correct source for the requested operation.
Today `OCPP` is the only supported source for internally managed variables.
Sources for externally managed configuration variables like the EVerest config service are under development.
Sequence of variable access for internally and externally managed variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. image:: images/sequence_config_service_and_ocpp.png
Class diagram for device model
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. image:: images/device_model_class_diagram.png
Clarification of the device model classes of this diagram:
* DeviceModel:
* Part of libocpp
* Contains device model representation and business logic to prevalidate requests to the device model variables
* Contains reference to device model interface implementation
* DeviceModelStorageInterface:
* Pure virtual class of libocpp
* Defines contract for device model implementations
* DeviceModelStorageSqlite
* Implements DeviceModelStorageInterface as part of libocpp
* This storage holds internally managed variables
* EverestDeviceModelStorage
* Implements DeviceModelStorageInterface as part of EVerest (OCPP201 module)
* Uses EVerest config service to retrieve configuration variables of EVerest modules
* ComposedDeviceModelStorage
* (Final) implementation of DeviceModelStorageInterface as part of EVerest (OCPP201 module)
* A reference of this class will be passed to libocpp's ChargePoint constructor
* Differentiates between externally and internally managed variables

View File

@@ -0,0 +1,104 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP201_ERROR_HANDLING_HPP
#define OCPP201_ERROR_HANDLING_HPP
#include <everest/conversions/ocpp/ocpp_conversions.hpp>
#include <ocpp/v2/ocpp_types.hpp>
#include <utils/error.hpp>
#include <unordered_map>
namespace module {
const std::unordered_map<std::string, std::string> MREC_ERROR_MAP = {
{"connector_lock/MREC1ConnectorLockFailure", "CX001"},
{"evse_board_support/MREC2GroundFailure", "CX002"},
{"evse_board_support/MREC3HighTemperature", "CX003"},
{"evse_board_support/MREC4OverCurrentFailure", "CX004"},
{"evse_board_support/MREC5OverVoltage", "CX005"},
{"evse_board_support/MREC6UnderVoltage", "CX006"},
{"evse_board_support/MREC8EmergencyStop", "CX008"},
{"evse_board_support/MREC10InvalidVehicleMode", "CX010"},
{"evse_board_support/MREC14PilotFault", "CX014"},
{"evse_board_support/MREC15PowerLoss", "CX015"},
{"evse_board_support/MREC17EVSEContactorFault", "CX017"},
{"evse_board_support/MREC18CableOverTempDerate", "CX018"},
{"evse_board_support/MREC19CableOverTempStop", "CX019"},
{"evse_board_support/MREC20PartialInsertion", "CX020"},
{"evse_board_support/MREC23ProximityFault", "CX023"},
{"evse_board_support/MREC24ConnectorVoltageHigh", "CX024"},
{"evse_board_support/MREC25BrokenLatch", "CX025"},
{"evse_board_support/MREC26CutCable", "CX026"},
{"evse_manager/MREC4OverCurrentFailure", "CX004"},
{"ac_rcd/MREC2GroundFailure", "CX002"},
{"evse_manager/MREC22ResistanceFault", "CX022"},
{"evse_manager/MREC11CableCheckFault", "CX011"},
{"evse_manager/MREC5OverVoltage", "CX005"},
};
const auto EVSE_MANAGER_INOPERATIVE_ERROR = "evse_manager/Inoperative";
const auto CHARGING_STATION_COMPONENT_NAME = "ChargingStation";
const auto EVSE_COMPONENT_NAME = "EVSE";
const auto CONNECTOR_COMPONENT_NAME = "Connector";
const auto PROBLEM_VARIABLE_NAME = "Problem";
/// \brief Returns simplified mapping from error origin to OCPP component based on evse and connector ids
ocpp::v2::Component get_component_from_error(const Everest::error::Error& error) {
ocpp::v2::Component component;
if (!error.origin.mapping.has_value()) {
// component is ChargingStation
component.name = CHARGING_STATION_COMPONENT_NAME;
return component;
}
const auto& mapping = error.origin.mapping.value();
const auto evse_id = mapping.evse;
if (!mapping.connector.has_value()) {
// component is EVSE
ocpp::v2::EVSE evse;
evse.id = evse_id;
component.name = EVSE_COMPONENT_NAME;
component.evse = evse;
return component;
}
// component is Connector
ocpp::v2::EVSE evse;
evse.id = evse_id;
evse.connectorId = mapping.connector.value();
component.name = EVSE_COMPONENT_NAME;
component.evse = evse;
return component;
}
/// \brief Derives the EventData from the given \p error, \p cleared and \p event_id parameters
ocpp::v2::EventData get_event_data(const Everest::error::Error& error, const bool cleared, const int32_t event_id) {
ocpp::v2::EventData event_data;
event_data.eventId = event_id; // This can theoretically conflict with eventIds generated in libocpp (e.g.
// for monitoring events), but the spec does not strictly forbid that
event_data.timestamp = ocpp::DateTime(error.timestamp);
event_data.trigger = ocpp::v2::EventTriggerEnum::Alerting;
event_data.cause = std::nullopt; // TODO: use caused_by when available within error object
event_data.actualValue = cleared ? "false" : "true";
if (MREC_ERROR_MAP.count(error.type)) {
event_data.techCode = MREC_ERROR_MAP.at(error.type);
} else {
event_data.techCode = error.type;
}
event_data.techInfo = error.description;
event_data.cleared = cleared;
event_data.transactionId = std::nullopt; // TODO: Do we need to set this here?
event_data.variableMonitoringId = std::nullopt; // We dont need to set this for HardwiredNotification
event_data.eventNotificationType = ocpp::v2::EventNotificationEnum::HardWiredNotification;
event_data.component = get_component_from_error(error);
event_data.variable = {PROBLEM_VARIABLE_NAME}; // TODO: use type of error for mapping to variable?
return event_data;
}
}; // namespace module
#endif // OCPP201_ERROR_HANDLING_HPP

View File

@@ -0,0 +1,133 @@
description: A OCPP charge point / charging station module for OCPP 2.0.1
config:
MessageLogPath:
description: Path to directory where logs of all OCPP messages are written to
type: string
default: /tmp/everest_ocpp_logs
CoreDatabasePath:
description: Path to the persistent SQLite database directory. Please refer to the libocpp documentation for more information
about the database and its structure.
type: string
default: /tmp/ocpp201
DeviceModelDatabasePath:
description: Path to the SQLite database for the device model
type: string
default: device_model_storage.db
EverestDeviceModelDatabasePath:
description: >-
Path to the SQLite databse for the EVerest device model. This database stores components and variables
like EVSE and Connector that are closely related to EVerest and therefore not owned and managed by libocpp.
type: string
default: everest_device_model_storage.db
DeviceModelDatabaseMigrationPath:
description: Path to the migration files for both device models
type: string
default: device_model_migrations
DeviceModelConfigPath:
description: Path to the device model component config directory. Libocpp defines a certain schema for these files. Please refer to the documentation
of libocpp for more information about the configuration options.
type: string
default: component_config
EnableExternalWebsocketControl:
description: If true websocket can be disconnected and connected externally. This parameter is for debug and testing purposes.
type: boolean
default: false
MessageQueueResumeDelay:
description: Time (seconds) to delay resuming the message queue after reconnecting. This parameter was introduced because
some OCTT test cases require that the first message after a reconnect is sent by the CSMS.
type: integer
default: 0
CompositeScheduleIntervalS:
description:
Interval in seconds in which composite schedules are received from libocpp
are be published over MQTT and signalled to connected modules. If the value
is set to 0, composite schedules are only published when changed by CSMS
type: integer
default: 30
RequestCompositeScheduleDurationS:
description: >-
Time (seconds) for which composite schedules are requested.
Schedules are requested from now until now + RequestCompositeScheduleDurationS
type: integer
default: 600
RequestCompositeScheduleUnit:
description: >-
Unit in which composite schedules are requested and shared within EVerest. It is recommended to use
Amps for AC and Watts for DC charging stations.
Allowed values:
- 'A' for Amps
. 'W' for Watts
type: string
default: 'A'
DelayOcppStart:
description: >-
Small delay in time (milliseconds) to start the ocpp chargepoint to allow time for the rest of everest to update the connector status.
This is only used to prevent issues from passing by availlable before preparing on a restart.
type: integer
default: 0
ResetStopDelay:
description: >-
Time (seconds) to delay the stopping of the charge point so that the CSMS has enough time to respond
to the charge point's last messages before resetting.
type: integer
default: 0
provides:
auth_validator:
description: Validates the provided token using CSMS, AuthorizationList or AuthorizationCache
interface: auth_token_validator
auth_provider:
description: Provides authorization requests by CSMS
interface: auth_token_provider
data_transfer:
description: OCPP data transfer towards the CSMS
interface: ocpp_data_transfer
ocpp_generic:
description: Generic OCPP interface.
interface: ocpp
session_cost:
description: Send session cost
interface: session_cost
requires:
evse_manager:
interface: evse_manager
min_connections: 1
max_connections: 128
system:
interface: system
min_connections: 1
max_connections: 1
security:
interface: evse_security
min_connections: 1
max_connections: 1
data_transfer:
interface: ocpp_data_transfer
min_connections: 0
max_connections: 1
auth:
interface: auth
min_connections: 1
max_connections: 1
evse_energy_sink:
interface: external_energy_limits
min_connections: 0
max_connections: 129
display_message:
interface: display_message
min_connections: 0
max_connections: 1
reservation:
interface: reservation
min_connections: 0
max_connections: 1
extensions_15118:
interface: iso15118_extensions
min_connections: 0
max_connections: 128
enable_external_mqtt: true
enable_global_errors: true
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Piet Gömpel
- Kai-Uwe Hermann

View File

@@ -0,0 +1,183 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "ocppImpl.hpp"
#include "everest/conversions/ocpp/evse_security_ocpp.hpp"
#include "ocpp/v2/ocpp_types.hpp"
#include <conversions.hpp>
#include <everest/conversions/ocpp/ocpp_conversions.hpp>
namespace {
inline module::ocpp_generic::ocppImpl::MonitorListEntry convert(const types::ocpp::ComponentVariable& cv) {
using namespace module::conversions;
return {to_ocpp_component(cv.component), to_ocpp_variable(cv.variable)};
}
} // namespace
namespace module {
namespace ocpp_generic {
void ocppImpl::init() {
}
void ocppImpl::ready() {
}
bool ocppImpl::handle_stop() {
// Disconnects the websocket connection and stops the OCPP communication.
// No OCPP messages will be stored and sent after a restart.
bool result{false};
if (mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle stop command";
} else {
std::lock_guard lock(chargepoint_state_mutex);
mod->charging_schedules_timer_stop();
mod->charge_point->stop();
result = true;
}
return result;
}
bool ocppImpl::handle_restart() {
// Connects the websocket and enables OCPP communication after a previous
// stop call.
bool result{false};
if (mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle restart command";
} else {
std::lock_guard lock(chargepoint_state_mutex);
mod->charging_schedules_timer_start();
mod->charge_point->start(ocpp::v2::BootReasonEnum::ApplicationReset, true);
result = true;
}
return result;
}
void ocppImpl::handle_security_event(types::ocpp::SecurityEvent& event) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not yet initialized. Cannot handle security event.";
return;
}
std::optional<ocpp::DateTime> timestamp;
if (event.timestamp.has_value()) {
timestamp = ocpp_conversions::to_ocpp_datetime_or_now(event.timestamp.value());
}
this->mod->charge_point->on_security_event(event.type, event.info, event.critical, timestamp);
}
std::vector<types::ocpp::GetVariableResult>
ocppImpl::handle_get_variables(std::vector<types::ocpp::GetVariableRequest>& requests) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not yet initialized. Cannot handle get variables request.";
std::vector<types::ocpp::GetVariableResult> results;
for (const auto& req : requests) {
types::ocpp::GetVariableResult result;
result.status = types::ocpp::GetVariableStatusEnumType::Rejected;
result.component_variable.component = req.component_variable.component;
result.component_variable.variable = req.component_variable.variable;
result.attribute_type = req.attribute_type;
results.push_back(result);
}
return results;
}
const auto _requests = conversions::to_ocpp_get_variable_data_vector(requests);
const auto response = this->mod->charge_point->get_variables(_requests);
return conversions::to_everest_get_variable_result_vector(response);
}
std::vector<types::ocpp::SetVariableResult>
ocppImpl::handle_set_variables(std::vector<types::ocpp::SetVariableRequest>& requests, std::string& source) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not yet initialized. Cannot handle set variables request.";
std::vector<types::ocpp::SetVariableResult> results;
for (const auto& req : requests) {
types::ocpp::SetVariableResult result;
result.status = types::ocpp::SetVariableStatusEnumType ::Rejected;
result.component_variable.component = req.component_variable.component;
result.component_variable.variable = req.component_variable.variable;
result.attribute_type = req.attribute_type;
results.push_back(result);
}
return results;
}
const auto _requests = conversions::to_ocpp_set_variable_data_vector(requests);
const auto response_map = this->mod->charge_point->set_variables(_requests, source);
std::vector<ocpp::v2::SetVariableResult> response;
for (const auto& [set_variable_data, set_variable_result] : response_map) {
response.push_back(set_variable_result);
}
return conversions::to_everest_set_variable_result_vector(response);
}
types::ocpp::ChangeAvailabilityResponse
ocppImpl::handle_change_availability(types::ocpp::ChangeAvailabilityRequest& request) {
using ChangeAvailabilityStatusEnum = ocpp::v2::ChangeAvailabilityStatusEnum;
ocpp::v2::ChangeAvailabilityResponse result;
result.status = ChangeAvailabilityStatusEnum::Rejected;
if (mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle change availability command";
} else {
const auto ocpp_request = conversions::to_ocpp_change_availability_request(request);
result = mod->charge_point->on_change_availability(ocpp_request);
}
return conversions::to_everest_change_availability_response(result);
}
void ocppImpl::handle_monitor_variables(std::vector<types::ocpp::ComponentVariable>& component_variables) {
using namespace ocpp::v2;
if (mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle monitor variables command";
} else {
std::lock_guard lock(monitor_list_mutex);
if (monitor_list.empty()) {
// register a handler
mod->charge_point->register_variable_listener(
[this](auto&, const Component& component, const Variable& variable, auto&, auto&, auto&,
const std::string& value) { variable_changed(component, variable, value); });
}
// add variables to monitor list
for (const auto& cv : component_variables) {
// failures to insert are likely to be the same variable being
// requested again
(void)monitor_list.insert(convert(cv));
}
}
}
void ocppImpl::variable_changed(const ocpp::v2::Component& component, const ocpp::v2::Variable& variable,
const std::string& value) {
using namespace conversions;
MonitorListEntry entry{component, variable};
bool publish;
{
std::lock_guard lock(monitor_list_mutex);
const auto it = monitor_list.find(entry);
publish = it != monitor_list.end();
}
if (publish) {
// monitor entry exists - publish
types::ocpp::EventData event_data;
event_data.component_variable.component = to_everest_component(component);
event_data.component_variable.variable = to_everest_variable(variable);
event_data.event_id = 0;
event_data.timestamp = ocpp::DateTime();
event_data.trigger = types::ocpp::EventTriggerEnum::Delta;
event_data.actual_value = value;
event_data.event_notification_type = types::ocpp::EventNotificationType::CustomMonitor;
publish_event_data(event_data);
}
}
} // namespace ocpp_generic
} // namespace module

View File

@@ -0,0 +1,80 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_GENERIC_OCPP_IMPL_HPP
#define OCPP_GENERIC_OCPP_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ocpp/Implementation.hpp>
#include "../OCPP201.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
#include <mutex>
#include <ocpp/v2/ocpp_types.hpp>
#include <set>
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace ocpp_generic {
struct Conf {};
class ocppImpl : public ocppImplBase {
public:
ocppImpl() = delete;
ocppImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<OCPP201>& mod, Conf& config) :
ocppImplBase(ev, "ocpp_generic"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
using MonitorListEntry = std::pair<ocpp::v2::Component, ocpp::v2::Variable>;
using MonitorList = std::set<MonitorListEntry>;
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual bool handle_stop() override;
virtual bool handle_restart() override;
virtual void handle_security_event(types::ocpp::SecurityEvent& event) override;
virtual std::vector<types::ocpp::GetVariableResult>
handle_get_variables(std::vector<types::ocpp::GetVariableRequest>& requests) override;
virtual std::vector<types::ocpp::SetVariableResult>
handle_set_variables(std::vector<types::ocpp::SetVariableRequest>& requests, std::string& source) override;
virtual types::ocpp::ChangeAvailabilityResponse
handle_change_availability(types::ocpp::ChangeAvailabilityRequest& request) override;
virtual void handle_monitor_variables(std::vector<types::ocpp::ComponentVariable>& component_variables) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
MonitorList monitor_list;
void variable_changed(const ocpp::v2::Component& component, const ocpp::v2::Variable& variable,
const std::string& value);
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<OCPP201>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
std::mutex chargepoint_state_mutex; // mutex used for start/stop operations
std::mutex monitor_list_mutex;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace ocpp_generic
} // namespace module
#endif // OCPP_GENERIC_OCPP_IMPL_HPP

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "session_costImpl.hpp"
namespace module {
namespace session_cost {
void session_costImpl::init() {
}
void session_costImpl::ready() {
}
} // namespace session_cost
} // namespace module

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef SESSION_COST_SESSION_COST_IMPL_HPP
#define SESSION_COST_SESSION_COST_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/session_cost/Implementation.hpp>
#include "../OCPP201.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace session_cost {
struct Conf {};
class session_costImpl : public session_costImplBase {
public:
session_costImpl() = delete;
session_costImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<OCPP201>& mod, Conf& config) :
session_costImplBase(ev, "session_cost"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// no commands defined for this interface
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<OCPP201>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace session_cost
} // namespace module
#endif // SESSION_COST_SESSION_COST_IMPL_HPP

View File

@@ -0,0 +1,28 @@
set(TEST_TARGET_NAME ${PROJECT_NAME}_transaction_handler_tests)
add_executable(${TEST_TARGET_NAME})
add_dependencies(${TEST_TARGET_NAME} ${MODULE_NAME})
get_target_property(GENERATED_INCLUDE_DIR generate_cpp_files EVEREST_GENERATED_INCLUDE_DIR)
target_include_directories(${TEST_TARGET_NAME} PUBLIC
..
${GENERATED_INCLUDE_DIR}
${CMAKE_BINARY_DIR}/generated/modules/${MODULE_NAME}
)
target_sources(${TEST_TARGET_NAME}
PRIVATE
ocpp_conversions_test.cpp
transaction_handler_tests.cpp
../transaction_handler.cpp
)
target_link_libraries(${TEST_TARGET_NAME}
PRIVATE
everest::ocpp
everest::framework
GTest::gtest_main
)
add_test(${TEST_TARGET_NAME} ${TEST_TARGET_NAME})
ev_register_test_target(${TEST_TARGET_NAME})

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <gtest/gtest.h>
#include <ocpp_generic/ocppImpl.hpp>
namespace {
using namespace module::ocpp_generic;
TEST(MonitorList, insert) {
// check that duplicates are avoided
ocppImpl::MonitorList list;
ocppImpl::MonitorListEntry entryA{{"Component"}, {"Variable"}};
ocppImpl::MonitorListEntry entryB{{"Component"}, {"Variable", "Instance"}};
auto result = list.insert(entryA);
EXPECT_TRUE(std::get<bool>(result));
result = list.insert(entryB);
EXPECT_TRUE(std::get<bool>(result));
ASSERT_EQ(list.size(), 2);
// attempt to add same object again
result = list.insert(entryB);
EXPECT_FALSE(std::get<bool>(result));
ASSERT_EQ(list.size(), 2);
// attempt to add new object with same values
ocppImpl::MonitorListEntry entryC{{"Component"}, {"Variable"}};
result = list.insert(entryC);
EXPECT_FALSE(std::get<bool>(result));
ASSERT_EQ(list.size(), 2);
}
} // namespace

View File

@@ -0,0 +1,133 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <gtest/gtest.h>
#include <transaction_handler.hpp>
namespace module {
class TransactionHandlerTest : public ::testing::Test {
protected:
void SetUp() override {
}
void TearDown() override {
}
std::shared_ptr<TransactionData> transaction_data() {
return std::make_shared<TransactionData>(1, "123", ocpp::DateTime(), ocpp::v2::TriggerReasonEnum::Authorized,
ocpp::v2::ChargingStateEnum::Idle);
}
};
TEST_F(TransactionHandlerTest, test_authorized) {
TransactionHandler transaction_handler(2, {TxStartStopPoint::Authorized}, {TxStartStopPoint::Authorized});
transaction_handler.add_transaction_data(1, transaction_data());
auto res = transaction_handler.submit_event(1, TxEvent::AUTHORIZED);
ASSERT_EQ(res, TxEventEffect::START_TRANSACTION);
transaction_handler.get_transaction_data(1)->started = true;
res = transaction_handler.submit_event(1, TxEvent::AUTHORIZED);
ASSERT_EQ(res, TxEventEffect::NONE);
res = transaction_handler.submit_event(1, TxEvent::DEAUTHORIZED);
ASSERT_EQ(res, TxEventEffect::STOP_TRANSACTION);
}
TEST_F(TransactionHandlerTest, test_power_path_closed) {
TransactionHandler transaction_handler(2, {TxStartStopPoint::PowerPathClosed}, {TxStartStopPoint::PowerPathClosed});
transaction_handler.add_transaction_data(1, transaction_data());
auto res = transaction_handler.submit_event(1, TxEvent::AUTHORIZED);
ASSERT_EQ(res, TxEventEffect::NONE);
res = transaction_handler.submit_event(1, TxEvent::EV_CONNECTED);
ASSERT_EQ(res, TxEventEffect::START_TRANSACTION);
transaction_handler.get_transaction_data(1)->started = true;
res = transaction_handler.submit_event(1, TxEvent::PARKING_BAY_UNOCCUPIED);
ASSERT_EQ(res, TxEventEffect::NONE);
res = transaction_handler.submit_event(1, TxEvent::EV_CONNECTED);
ASSERT_EQ(res, TxEventEffect::NONE);
res = transaction_handler.submit_event(1, TxEvent::EV_DISCONNECTED);
ASSERT_EQ(res, TxEventEffect::STOP_TRANSACTION);
}
TEST_F(TransactionHandlerTest, test_ev_connected) {
TransactionHandler transaction_handler(2, {TxStartStopPoint::EVConnected}, {TxStartStopPoint::EVConnected});
transaction_handler.add_transaction_data(1, transaction_data());
auto res = transaction_handler.submit_event(1, TxEvent::EV_CONNECTED);
ASSERT_EQ(res, TxEventEffect::START_TRANSACTION);
transaction_handler.get_transaction_data(1)->started = true;
res = transaction_handler.submit_event(1, TxEvent::DEAUTHORIZED);
ASSERT_EQ(res, TxEventEffect::NONE);
res = transaction_handler.submit_event(1, TxEvent::EV_DISCONNECTED);
ASSERT_EQ(res, TxEventEffect::STOP_TRANSACTION);
}
TEST_F(TransactionHandlerTest, test_parking_bay_occupied) {
TransactionHandler transaction_handler(2, {TxStartStopPoint::ParkingBayOccupancy},
{TxStartStopPoint::ParkingBayOccupancy});
transaction_handler.add_transaction_data(1, transaction_data());
auto res = transaction_handler.submit_event(1, TxEvent::PARKING_BAY_OCCUPIED);
ASSERT_EQ(res, TxEventEffect::START_TRANSACTION);
transaction_handler.get_transaction_data(1)->started = true;
res = transaction_handler.submit_event(1, TxEvent::EV_DISCONNECTED);
ASSERT_EQ(res, TxEventEffect::NONE);
res = transaction_handler.submit_event(1, TxEvent::PARKING_BAY_UNOCCUPIED);
ASSERT_EQ(res, TxEventEffect::STOP_TRANSACTION);
}
TEST_F(TransactionHandlerTest, test_multiple) {
TransactionHandler transaction_handler(2, {TxStartStopPoint::EVConnected, TxStartStopPoint::Authorized},
{TxStartStopPoint::PowerPathClosed});
transaction_handler.add_transaction_data(1, transaction_data());
auto res = transaction_handler.submit_event(1, TxEvent::EV_CONNECTED);
ASSERT_EQ(res, TxEventEffect::START_TRANSACTION);
transaction_handler.get_transaction_data(1)->started = true;
res = transaction_handler.submit_event(1, TxEvent::DEAUTHORIZED);
ASSERT_EQ(res, TxEventEffect::STOP_TRANSACTION);
transaction_handler.reset_transaction_data(1);
transaction_handler.add_transaction_data(1, transaction_data());
res = transaction_handler.submit_event(1, TxEvent::AUTHORIZED);
ASSERT_EQ(res, TxEventEffect::START_TRANSACTION);
transaction_handler.get_transaction_data(1)->started = true;
res = transaction_handler.submit_event(1, TxEvent::SIGNED_START_DATA_RECEIVED);
ASSERT_EQ(res, TxEventEffect::NONE);
res = transaction_handler.submit_event(1, TxEvent::EV_DISCONNECTED);
ASSERT_EQ(res, TxEventEffect::STOP_TRANSACTION);
}
TEST_F(TransactionHandlerTest, test_invalid_params) {
TransactionHandler transaction_handler(2, {TxStartStopPoint::EVConnected, TxStartStopPoint::Authorized},
{TxStartStopPoint::PowerPathClosed});
ASSERT_THROW(transaction_handler.get_transaction_data(3), std::out_of_range);
ASSERT_THROW(transaction_handler.reset_transaction_data(3), std::out_of_range);
ASSERT_THROW(transaction_handler.add_transaction_data(-1, transaction_data()), std::out_of_range);
ASSERT_THROW(transaction_handler.add_transaction_data(3, transaction_data()), std::out_of_range);
}
} // namespace module

View File

@@ -0,0 +1,123 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <algorithm>
#include <transaction_handler.hpp>
namespace module {
TransactionHandler::TransactionHandler(const int32_t nr_of_evses, const std::set<TxStartStopPoint>& tx_start_points,
const std::set<TxStartStopPoint>& tx_stop_points) :
tx_start_points(tx_start_points), tx_stop_points(tx_stop_points) {
if (tx_start_points.empty() or tx_stop_points.empty()) {
throw std::runtime_error(
"Cannot construct TransactionHandler with either TxStart or TxStop points being empty");
}
if (nr_of_evses <= 0) {
throw std::runtime_error("Cannot construct TransactionHandler with nr_of_evses <= 0");
}
for (int32_t evse_id = 1; evse_id <= nr_of_evses; evse_id++) {
tx_start_stop_conditions[evse_id] = TxStartStopConditions();
evse_id_transaction_data_map[evse_id] = nullptr;
}
}
void TransactionHandler::set_tx_start_points(const std::set<TxStartStopPoint>& tx_start_points) {
this->tx_start_points = tx_start_points;
}
void TransactionHandler::set_tx_stop_points(const std::set<TxStartStopPoint>& tx_stop_points) {
this->tx_stop_points = tx_stop_points;
}
TxEventEffect TransactionHandler::submit_event(const int32_t evse_id, const TxEvent tx_event) {
this->tx_start_stop_conditions[evse_id].submit_event(tx_event);
if (this->should_transaction_start(evse_id)) {
return TxEventEffect::START_TRANSACTION;
}
if (this->should_transaction_stop(evse_id)) {
return TxEventEffect::STOP_TRANSACTION;
}
return TxEventEffect::NONE;
}
void TransactionHandler::add_transaction_data(const int32_t evse_id,
const std::shared_ptr<TransactionData>& transaction_data) {
if (evse_id <= 0 or evse_id > this->tx_start_stop_conditions.size()) {
throw std::out_of_range("Can not add transaction data for evse_id <= 0 or > nr_of_evses");
}
this->evse_id_transaction_data_map[evse_id] = transaction_data;
}
std::shared_ptr<TransactionData> TransactionHandler::get_transaction_data(const int32_t evse_id) {
if (!this->evse_id_transaction_data_map.count(evse_id)) {
throw std::out_of_range("Attempt to get transaction_data for invalid evse_id");
}
return this->evse_id_transaction_data_map[evse_id];
}
int TransactionHandler::get_evse_id(const std::string& transaction_id) {
const auto& found =
std::find_if(this->evse_id_transaction_data_map.cbegin(), this->evse_id_transaction_data_map.cend(),
[transaction_id](const std::pair<const int, std::shared_ptr<module::TransactionData>>& it) {
return it.second != nullptr and it.second->session_id == transaction_id;
});
return found != this->evse_id_transaction_data_map.cend() ? found->first : -1;
}
void TransactionHandler::reset_transaction_data(const int32_t evse_id) {
if (!this->evse_id_transaction_data_map.count(evse_id)) {
throw std::out_of_range("Attempt to reset transaction_data for invalid evse_id");
}
this->evse_id_transaction_data_map[evse_id].reset();
}
bool TransactionHandler::should_transaction_start(const int32_t evse_id) {
if (!this->evse_id_transaction_data_map.count(evse_id)) {
throw std::out_of_range("Can not retrieve if transaction should start for invalid evse_id");
}
// check if transaction data is present
if (this->evse_id_transaction_data_map[evse_id] == nullptr) {
return false;
}
// check if transaction has not yet been started (in OCPP)
if (!this->evse_id_transaction_data_map[evse_id]->started) {
for (const auto& tx_start_condition : this->tx_start_points) {
if (this->tx_start_stop_conditions[evse_id].is_start_condition_fullfilled(tx_start_condition)) {
return true;
}
}
}
return false;
}
bool TransactionHandler::should_transaction_stop(const int32_t evse_id) {
if (!this->evse_id_transaction_data_map.count(evse_id)) {
throw std::out_of_range("Can not retrieve if transaction should stop for invalid evse_id");
}
// check if transaction data is present
if (this->evse_id_transaction_data_map[evse_id] == nullptr) {
return false;
}
// check if transaction is already started (in OCPP) for this evse
if (this->evse_id_transaction_data_map[evse_id]->started) {
for (const auto& tx_stop_condition : this->tx_stop_points) {
if (this->tx_start_stop_conditions[evse_id].is_stop_condition_fullfilled(tx_stop_condition)) {
return true;
}
}
}
return false;
}
} // namespace module

View File

@@ -0,0 +1,223 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <vector>
#include <ocpp/v2/ocpp_types.hpp>
namespace module {
/// \brief TxStartStopPoint of OCPP2.0.1
enum class TxStartStopPoint {
ParkingBayOccupancy,
EVConnected,
Authorized,
PowerPathClosed,
EnergyTransfer,
DataSigned
};
/// \brief TxEvents that influence if conditions for TxStartStopPoints are fullfilled
enum class TxEvent {
NONE,
EV_CONNECTED,
EV_DISCONNECTED,
AUTHORIZED,
DEAUTHORIZED,
PARKING_BAY_OCCUPIED,
PARKING_BAY_UNOCCUPIED,
ENERGY_TRANSFER_STARTED,
ENERGY_TRANSFER_STOPPED,
SIGNED_START_DATA_RECEIVED,
IMMEDIATE_RESET
};
/// \brief Effect to an TxEvent. An effect can either trigger the start of a Transaction, the stop of a transaction or
/// it can have no effect
enum class TxEventEffect {
START_TRANSACTION,
STOP_TRANSACTION,
NONE
};
/// \brief Struct that contains boolean conditions from which a TxEventEffect can be derived
struct TxStartStopConditions {
bool is_authorized = false;
bool is_ev_connected = false;
bool is_parking_bay_occupied = false;
bool is_energy_transfered = false;
bool is_signed_data_received = false;
bool is_immediate_reset = false;
void submit_event(const TxEvent tx_event) {
switch (tx_event) {
case TxEvent::EV_CONNECTED:
is_ev_connected = true;
break;
case TxEvent::EV_DISCONNECTED:
is_ev_connected = false;
is_energy_transfered = false;
break;
case TxEvent::AUTHORIZED:
is_authorized = true;
break;
case TxEvent::DEAUTHORIZED:
is_authorized = false;
break;
case TxEvent::PARKING_BAY_OCCUPIED:
is_parking_bay_occupied = true;
break;
case TxEvent::PARKING_BAY_UNOCCUPIED:
is_parking_bay_occupied = false;
break;
case TxEvent::ENERGY_TRANSFER_STARTED:
is_energy_transfered = true;
break;
case TxEvent::ENERGY_TRANSFER_STOPPED:
is_energy_transfered = false;
break;
case TxEvent::SIGNED_START_DATA_RECEIVED:
is_signed_data_received = true;
break;
case TxEvent::IMMEDIATE_RESET:
is_immediate_reset = true;
break;
case TxEvent::NONE:
break;
}
};
bool is_start_condition_fullfilled(const TxStartStopPoint tx_start_point) {
switch (tx_start_point) {
case TxStartStopPoint::ParkingBayOccupancy:
return is_parking_bay_occupied;
case TxStartStopPoint::EVConnected:
return is_ev_connected;
case TxStartStopPoint::Authorized:
return is_authorized;
case TxStartStopPoint::PowerPathClosed:
return is_authorized and is_ev_connected;
case TxStartStopPoint::EnergyTransfer:
return is_energy_transfered;
case TxStartStopPoint::DataSigned:
return is_signed_data_received;
default:
return false;
}
};
bool is_stop_condition_fullfilled(const TxStartStopPoint tx_stop_point) {
if (is_immediate_reset) {
return true;
}
switch (tx_stop_point) {
case TxStartStopPoint::ParkingBayOccupancy:
return !is_parking_bay_occupied;
case TxStartStopPoint::EVConnected:
return !is_ev_connected;
case TxStartStopPoint::Authorized:
return !is_authorized;
case TxStartStopPoint::PowerPathClosed:
return !is_authorized or !is_ev_connected;
case TxStartStopPoint::EnergyTransfer:
return !is_energy_transfered;
case TxStartStopPoint::DataSigned:
return false;
}
return false;
};
};
/// \brief Contains information that is required for a TransactionEvent (Started / Stopped) message in OCPP2.0.1.
struct TransactionData {
bool started = false;
int32_t connector_id;
std::string session_id;
ocpp::DateTime timestamp;
ocpp::v2::TriggerReasonEnum trigger_reason;
ocpp::v2::MeterValue meter_value;
ocpp::v2::ChargingStateEnum charging_state;
ocpp::v2::ReasonEnum stop_reason = ocpp::v2::ReasonEnum::Other;
std::optional<ocpp::v2::IdToken> id_token;
std::optional<ocpp::v2::IdToken> group_id_token;
std::optional<int32_t> reservation_id;
std::optional<int32_t> remote_start_id;
TransactionData(const int32_t connector_id, const std::string& session_id, const ocpp::DateTime timestamp,
const ocpp::v2::TriggerReasonEnum trigger_reason,
const ocpp::v2::ChargingStateEnum charging_state) :
connector_id(connector_id),
session_id(session_id),
timestamp(timestamp),
trigger_reason(trigger_reason),
charging_state(charging_state){};
};
/// \brief OCPP2.0.1 defines configurable TxStartPoints and TxStopPoints. This class handles TxEvents to determine when
/// OCPP2.0.1 transactions shall start and stop.
class TransactionHandler {
public:
TransactionHandler(const int32_t nr_of_evses, const std::set<TxStartStopPoint>& tx_start_points,
const std::set<TxStartStopPoint>& tx_stop_points);
/// \brief Submits the given \p tx_event at the given \p evse_id . Based on the configured \ref tx_start_points and
/// \ref tx_stop_points, this function determines if a Transaction shall start or stop. The effect of the event is
/// returned
/// \param evse_id
/// \param tx_event
/// \return
TxEventEffect submit_event(const int32_t evse_id, const TxEvent tx_event);
/// \brief Sets the given \p tx_start_points
void set_tx_start_points(const std::set<TxStartStopPoint>& tx_start_points);
/// \brief Sets the given \p tx_stop_points
void set_tx_stop_points(const std::set<TxStartStopPoint>& tx_stop_points);
/// \brief Adds \p transaction_data to \ref evse_id_transaction_data_map for the given \p evse_id
/// \param evse_id
/// \param transaction_data
void add_transaction_data(const int32_t evse_id, const std::shared_ptr<TransactionData>& transaction_data);
/// \brief Gets evse_id associated to a specific transaction id.
std::shared_ptr<TransactionData> get_transaction_data(const int32_t evse_id);
/// \brief Gets transaction_data for the given \p evse_id from the \ref evse_id_transaction_data_map
/// \param transaction_id string corresponding to the id of the transaction
/// \return will return -1 if not found, else will return associated evse_id
int get_evse_id(const std::string& transaction_id);
/// \brief Resets transaction_data for the given \p evse_id in the \ref evse_id_transaction_data_map. It efectively
/// sets the value for the \p evse_id to nullptr
void reset_transaction_data(const int32_t evse_id);
private:
std::set<TxStartStopPoint> tx_start_points;
std::set<TxStartStopPoint> tx_stop_points;
// map that holds information about states that are relevant in order to determine start and stop of OCPP2.0.1
// transactions
std::map<int32_t, TxStartStopConditions> tx_start_stop_conditions;
// map that holds transaction_data for each evse_id . The transaction_data shall be updated based on the present
// information available on the charge point
std::map<int32_t, std::shared_ptr<TransactionData>> evse_id_transaction_data_map;
/// \brief Determines if a transaction shall start for the given \p evse_id based on the present \ref
/// tx_start_stop_conditions for this \p evse_id
bool should_transaction_start(const int32_t evse_id);
/// \brief Determines if a transaction shall stop for the given \p evse_id based on the present \ref
/// tx_start_stop_conditions for this \p evse_id
bool should_transaction_stop(const int32_t evse_id);
};
} // namespace module