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:
105
tools/EVerest-main/modules/EVSE/OCPP201/BUILD.bazel
Normal file
105
tools/EVerest-main/modules/EVSE/OCPP201/BUILD.bazel
Normal 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"],
|
||||
)
|
||||
55
tools/EVerest-main/modules/EVSE/OCPP201/CMakeLists.txt
Normal file
55
tools/EVerest-main/modules/EVSE/OCPP201/CMakeLists.txt
Normal 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
|
||||
1854
tools/EVerest-main/modules/EVSE/OCPP201/OCPP201.cpp
Normal file
1854
tools/EVerest-main/modules/EVSE/OCPP201/OCPP201.cpp
Normal file
File diff suppressed because it is too large
Load Diff
207
tools/EVerest-main/modules/EVSE/OCPP201/OCPP201.hpp
Normal file
207
tools/EVerest-main/modules/EVSE/OCPP201/OCPP201.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
1935
tools/EVerest-main/modules/EVSE/OCPP201/conversions.cpp
Normal file
1935
tools/EVerest-main/modules/EVSE/OCPP201/conversions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
306
tools/EVerest-main/modules/EVSE/OCPP201/conversions.hpp
Normal file
306
tools/EVerest-main/modules/EVSE/OCPP201/conversions.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 |
@@ -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 |
@@ -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
|
||||
458
tools/EVerest-main/modules/EVSE/OCPP201/docs/index.rst
Normal file
458
tools/EVerest-main/modules/EVSE/OCPP201/docs/index.rst
Normal 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
|
||||
104
tools/EVerest-main/modules/EVSE/OCPP201/error_handling.hpp
Normal file
104
tools/EVerest-main/modules/EVSE/OCPP201/error_handling.hpp
Normal 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
|
||||
133
tools/EVerest-main/modules/EVSE/OCPP201/manifest.yaml
Normal file
133
tools/EVerest-main/modules/EVSE/OCPP201/manifest.yaml
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
28
tools/EVerest-main/modules/EVSE/OCPP201/tests/CMakeLists.txt
Normal file
28
tools/EVerest-main/modules/EVSE/OCPP201/tests/CMakeLists.txt
Normal 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})
|
||||
@@ -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
|
||||
@@ -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
|
||||
123
tools/EVerest-main/modules/EVSE/OCPP201/transaction_handler.cpp
Normal file
123
tools/EVerest-main/modules/EVSE/OCPP201/transaction_handler.cpp
Normal 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
|
||||
223
tools/EVerest-main/modules/EVSE/OCPP201/transaction_handler.hpp
Normal file
223
tools/EVerest-main/modules/EVSE/OCPP201/transaction_handler.hpp
Normal 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
|
||||
Reference in New Issue
Block a user