Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

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

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

View File

@@ -0,0 +1,48 @@
#
# 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
"main/ocpp_1_6_charge_pointImpl.cpp"
"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"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,201 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_HPP
#define OCPP_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_1_6_charge_point/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/charger_information/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 <chrono>
#include <date/date.h>
#include <date/tz.h>
#include <everest/timer.hpp>
#include <everest/util/async/monitor.hpp>
#include <filesystem>
#include <memory>
#include <mutex>
#include <queue>
#include <ocpp/common/types.hpp>
#include <ocpp/v16/charge_point.hpp>
#include <ocpp/v16/charge_point_configuration.hpp>
#include <ocpp/v16/types.hpp>
#include <ocpp/v2/ocpp_types.hpp>
struct ErrorRaised : public Everest::error::Error {};
struct ErrorCleared : public Everest::error::Error {};
struct PowermeterPublicKey {
std::string value;
};
using EventData = std::variant<types::evse_manager::SessionEvent, ErrorRaised, ErrorCleared, types::system::LogStatus,
types::system::FirmwareUpdateStatus, PowermeterPublicKey>;
struct Event {
int32_t evse_id;
EventData data;
Event(int32_t evse_id_, EventData data_) : evse_id(evse_id_), data(std::move(data_)) {
}
};
using EvseConnectorMap = std::map<int32_t, std::map<int32_t, int32_t>>;
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {
std::string ChargePointConfigPath;
std::string UserConfigPath;
std::string DatabasePath;
bool EnableExternalWebsocketControl;
int PublishChargingScheduleIntervalS;
int PublishChargingScheduleDurationS;
std::string MessageLogPath;
int MessageQueueResumeDelay;
std::string RequestCompositeScheduleUnit;
int DelayOcppStart;
int ResetStopDelay;
};
class OCPP : public Everest::ModuleBase {
public:
OCPP() = delete;
OCPP(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider,
std::unique_ptr<ocpp_1_6_charge_pointImplBase> p_main,
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<charger_informationIntf>> r_charger_information,
std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager,
std::vector<std::unique_ptr<external_energy_limitsIntf>> r_evse_energy_sink,
std::unique_ptr<reservationIntf> r_reservation, std::unique_ptr<authIntf> r_auth,
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::vector<std::unique_ptr<display_messageIntf>> r_display_message,
std::vector<std::unique_ptr<iso15118_extensionsIntf>> r_extensions_15118, Conf& config) :
ModuleBase(info),
mqtt(mqtt_provider),
p_main(std::move(p_main)),
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_charger_information(std::move(r_charger_information)),
r_evse_manager(std::move(r_evse_manager)),
r_evse_energy_sink(std::move(r_evse_energy_sink)),
r_reservation(std::move(r_reservation)),
r_auth(std::move(r_auth)),
r_system(std::move(r_system)),
r_security(std::move(r_security)),
r_data_transfer(std::move(r_data_transfer)),
r_display_message(std::move(r_display_message)),
r_extensions_15118(std::move(r_extensions_15118)),
config(config){};
Everest::MqttProvider& mqtt;
const std::unique_ptr<ocpp_1_6_charge_pointImplBase> p_main;
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<charger_informationIntf>> r_charger_information;
const std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager;
const std::vector<std::unique_ptr<external_energy_limitsIntf>> r_evse_energy_sink;
const std::unique_ptr<reservationIntf> r_reservation;
const std::unique_ptr<authIntf> r_auth;
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::vector<std::unique_ptr<display_messageIntf>> r_display_message;
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::v16::ChargePoint> charge_point;
std::unique_ptr<ocpp::v16::ChargePointConfiguration> charge_point_config;
std::unique_ptr<Everest::SteadyTimer> charging_schedules_timer;
bool ocpp_stopped = false;
// Return the OCPP connector id from a pair of EVerest EVSE id and connector
// id
int32_t get_ocpp_connector_id(int32_t evse_id, int32_t connector_id);
// 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::filesystem::path ocpp_share_path;
ocpp::v16::ChargingRateUnit composite_schedule_charging_rate_unit;
void set_external_limits(const std::map<int32_t, ocpp::v16::EnhancedChargingSchedule>& charging_schedules);
void publish_charging_schedules(const std::map<int32_t, ocpp::v16::EnhancedChargingSchedule>& charging_schedules);
void init_evse_subscriptions(); // initialize subscriptions to all EVSEs
// provided by r_evse_manager
void init_evse_connector_map();
void init_evse_maps();
void init_module_configuration();
void handle_config_key(const ocpp::v16::KeyValue& kv);
EvseConnectorMap evse_connector_map; // provides access to OCPP connector id by using
// EVerests evse and connector id
std::map<int32_t, int32_t> connector_evse_index_map; // provides access to r_evse_manager index by
// using OCPP connector id
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::set<std::string> resuming_session_ids;
std::mutex event_mutex;
bool started{false};
std::queue<Event> event_queue;
void process_session_event(int32_t evse_id, const types::evse_manager::SessionEvent& session_event);
std::string source_ext_limit;
// 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 // OCPP_HPP

View File

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

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef AUTH_PROVIDER_AUTH_TOKEN_PROVIDER_IMPL_HPP
#define AUTH_PROVIDER_AUTH_TOKEN_PROVIDER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
#include "../OCPP.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<OCPP>& 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<OCPP>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace auth_provider
} // namespace module
#endif // AUTH_PROVIDER_AUTH_TOKEN_PROVIDER_IMPL_HPP

View File

@@ -0,0 +1,124 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
#include "auth_token_validatorImpl.hpp"
#include <conversions.hpp>
#include <everest/conversions/ocpp/ocpp_conversions.hpp>
#include <ocpp/common/types.hpp>
#include <ocpp/v16/ocpp_enums.hpp>
#include <ocpp/v2/ocpp_types.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 result;
result.authorization_status = types::authorization::AuthorizationStatus::Unknown;
return result;
}
if (provided_token.authorization_type == types::authorization::AuthorizationType::PlugAndCharge) {
return validate_pnc_request(provided_token);
} else {
return validate_standard_request(provided_token);
}
};
types::authorization::ValidationResult
auth_token_validatorImpl::validate_pnc_request(const types::authorization::ProvidedIdToken& provided_token) {
types::authorization::ValidationResult validation_result;
try {
// preparing payload for data_transfer_pnc_authorize
std::optional<std::vector<ocpp::v2::OCSPRequestData>> iso15118_certificate_hash_data_opt;
if (provided_token.iso15118CertificateHashData.has_value()) {
std::vector<ocpp::v2::OCSPRequestData> iso15118_certificate_hash_data;
for (const auto& certificate_hash_data : provided_token.iso15118CertificateHashData.value()) {
ocpp::v2::OCSPRequestData v2_certificate_hash_data;
v2_certificate_hash_data.hashAlgorithm =
conversions::to_ocpp_hash_algorithm_enum(certificate_hash_data.hashAlgorithm);
v2_certificate_hash_data.issuerKeyHash = certificate_hash_data.issuerKeyHash;
v2_certificate_hash_data.issuerNameHash = certificate_hash_data.issuerNameHash;
v2_certificate_hash_data.responderURL = certificate_hash_data.responderURL;
v2_certificate_hash_data.serialNumber = certificate_hash_data.serialNumber;
iso15118_certificate_hash_data.push_back(v2_certificate_hash_data);
}
iso15118_certificate_hash_data_opt.emplace(iso15118_certificate_hash_data);
}
// this is the actual OCPP request via DataTransfer.req to CSMS according to
// PnC1.6 whitepaper
const auto authorize_response = mod->charge_point->data_transfer_pnc_authorize(
provided_token.id_token.value, provided_token.certificate, iso15118_certificate_hash_data_opt);
validation_result.authorization_status =
conversions::to_everest_authorization_status(authorize_response.idTokenInfo.status);
validation_result.evse_ids = authorize_response.idTokenInfo.evseId;
if (authorize_response.certificateStatus.has_value()) {
validation_result.certificate_status.emplace(
conversions::to_everest_certificate_status(authorize_response.certificateStatus.value()));
}
if (authorize_response.idTokenInfo.cacheExpiryDateTime.has_value()) {
validation_result.expiry_time.emplace(
authorize_response.idTokenInfo.cacheExpiryDateTime.value().to_rfc3339());
}
if (authorize_response.idTokenInfo.groupIdToken.has_value()) {
validation_result.parent_id_token = {authorize_response.idTokenInfo.groupIdToken.value().idToken.get(),
types::authorization::IdTokenType::Central};
}
} 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;
}
types::authorization::ValidationResult
auth_token_validatorImpl::validate_standard_request(const types::authorization::ProvidedIdToken& provided_token) {
types::authorization::ValidationResult result;
try {
const auto enhanced_id_tag_info =
mod->charge_point->authorize_id_token(ocpp::CiString<20>(provided_token.id_token.value));
const auto id_tag_info = enhanced_id_tag_info.id_tag_info;
result.authorization_status = conversions::to_everest_authorization_status(id_tag_info.status);
if (id_tag_info.expiryDate) {
result.expiry_time = id_tag_info.expiryDate->to_rfc3339();
}
if (id_tag_info.parentIdTag) {
result.parent_id_token = {
id_tag_info.parentIdTag->get(),
types::authorization::IdTokenType::Central}; // For OCPP1.6 no IdTokenType is given,
// so we assume it is a central token
}
if (enhanced_id_tag_info.tariff_message.has_value()) {
// this can be used as the TT field of the OCMF.
const auto& tariff_message = enhanced_id_tag_info.tariff_message.value();
for (const auto& message : tariff_message.message) {
result.tariff_messages.push_back(ocpp_conversions::to_everest_display_message_content(message));
}
}
} catch (const ocpp::StringConversionException& e) {
EVLOG_warning << "Error converting id token to validate: " << e.what();
result.authorization_status = types::authorization::AuthorizationStatus::Unknown;
} catch (const std::exception& e) {
EVLOG_warning << "Unknown error during validation of id token: " << e.what();
result.authorization_status = types::authorization::AuthorizationStatus::Unknown;
}
return result;
};
} // namespace auth_validator
} // namespace module

View File

@@ -0,0 +1,66 @@
// 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 "../OCPP.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<OCPP>& 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<OCPP>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
types::authorization::ValidationResult
validate_standard_request(const types::authorization::ProvidedIdToken& provided_token);
types::authorization::ValidationResult
validate_pnc_request(const types::authorization::ProvidedIdToken& provided_token);
// 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

View File

@@ -0,0 +1,477 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <conversions.hpp>
#include <everest/conversions/ocpp/ocpp_conversions.hpp>
namespace module {
namespace conversions {
ocpp::FirmwareStatusNotification
to_ocpp_firmware_status_notification(const types::system::FirmwareUpdateStatusEnum status) {
switch (status) {
case types::system::FirmwareUpdateStatusEnum::Downloaded:
return ocpp::FirmwareStatusNotification::Downloaded;
case types::system::FirmwareUpdateStatusEnum::DownloadFailed:
return ocpp::FirmwareStatusNotification::DownloadFailed;
case types::system::FirmwareUpdateStatusEnum::Downloading:
return ocpp::FirmwareStatusNotification::Downloading;
case types::system::FirmwareUpdateStatusEnum::DownloadScheduled:
return ocpp::FirmwareStatusNotification::DownloadScheduled;
case types::system::FirmwareUpdateStatusEnum::DownloadPaused:
return ocpp::FirmwareStatusNotification::DownloadPaused;
case types::system::FirmwareUpdateStatusEnum::Idle:
return ocpp::FirmwareStatusNotification::Idle;
case types::system::FirmwareUpdateStatusEnum::InstallationFailed:
return ocpp::FirmwareStatusNotification::InstallationFailed;
case types::system::FirmwareUpdateStatusEnum::Installing:
return ocpp::FirmwareStatusNotification::Installing;
case types::system::FirmwareUpdateStatusEnum::Installed:
return ocpp::FirmwareStatusNotification::Installed;
case types::system::FirmwareUpdateStatusEnum::InstallRebooting:
return ocpp::FirmwareStatusNotification::InstallRebooting;
case types::system::FirmwareUpdateStatusEnum::InstallScheduled:
return ocpp::FirmwareStatusNotification::InstallScheduled;
case types::system::FirmwareUpdateStatusEnum::InstallVerificationFailed:
return ocpp::FirmwareStatusNotification::InstallVerificationFailed;
case types::system::FirmwareUpdateStatusEnum::InvalidSignature:
return ocpp::FirmwareStatusNotification::InvalidSignature;
case types::system::FirmwareUpdateStatusEnum::SignatureVerified:
return ocpp::FirmwareStatusNotification::SignatureVerified;
}
throw std::out_of_range("Could not convert FirmwareUpdateStatusEnum to FirmwareStatusNotification");
}
ocpp::SessionStartedReason to_ocpp_session_started_reason(const types::evse_manager::StartSessionReason reason) {
switch (reason) {
case types::evse_manager::StartSessionReason::EVConnected:
return ocpp::SessionStartedReason::EVConnected;
case types::evse_manager::StartSessionReason::Authorized:
return ocpp::SessionStartedReason::Authorized;
}
throw std::out_of_range("Could not convert types::evse_manager::StartSessionReason to ocpp::SessionStartedReason");
}
ocpp::v16::DataTransferStatus to_ocpp_data_transfer_status(const types::ocpp::DataTransferStatus status) {
switch (status) {
case types::ocpp::DataTransferStatus::Accepted:
return ocpp::v16::DataTransferStatus::Accepted;
case types::ocpp::DataTransferStatus::Rejected:
return ocpp::v16::DataTransferStatus::Rejected;
case types::ocpp::DataTransferStatus::UnknownMessageId:
return ocpp::v16::DataTransferStatus::UnknownMessageId;
case types::ocpp::DataTransferStatus::UnknownVendorId:
return ocpp::v16::DataTransferStatus::UnknownVendorId;
case types::ocpp::DataTransferStatus::Offline:
return ocpp::v16::DataTransferStatus::UnknownVendorId;
}
return ocpp::v16::DataTransferStatus::UnknownVendorId;
}
ocpp::v16::Reason to_ocpp_reason(const types::evse_manager::StopTransactionReason reason) {
switch (reason) {
case types::evse_manager::StopTransactionReason::EmergencyStop:
return ocpp::v16::Reason::EmergencyStop;
case types::evse_manager::StopTransactionReason::EVDisconnected:
return ocpp::v16::Reason::EVDisconnected;
case types::evse_manager::StopTransactionReason::HardReset:
return ocpp::v16::Reason::HardReset;
case types::evse_manager::StopTransactionReason::Local:
return ocpp::v16::Reason::Local;
case types::evse_manager::StopTransactionReason::PowerLoss:
return ocpp::v16::Reason::PowerLoss;
case types::evse_manager::StopTransactionReason::Reboot:
return ocpp::v16::Reason::Reboot;
case types::evse_manager::StopTransactionReason::Remote:
return ocpp::v16::Reason::Remote;
case types::evse_manager::StopTransactionReason::SoftReset:
return ocpp::v16::Reason::SoftReset;
case types::evse_manager::StopTransactionReason::UnlockCommand:
return ocpp::v16::Reason::UnlockCommand;
case types::evse_manager::StopTransactionReason::DeAuthorized:
return ocpp::v16::Reason::DeAuthorized;
case types::evse_manager::StopTransactionReason::EnergyLimitReached:
case types::evse_manager::StopTransactionReason::GroundFault:
case types::evse_manager::StopTransactionReason::LocalOutOfCredit:
case types::evse_manager::StopTransactionReason::MasterPass:
case types::evse_manager::StopTransactionReason::OvercurrentFault:
case types::evse_manager::StopTransactionReason::PowerQuality:
case types::evse_manager::StopTransactionReason::SOCLimitReached:
case types::evse_manager::StopTransactionReason::StoppedByEV:
case types::evse_manager::StopTransactionReason::TimeLimitReached:
case types::evse_manager::StopTransactionReason::Timeout:
case types::evse_manager::StopTransactionReason::ReqEnergyTransferRejected:
case types::evse_manager::StopTransactionReason::Other:
case types::evse_manager::StopTransactionReason::EVSEDisabled:
return ocpp::v16::Reason::Other;
}
throw std::out_of_range("Could not convert types::evse_manager::StopTransactionReason to ocpp::v16::Reason");
}
ocpp::v2::CertificateActionEnum to_ocpp_certificate_action_enum(const types::iso15118::CertificateActionEnum action) {
switch (action) {
case types::iso15118::CertificateActionEnum::Install:
return ocpp::v2::CertificateActionEnum::Install;
case types::iso15118::CertificateActionEnum::Update:
return ocpp::v2::CertificateActionEnum::Update;
}
throw std::out_of_range(
"Could not convert types::iso15118::CertificateActionEnum to ocpp::v2::CertificateActionEnum");
}
ocpp::v16::ReservationStatus to_ocpp_reservation_status(const types::reservation::ReservationResult result) {
switch (result) {
case types::reservation::ReservationResult::Accepted:
return ocpp::v16::ReservationStatus::Accepted;
case types::reservation::ReservationResult::Faulted:
return ocpp::v16::ReservationStatus::Faulted;
case types::reservation::ReservationResult::Occupied:
return ocpp::v16::ReservationStatus::Occupied;
case types::reservation::ReservationResult::Rejected:
return ocpp::v16::ReservationStatus::Rejected;
case types::reservation::ReservationResult::Unavailable:
return ocpp::v16::ReservationStatus::Unavailable;
}
throw std::out_of_range("Could not convert types::reservation::ReservationResult to ocpp::v16::ReservationStatus");
}
ocpp::v16::LogStatusEnumType to_ocpp_log_status_enum_type(const types::system::UploadLogsStatus status) {
switch (status) {
case types::system::UploadLogsStatus::Accepted:
return ocpp::v16::LogStatusEnumType::Accepted;
case types::system::UploadLogsStatus::Rejected:
return ocpp::v16::LogStatusEnumType::Rejected;
case types::system::UploadLogsStatus::AcceptedCanceled:
return ocpp::v16::LogStatusEnumType::AcceptedCanceled;
}
throw std::out_of_range("Could not convert types::system::UploadLogsStatus to ocpp::v16::LogStatusEnumType");
}
ocpp::v16::UpdateFirmwareStatusEnumType
to_ocpp_update_firmware_status_enum_type(const types::system::UpdateFirmwareResponse response) {
switch (response) {
case types::system::UpdateFirmwareResponse::Accepted:
return ocpp::v16::UpdateFirmwareStatusEnumType::Accepted;
case types::system::UpdateFirmwareResponse::Rejected:
return ocpp::v16::UpdateFirmwareStatusEnumType::Rejected;
case types::system::UpdateFirmwareResponse::AcceptedCanceled:
return ocpp::v16::UpdateFirmwareStatusEnumType::AcceptedCanceled;
case types::system::UpdateFirmwareResponse::InvalidCertificate:
return ocpp::v16::UpdateFirmwareStatusEnumType::InvalidCertificate;
case types::system::UpdateFirmwareResponse::RevokedCertificate:
return ocpp::v16::UpdateFirmwareStatusEnumType::RevokedCertificate;
}
throw std::out_of_range(
"Could not convert types::system::UpdateFirmwareResponse to ocpp::v16::UpdateFirmwareStatusEnumType");
}
ocpp::v16::HashAlgorithmEnumType to_ocpp_hash_algorithm_enum_type(const types::iso15118::HashAlgorithm hash_algorithm) {
switch (hash_algorithm) {
case types::iso15118::HashAlgorithm::SHA256:
return ocpp::v16::HashAlgorithmEnumType::SHA256;
case types::iso15118::HashAlgorithm::SHA384:
return ocpp::v16::HashAlgorithmEnumType::SHA384;
case types::iso15118::HashAlgorithm::SHA512:
return ocpp::v16::HashAlgorithmEnumType::SHA512;
}
throw std::out_of_range("Could not convert types::iso15118::HashAlgorithm to ocpp::v16::HashAlgorithmEnumType");
}
ocpp::v16::BootReasonEnum to_ocpp_boot_reason_enum(const types::system::BootReason reason) {
switch (reason) {
case types::system::BootReason::ApplicationReset:
return ocpp::v16::BootReasonEnum::ApplicationReset;
case types::system::BootReason::FirmwareUpdate:
return ocpp::v16::BootReasonEnum::FirmwareUpdate;
case types::system::BootReason::LocalReset:
return ocpp::v16::BootReasonEnum::LocalReset;
case types::system::BootReason::PowerUp:
return ocpp::v16::BootReasonEnum::PowerUp;
case types::system::BootReason::RemoteReset:
return ocpp::v16::BootReasonEnum::RemoteReset;
case types::system::BootReason::ScheduledReset:
return ocpp::v16::BootReasonEnum::ScheduledReset;
case types::system::BootReason::Triggered:
return ocpp::v16::BootReasonEnum::Triggered;
case types::system::BootReason::Unknown:
return ocpp::v16::BootReasonEnum::Unknown;
case types::system::BootReason::Watchdog:
return ocpp::v16::BootReasonEnum::Watchdog;
}
throw std::runtime_error("Could not convert BootReasonEnum");
}
ocpp::Powermeter to_ocpp_power_meter(const types::powermeter::Powermeter& powermeter) {
ocpp::Powermeter ocpp_powermeter;
ocpp_powermeter.timestamp = ocpp_conversions::to_ocpp_datetime_or_now(powermeter.timestamp);
ocpp_powermeter.energy_Wh_import = {powermeter.energy_Wh_import.total, powermeter.energy_Wh_import.L1,
powermeter.energy_Wh_import.L2, powermeter.energy_Wh_import.L3};
ocpp_powermeter.meter_id = powermeter.meter_id;
ocpp_powermeter.phase_seq_error = powermeter.phase_seq_error;
if (powermeter.energy_Wh_export.has_value()) {
const auto energy_wh_export = powermeter.energy_Wh_export.value();
ocpp_powermeter.energy_Wh_export =
ocpp::Energy{energy_wh_export.total, energy_wh_export.L1, energy_wh_export.L2, energy_wh_export.L3};
}
if (powermeter.power_W.has_value()) {
const auto power_w = powermeter.power_W.value();
ocpp_powermeter.power_W = ocpp::Power{power_w.total, power_w.L1, power_w.L2, power_w.L3};
}
if (powermeter.voltage_V.has_value()) {
const auto voltage_v = powermeter.voltage_V.value();
ocpp_powermeter.voltage_V = ocpp::Voltage{voltage_v.DC, voltage_v.L1, voltage_v.L2, voltage_v.L3};
}
if (powermeter.VAR.has_value()) {
const auto var = powermeter.VAR.value();
ocpp_powermeter.VAR = ocpp::ReactivePower{var.total, var.L1, var.L2, var.L3};
}
if (powermeter.current_A.has_value()) {
const auto current_a = powermeter.current_A.value();
ocpp_powermeter.current_A = ocpp::Current{current_a.DC, current_a.L1, current_a.L2, current_a.L3, current_a.N};
}
if (powermeter.frequency_Hz.has_value()) {
const auto frequency_hz = powermeter.frequency_Hz.value();
ocpp_powermeter.frequency_Hz = ocpp::Frequency{frequency_hz.L1, frequency_hz.L2, frequency_hz.L3};
}
return ocpp_powermeter;
}
std::vector<ocpp::Temperature> to_ocpp_temperatures(const std::vector<types::temperature::Temperature>& temperatures) {
std::vector<ocpp::Temperature> ocpp_temperatures;
for (const auto temperature : temperatures) {
ocpp::Temperature ocpp_temperature;
ocpp_temperature.value = temperature.temperature;
if (temperature.location.has_value()) {
ocpp_temperature.location = temperature.location.value();
}
ocpp_temperatures.push_back(ocpp_temperature);
}
return ocpp_temperatures;
}
ocpp::v2::HashAlgorithmEnum to_ocpp_hash_algorithm_enum(const types::iso15118::HashAlgorithm hash_algorithm) {
switch (hash_algorithm) {
case types::iso15118::HashAlgorithm::SHA256:
return ocpp::v2::HashAlgorithmEnum::SHA256;
case types::iso15118::HashAlgorithm::SHA384:
return ocpp::v2::HashAlgorithmEnum::SHA384;
case types::iso15118::HashAlgorithm::SHA512:
return ocpp::v2::HashAlgorithmEnum::SHA512;
}
throw std::out_of_range("Could not convert types::iso15118::HashAlgorithm to ocpp::v16::HashAlgorithmEnumType");
}
types::evse_manager::StopTransactionReason to_everest_stop_transaction_reason(const ocpp::v16::Reason reason) {
switch (reason) {
case ocpp::v16::Reason::EmergencyStop:
return types::evse_manager::StopTransactionReason::EmergencyStop;
case ocpp::v16::Reason::EVDisconnected:
return types::evse_manager::StopTransactionReason::EVDisconnected;
case ocpp::v16::Reason::HardReset:
return types::evse_manager::StopTransactionReason::HardReset;
case ocpp::v16::Reason::Local:
return types::evse_manager::StopTransactionReason::Local;
case ocpp::v16::Reason::PowerLoss:
return types::evse_manager::StopTransactionReason::PowerLoss;
case ocpp::v16::Reason::Reboot:
return types::evse_manager::StopTransactionReason::Reboot;
case ocpp::v16::Reason::Remote:
return types::evse_manager::StopTransactionReason::Remote;
case ocpp::v16::Reason::SoftReset:
return types::evse_manager::StopTransactionReason::SoftReset;
case ocpp::v16::Reason::UnlockCommand:
return types::evse_manager::StopTransactionReason::UnlockCommand;
case ocpp::v16::Reason::DeAuthorized:
return types::evse_manager::StopTransactionReason::DeAuthorized;
case ocpp::v16::Reason::Other:
return types::evse_manager::StopTransactionReason::Other;
}
throw std::out_of_range("Could not convert ocpp::v16::Reason to types::evse_manager::StopTransactionReason");
}
types::system::ResetType to_everest_reset_type(const ocpp::v16::ResetType type) {
switch (type) {
case ocpp::v16::ResetType::Hard:
return types::system::ResetType::Hard;
case ocpp::v16::ResetType::Soft:
return types::system::ResetType::Soft;
}
throw std::out_of_range("Could not convert ocpp::v16::ResetType to types::system::ResetType");
}
types::iso15118::Status to_everest_iso15118_status(const ocpp::v2::Iso15118EVCertificateStatusEnum status) {
switch (status) {
case ocpp::v2::Iso15118EVCertificateStatusEnum::Accepted:
return types::iso15118::Status::Accepted;
case ocpp::v2::Iso15118EVCertificateStatusEnum::Failed:
return types::iso15118::Status::Failed;
}
throw std::out_of_range("Could not convert ocpp::v2::Iso15118EVCertificateStatusEnum to types::iso15118::Status");
}
types::iso15118::CertificateActionEnum
to_everest_certificate_action_enum(const ocpp::v2::CertificateActionEnum action) {
switch (action) {
case ocpp::v2::CertificateActionEnum::Install:
return types::iso15118::CertificateActionEnum::Install;
case ocpp::v2::CertificateActionEnum::Update:
return types::iso15118::CertificateActionEnum::Update;
}
throw std::out_of_range(
"Could not convert ocpp::v2::CertificateActionEnum to types::iso15118::CertificateActionEnum");
}
types::authorization::CertificateStatus
to_everest_certificate_status(const ocpp::v2::AuthorizeCertificateStatusEnum status) {
switch (status) {
case ocpp::v2::AuthorizeCertificateStatusEnum::Accepted:
return types::authorization::CertificateStatus::Accepted;
case ocpp::v2::AuthorizeCertificateStatusEnum::SignatureError:
return types::authorization::CertificateStatus::SignatureError;
case ocpp::v2::AuthorizeCertificateStatusEnum::CertificateExpired:
return types::authorization::CertificateStatus::CertificateExpired;
case ocpp::v2::AuthorizeCertificateStatusEnum::CertificateRevoked:
return types::authorization::CertificateStatus::CertificateRevoked;
case ocpp::v2::AuthorizeCertificateStatusEnum::NoCertificateAvailable:
return types::authorization::CertificateStatus::NoCertificateAvailable;
case ocpp::v2::AuthorizeCertificateStatusEnum::CertChainError:
return types::authorization::CertificateStatus::CertChainError;
case ocpp::v2::AuthorizeCertificateStatusEnum::ContractCancelled:
return types::authorization::CertificateStatus::ContractCancelled;
}
throw std::out_of_range(
"Could not convert ocpp::v2::AuthorizeCertificateStatusEnum to types::authorization::CertificateStatus");
}
types::authorization::AuthorizationStatus to_everest_authorization_status(const ocpp::v16::AuthorizationStatus status) {
switch (status) {
case ocpp::v16::AuthorizationStatus::Accepted:
return types::authorization::AuthorizationStatus::Accepted;
case ocpp::v16::AuthorizationStatus::Blocked:
return types::authorization::AuthorizationStatus::Blocked;
case ocpp::v16::AuthorizationStatus::Expired:
return types::authorization::AuthorizationStatus::Expired;
case ocpp::v16::AuthorizationStatus::Invalid:
return types::authorization::AuthorizationStatus::Invalid;
case ocpp::v16::AuthorizationStatus::ConcurrentTx:
return types::authorization::AuthorizationStatus::ConcurrentTx;
}
throw std::out_of_range(
"Could not convert ocpp::v16::AuthorizationStatus to types::authorization::AuthorizationStatus");
}
types::authorization::AuthorizationStatus
to_everest_authorization_status(const ocpp::v2::AuthorizationStatusEnum status) {
switch (status) {
case ocpp::v2::AuthorizationStatusEnum::Accepted:
return types::authorization::AuthorizationStatus::Accepted;
case ocpp::v2::AuthorizationStatusEnum::Blocked:
return types::authorization::AuthorizationStatus::Blocked;
case ocpp::v2::AuthorizationStatusEnum::ConcurrentTx:
return types::authorization::AuthorizationStatus::ConcurrentTx;
case ocpp::v2::AuthorizationStatusEnum::Expired:
return types::authorization::AuthorizationStatus::Expired;
case ocpp::v2::AuthorizationStatusEnum::Invalid:
return types::authorization::AuthorizationStatus::Invalid;
case ocpp::v2::AuthorizationStatusEnum::NoCredit:
return types::authorization::AuthorizationStatus::NoCredit;
case ocpp::v2::AuthorizationStatusEnum::NotAllowedTypeEVSE:
return types::authorization::AuthorizationStatus::NotAllowedTypeEVSE;
case ocpp::v2::AuthorizationStatusEnum::NotAtThisLocation:
return types::authorization::AuthorizationStatus::NotAtThisLocation;
case ocpp::v2::AuthorizationStatusEnum::NotAtThisTime:
return types::authorization::AuthorizationStatus::NotAtThisTime;
case ocpp::v2::AuthorizationStatusEnum::Unknown:
return types::authorization::AuthorizationStatus::Unknown;
}
throw std::out_of_range(
"Could not convert ocpp::v2::AuthorizationStatusEnum to types::authorization::AuthorizationStatus");
}
types::ocpp::ChargingSchedulePeriod
to_charging_schedule_period(const ocpp::v16::EnhancedChargingSchedulePeriod& period) {
types::ocpp::ChargingSchedulePeriod csp;
csp.start_period = period.startPeriod;
csp.limit = period.limit;
csp.number_phases = period.numberPhases;
csp.stack_level = period.stackLevel;
return csp;
}
types::ocpp::ChargingSchedule to_charging_schedule(const ocpp::v16::EnhancedChargingSchedule& schedule) {
types::ocpp::ChargingSchedule csch = {
0,
ocpp::v16::conversions::charging_rate_unit_to_string(schedule.chargingRateUnit),
{},
schedule.duration,
std::nullopt,
schedule.minChargingRate};
for (const auto& i : schedule.chargingSchedulePeriod) {
csch.charging_schedule_period.emplace_back(to_charging_schedule_period(i));
}
if (schedule.startSchedule.has_value()) {
csch.start_schedule = schedule.startSchedule.value().to_rfc3339();
}
return csch;
}
types::ocpp::BootNotificationResponse
to_everest_boot_notification_response(const ocpp::v16::BootNotificationResponse& boot_notification_response) {
types::ocpp::BootNotificationResponse everest_boot_notification_response;
everest_boot_notification_response.status = to_everest_registration_status(boot_notification_response.status);
everest_boot_notification_response.current_time = boot_notification_response.currentTime.to_rfc3339();
everest_boot_notification_response.interval = boot_notification_response.interval;
return everest_boot_notification_response;
}
types::ocpp::RegistrationStatus
to_everest_registration_status(const ocpp::v16::RegistrationStatus& registration_status) {
switch (registration_status) {
case ocpp::v16::RegistrationStatus::Accepted:
return types::ocpp::RegistrationStatus::Accepted;
case ocpp::v16::RegistrationStatus::Pending:
return types::ocpp::RegistrationStatus::Pending;
case ocpp::v16::RegistrationStatus::Rejected:
return types::ocpp::RegistrationStatus::Rejected;
}
throw std::out_of_range("Could not convert ocpp::v2::RegistrationStatus to types::ocpp::RegistrationStatus");
}
ocpp::v16::DataTransferStatus
to_ocpp_data_transfer_status(const types::display_message::DisplayMessageStatusEnum display_message_status_enum) {
switch (display_message_status_enum) {
case types::display_message::DisplayMessageStatusEnum::Accepted:
return ocpp::v16::DataTransferStatus::Accepted;
case types::display_message::DisplayMessageStatusEnum::NotSupportedMessageFormat:
return ocpp::v16::DataTransferStatus::Rejected;
case types::display_message::DisplayMessageStatusEnum::Rejected:
return ocpp::v16::DataTransferStatus::Rejected;
case types::display_message::DisplayMessageStatusEnum::NotSupportedPriority:
return ocpp::v16::DataTransferStatus::Rejected;
case types::display_message::DisplayMessageStatusEnum::NotSupportedState:
return ocpp::v16::DataTransferStatus::Rejected;
case types::display_message::DisplayMessageStatusEnum::UnknownTransaction:
return ocpp::v16::DataTransferStatus::Rejected;
}
throw std::out_of_range(
"Could not convert types::display_message::DisplayMessageStatusEnum to ocpp::v16::DataTransferStatus");
}
ocpp::v16::DataTransferResponse
to_ocpp_data_transfer_response(const types::display_message::SetDisplayMessageResponse& set_display_message_response) {
ocpp::v16::DataTransferResponse response;
response.status = to_ocpp_data_transfer_status(set_display_message_response.status);
response.data = set_display_message_response.status_info;
return response;
}
} // namespace conversions
} // namespace module

View File

@@ -0,0 +1,121 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_V16_CONVERSIONS_HPP
#define OCPP_V16_CONVERSIONS_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/session_cost.hpp>
#include <generated/types/system.hpp>
#include <ocpp/common/types.hpp>
#include <ocpp/v16/messages/BootNotification.hpp>
#include <ocpp/v16/messages/DataTransfer.hpp>
#include <ocpp/v16/messages/GetDiagnostics.hpp>
#include <ocpp/v16/messages/LogStatusNotification.hpp>
#include <ocpp/v16/messages/ReserveNow.hpp>
#include <ocpp/v16/messages/StopTransaction.hpp>
#include <ocpp/v16/types.hpp>
#include <ocpp/v2/messages/DeleteCertificate.hpp>
#include <ocpp/v2/messages/Get15118EVCertificate.hpp>
namespace module {
namespace conversions {
/// \brief Converts a given types::system::FirmwareUpdateStatusEnum \p status to a ocpp::FirmwareStatusNotification.
ocpp::FirmwareStatusNotification
to_ocpp_firmware_status_notification(const types::system::FirmwareUpdateStatusEnum status);
/// \brief Converts a given types::evse_manager::StartSessionReason \p reason to a ocpp::SessionStartedReason.
ocpp::SessionStartedReason to_ocpp_session_started_reason(const types::evse_manager::StartSessionReason reason);
/// \brief Converts a given types::ocpp::DataTransferStatus \p status to a ocpp::v16::DataTransferStatus.
ocpp::v16::DataTransferStatus to_ocpp_data_transfer_status(const types::ocpp::DataTransferStatus status);
/// \brief Converts a given types::evse_manager::StopTransactionReason \p reason to a ocpp::v16::Reason.
ocpp::v16::Reason to_ocpp_reason(const types::evse_manager::StopTransactionReason reason);
/// \brief Converts a given types::iso15118::CertificateActionEnum \p action to a
/// ocpp::v2::CertificateActionEnum.
ocpp::v2::CertificateActionEnum to_ocpp_certificate_action_enum(const types::iso15118::CertificateActionEnum action);
/// \brief Converts a given types::reservation::ReservationResult \p result to a ocpp::v16::ReservationStatus.
ocpp::v16::ReservationStatus to_ocpp_reservation_status(const types::reservation::ReservationResult result);
/// \brief Converts a given types::system::UploadLogsStatus \p status to a ocpp::v16::LogStatusEnumType.
ocpp::v16::LogStatusEnumType to_ocpp_log_status_enum_type(const types::system::UploadLogsStatus status);
/// \brief Converts a given types::system::UpdateFirmwareResponse \p response to a
/// ocpp::v16::UpdateFirmwareStatusEnumType.
ocpp::v16::UpdateFirmwareStatusEnumType
to_ocpp_update_firmware_status_enum_type(const types::system::UpdateFirmwareResponse response);
/// \brief Converts a given types::iso15118::HashAlgorithm \p hash_algorithm to a
/// ocpp::v16::HashAlgorithmEnumType.
ocpp::v16::HashAlgorithmEnumType to_ocpp_hash_algorithm_enum_type(const types::iso15118::HashAlgorithm hash_algorithm);
/// \brief Converts a given types::iso15118::Status \p status to a ocpp::v2::Iso15118EVCertificateStatusEnum.
ocpp::v16::BootReasonEnum to_ocpp_boot_reason_enum(const types::system::BootReason reason);
/// \brief Converts a given types::powermeter::Powermeter \p powermeter to a ocpp::Powermeter
ocpp::Powermeter to_ocpp_power_meter(const types::powermeter::Powermeter& powermeter);
/// \brief Converts a given vector of types::temperature::Temperature \p powermeter to a vector of ocpp::Temperature
std::vector<ocpp::Temperature> to_ocpp_temperatures(const std::vector<types::temperature::Temperature>& temperatures);
/// \brief Converts a given types::iso15118::HashAlgorithm \p hash_algorithm to a ocpp::v2::HashAlgorithmEnum.
ocpp::v2::HashAlgorithmEnum to_ocpp_hash_algorithm_enum(const types::iso15118::HashAlgorithm hash_algorithm);
/// \brief Converts a given ocpp::v16::Reason \p reason to a types::evse_manager::StopTransactionReason.
types::evse_manager::StopTransactionReason to_everest_stop_transaction_reason(const ocpp::v16::Reason reason);
/// \brief Converts a given ocpp::v16::ResetType \p type to a types::system::ResetType.
types::system::ResetType to_everest_reset_type(const ocpp::v16::ResetType type);
/// \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::CertificateActionEnum \p action to a
/// types::iso15118::CertificateActionEnum.
types::iso15118::CertificateActionEnum to_everest_certificate_action_enum(const ocpp::v2::CertificateActionEnum action);
/// \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::v16::AuthorizationStatus \p status to a types::authorization::AuthorizationStatus.
types::authorization::AuthorizationStatus to_everest_authorization_status(const ocpp::v16::AuthorizationStatus status);
/// \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 Convert ocpp::v16::EnhancedChargingSchedulePeriod to types::ocpp::ChargingSchedulePeriod
types::ocpp::ChargingSchedulePeriod
to_charging_schedule_period(const ocpp::v16::EnhancedChargingSchedulePeriod& period);
/// \brief Convert ocpp::v16::EnhancedChargingSchedule to types::ocpp::ChargingSchedule
types::ocpp::ChargingSchedule to_charging_schedule(const ocpp::v16::EnhancedChargingSchedule& schedule);
/// \brief Converts a given ocpp::v16::BootNotificationResponse \p boot_notification_response to a
/// types::ocpp::BootNotificationResponse
types::ocpp::BootNotificationResponse
to_everest_boot_notification_response(const ocpp::v16::BootNotificationResponse& boot_notification_response);
/// \brief Converts a given ocpp::v16::RegistrationStatus \p registration_status to a
/// types::ocpp::RegistrationStatus
types::ocpp::RegistrationStatus
to_everest_registration_status(const ocpp::v16::RegistrationStatus& registration_status);
ocpp::v16::DataTransferResponse
to_ocpp_data_transfer_response(const types::display_message::SetDisplayMessageResponse& set_display_message_response);
} // namespace conversions
} // namespace module
#endif // OCPP_V16_CONVERSIONS_HPP

View File

@@ -0,0 +1,51 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "ocpp_data_transferImpl.hpp"
namespace module {
namespace data_transfer {
void ocpp_data_transferImpl::init() {
}
void ocpp_data_transferImpl::ready() {
}
types::ocpp::DataTransferStatus to_everest(ocpp::v16::DataTransferStatus status) {
switch (status) {
case ocpp::v16::DataTransferStatus::Accepted:
return types::ocpp::DataTransferStatus::Accepted;
case ocpp::v16::DataTransferStatus::Rejected:
return types::ocpp::DataTransferStatus::Rejected;
case ocpp::v16::DataTransferStatus::UnknownMessageId:
return types::ocpp::DataTransferStatus::UnknownMessageId;
case ocpp::v16::DataTransferStatus::UnknownVendorId:
return types::ocpp::DataTransferStatus::UnknownVendorId;
default:
return types::ocpp::DataTransferStatus::UnknownVendorId;
}
}
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 handle data transfer command";
types::ocpp::DataTransferResponse response;
response.status = types::ocpp::DataTransferStatus::Offline;
return response;
}
auto ocpp_response = mod->charge_point->data_transfer(request.vendor_id, request.message_id, request.data);
types::ocpp::DataTransferResponse response;
if (ocpp_response.has_value()) {
response.status = to_everest(ocpp_response.value().status);
response.data = ocpp_response.value().data;
} else {
response.status = types::ocpp::DataTransferStatus::Offline;
}
return response;
}
} // namespace data_transfer
} // namespace module

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP
#define DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ocpp_data_transfer/Implementation.hpp>
#include "../OCPP.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<OCPP>& 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<OCPP>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace data_transfer
} // namespace module
#endif // DATA_TRANSFER_OCPP_DATA_TRANSFER_IMPL_HPP

View File

@@ -0,0 +1,400 @@
.. _everest_modules_handwritten_OCPP:
.. **************
.. OCPP1.6 Module
.. **************
This module implements and integrates OCPP1.6 within EVerest, including all feature profiles defined by the specification. 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-ocpp.yaml`` serves as an example for how to add the OCPP module to
your EVerest config.
Module configuration
====================
Like for every EVerest module, the configuration parameters are defined as part of the module ``manifestyaml``. This module
is a little special though. The OCPP1.6 protocol defines a lot of standardized configuration keys that are used as part of the functional
requirements of the specification. These configuration keys mainly influence the control flow of libocpp and are managed by a separate
JSON configuration file. The module uses the configuration parameter **ChargePointConfigPath** to point to this file.
:doc:`This EVerest OCPP tutorial </tutorials/ocpp16>`, the OCPP specification, and
`libocpp's documentation <https://github.com/EVerest/libocpp>`_ are great resources to learn about the different configuration options.
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: main
^^^^^^^^^^^^^^
**Interface**: :ref:`ocpp_1_6_charge_point <everest_interfaces_ocpp_1_6_charge_point>`
This interface is implemented to provide an API to control the websocket connection and to control and retrieve OCPP-specific data like
security events and configuration keys.
*Note: This interface is deprecated soon and will be removed soon. The functionality is already covered by the generic interface*
:ref:`ocpp <everest_interfaces_ocpp>` *which is used by this module and OCPP201.*
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 by triggering a
`DataTransfer.req(Authorize)`.
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
turned out by a **RemoteStartTransaction.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_ocpp_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 **StopTransaction.conf** indicates charging shall be paused
* **resume_charging** to resume charging
* **stop_transaction** to stop a transaction in case the CSMS stops a transaction by e.g. a **RemoteStopTransaction.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. OCPP uses
a priority of 5000, which is mid-range.
* **set_external_limits** to apply a 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_ocpp_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.
* **external_ready_to_start_charging**: To signal that the module has started to establish an OCPP connection to the CSMS
The interface is used to receive the following variables:
* **powermeter** to push powermeter values of an EVSE. Libocpp initiates **MeterValues.req** internally and is responsible to comply 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**, **StartTransaction.req**, and **StopTransaction.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 a power or ampere limits for EVSE id zero (the whole charging station).
Requires: reservation
^^^^^^^^^^^^^^^^^^^^^
**Interface**: :ref:`reservation <everest_interfaces_reservation>`
Typically the :ref:`Auth <everest_modules_Auth>` module is used to fulfill this requirement.
This module requires a connection to a module implementing the `reservation` interface. This connection is used to apply reservation
requests from the CSMS.
This module makes use of the following commands of this interface:
* **reserve_now** which is called when the CSMS sends a **ReserveNow.req**
* **cancel_reservation** which is called when the CSMS sends a **CancelReservation.req**
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 **ChangeConfiguration.req(ConnectionTimeout)**
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 **UpdateFirmware.req** or **SignedUpdateFirmware.req** messages 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 **GetDiagnostics.req** or **GetLog.req** messages 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** or **DiagnosticsStatusNotification.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** or
**SignedFirmwareStatusNotification.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 **InstallCertificate.req** and **DataTransfer.req(InstallCertificate)** messages from the CSMS
* **delete_certificate** to handle **DeleteCertificate.req** and **DataTransfer.req(DeleteCertificate)** messages from the CSMS
* **update_leaf_certificate** to handle **CertificateSigned.req** and **DataTransfer.req(CertificateSigned)** messages from the CSMS
* **verify_certificate** to verify certificates from the CSMS that are sent as part of **SignedUpdateFirmware.req**
* **get_installed_certificates** to handle **GetInstalledCertificateIds.req** and **DataTransfer.req(GetInstalledCertificateIds)**
messages from the CSMS
* **get_v2g_ocsp_request_data** to update the OCSP cache of V2G sub-CA certificates using a **DataTransfer.req(GetCertificateStatus)**.
Triggering this message is handled by libocpp internally
* **get_mo_ocsp_request_data** to include the **iso15118CertificateHashData** as part of a **DataTransfer.req(Authorize)** for Plug&Charge
if required
* **update_ocsp_cache** to update the OCSP cache which is part of a **DataTransfer.conf(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** and
`DataTransfer.req(SignCertificate)` message to the CSMS.
* **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 OCPP module and the OCPP201 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.
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
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
Global Errors and Error Reporting
=================================
The **enable_global_errors** flag for this module is enabled in its manifest. This module is therefore able to retrieve and process all reported errors
from other modules loaded in the same EVerest configuration.
In OCPP1.6 errors can be reported using the **StatusNotification.req** message. If this module gets notified about a raised error,
it initiates a **StatusNotification.req** that contains information about the error that has been raised.
The field **status** of the **StatusNotification.req** will be set to faulted only in case the error is of the special type
**evse_manager/Inoperative**. The field **connectorId** is set based on the mapping (for EVSE id and connector id) of the origin of the error.
If no mapping is provided, the error will be reported on connectorId 0. Note that the mapping can be configured per module inside the
EVerest config file.
For all other errors, raised in EVerest, the following mapping to an
OCPP **StatusNotification.req** will be used:
* **StatusNotification.req** property ``errorCode`` will always be
``OtherError``
* **StatusNotification.req** property ``status`` will reflect the present status of the
charge point
* **StatusNotification.req** property ``info`` -> origin of EVerest error
* **StatusNotification.req** property ``vendorErrorCode`` -> EVerest error type and
subtype (the error type is simplified, meaning, that its leading part,
the interface name, is stripped)
* **StatusNotification.req** property ``vendorId`` -> EVerest error message
The reason for using the **StatusNotification.req** property property
``vendorId`` for the error message is that it can carry the largest
string (255 characters), whereas the other fields (``info`` and
``vendorErrorCode``) only allow up to 50 characters.
If for example the module with id `yeti_driver` within its
implementation with id `board_support` creates the following error:
.. code-block:: cpp
error_factory->create_error("evse_board_support/EnergyManagement",
"OutOfEnergy", "someone cut the wires")
the corresponding fields in the **StatusNotification.req** message will
look like this:
.. code-block:: JSON
{
"info": "yeti_driver->board_support",
"vendorErrorCode": "EnergyManagement/OutOfEnergy",
"vendorId": "someone cut the wires"
}
The **StatusNotification.req** message has some limitations with respect
to reporting errors:
* Single errors cannot simply be cleared. If multiple errors are raised,
it is not possible to clear individual errors.
* ``vendorId``, ``info`` and ``vendorErrorCode`` are limited in length
(see above).
This module attempts to follow 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 OCPP1.6.
This module currently deviates from the MREC specification in the following points:
* Simultaneous errors: MREC requires reporting simultaneous errors by reporting them in a single **StatusNotification.req** by separating
the information of the fields **vendorId** and **info** by a semicolon. This module sends one **StatusNotification.req** per individual error
because of the limited maximum characters of the **info** field.
* MREC requires always using the value **Faulted** for the **status** field when reporting an error. The OCPP1.6 specification defines the
**Faulted** value as follows: "When a Charge Point or connector has reported an error and is not available for energy delivery.
(Inoperative)." This module, therefore, only reports **Faulted** when the Charge Point is not available for energy delivery.
Energy Management and Smart Charging Integration
================================================
OCPP1.6 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 OCPP1.6 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
:doc:`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 `PublishChargingScheduleIntervalS` defines a periodic interval to retrieve
the composite schedule also in case no charging profiles have been changed. The configuration parameter `PublishChargingScheduleDurationS` defines
the duration in seconds of the requested composite schedules starting now. The value configured for `PublishChargingScheduleDurationS` shall be greater
than the value configured for `PublishChargingScheduleIntervalS` because otherwise time periods could be missed by the application.
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 **ISO15118CertificateManagementEnabled** 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 (or can be configured using the **OCSPRequestInterval** configuration key).
The timestamp of the last update is stored persistently, so that this process is not necessarily performed at every start up.

View File

@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef OCPP_ERROR_MAPPING_HPP
#define OCPP_ERROR_MAPPING_HPP
#include <unordered_map>
#include <ocpp/v16/ocpp_enums.hpp>
namespace module {
const std::string CHARGE_X_MREC_VENDOR_ID = "https://chargex.inl.gov";
// Error type mappings
const std::unordered_map<std::string, std::pair<ocpp::v16::ChargePointErrorCode, std::string>> MREC_ERROR_MAP = {
{"connector_lock/MREC1ConnectorLockFailure", {ocpp::v16::ChargePointErrorCode::ConnectorLockFailure, "CX001"}},
{"evse_board_support/MREC2GroundFailure", {ocpp::v16::ChargePointErrorCode::GroundFailure, "CX002"}},
{"evse_board_support/MREC3HighTemperature", {ocpp::v16::ChargePointErrorCode::HighTemperature, "CX003"}},
{"evse_board_support/MREC4OverCurrentFailure", {ocpp::v16::ChargePointErrorCode::OverCurrentFailure, "CX004"}},
{"evse_board_support/MREC5OverVoltage", {ocpp::v16::ChargePointErrorCode::OverVoltage, "CX005"}},
{"evse_board_support/MREC6UnderVoltage", {ocpp::v16::ChargePointErrorCode::UnderVoltage, "CX006"}},
{"evse_board_support/MREC8EmergencyStop", {ocpp::v16::ChargePointErrorCode::OtherError, "CX008"}},
{"evse_board_support/MREC10InvalidVehicleMode", {ocpp::v16::ChargePointErrorCode::OtherError, "CX010"}},
{"evse_board_support/MREC14PilotFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX014"}},
{"evse_board_support/MREC15PowerLoss", {ocpp::v16::ChargePointErrorCode::OtherError, "CX015"}},
{"evse_board_support/MREC17EVSEContactorFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX017"}},
{"evse_board_support/MREC18CableOverTempDerate", {ocpp::v16::ChargePointErrorCode::OtherError, "CX018"}},
{"evse_board_support/MREC19CableOverTempStop", {ocpp::v16::ChargePointErrorCode::OtherError, "CX019"}},
{"evse_board_support/MREC20PartialInsertion", {ocpp::v16::ChargePointErrorCode::OtherError, "CX020"}},
{"evse_board_support/MREC23ProximityFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX023"}},
{"evse_board_support/MREC24ConnectorVoltageHigh", {ocpp::v16::ChargePointErrorCode::OtherError, "CX024"}},
{"evse_board_support/MREC25BrokenLatch", {ocpp::v16::ChargePointErrorCode::OtherError, "CX025"}},
{"evse_board_support/MREC26CutCable", {ocpp::v16::ChargePointErrorCode::OtherError, "CX026"}},
{"evse_manager/MREC4OverCurrentFailure", {ocpp::v16::ChargePointErrorCode::OverCurrentFailure, "CX004"}},
{"ac_rcd/MREC2GroundFailure", {ocpp::v16::ChargePointErrorCode::GroundFailure, "CX002"}},
{"evse_manager/MREC22ResistanceFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX022"}},
{"evse_manager/MREC11CableCheckFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX011"}},
{"evse_manager/MREC5OverVoltage", {ocpp::v16::ChargePointErrorCode::OverVoltage, "CX005"}},
};
// TODO: add other ChargePointErrorCode mappings
const std::unordered_map<std::string, ocpp::v16::ChargePointErrorCode> OCPP_ERROR_MAP = {
{"powermeter/CommunicationFault", ocpp::v16::ChargePointErrorCode::PowerMeterFailure},
};
} // namespace module
#endif

View File

@@ -0,0 +1,141 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
#include "ocpp_1_6_charge_pointImpl.hpp"
namespace module {
namespace main {
void ocpp_1_6_charge_pointImpl::init() {
}
void ocpp_1_6_charge_pointImpl::ready() {
}
bool ocpp_1_6_charge_pointImpl::handle_stop() {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle stop command";
return false;
}
std::lock_guard<std::mutex> lock(this->m);
mod->charging_schedules_timer->stop();
bool success = mod->charge_point->stop();
if (success) {
this->mod->ocpp_stopped = true;
}
return success;
}
bool ocpp_1_6_charge_pointImpl::handle_restart() {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle restart command";
return false;
}
std::lock_guard<std::mutex> lock(this->m);
mod->charging_schedules_timer->interval(std::chrono::seconds(this->mod->config.PublishChargingScheduleIntervalS));
bool success = mod->charge_point->restart();
if (success) {
this->mod->ocpp_stopped = false;
}
return success;
}
types::ocpp::KeyValue to_everest(const ocpp::v16::KeyValue& key_value) {
types::ocpp::KeyValue _key_value;
_key_value.key = key_value.key.get();
_key_value.read_only = key_value.readonly;
if (key_value.value.has_value()) {
_key_value.value = key_value.value.value().get();
}
return _key_value;
}
types::ocpp::ConfigurationStatus to_everest(const ocpp::v16::ConfigurationStatus status) {
switch (status) {
case ocpp::v16::ConfigurationStatus::Accepted:
return types::ocpp::ConfigurationStatus::Accepted;
case ocpp::v16::ConfigurationStatus::Rejected:
return types::ocpp::ConfigurationStatus::Rejected;
case ocpp::v16::ConfigurationStatus::RebootRequired:
return types::ocpp::ConfigurationStatus::RebootRequired;
case ocpp::v16::ConfigurationStatus::NotSupported:
return types::ocpp::ConfigurationStatus::NotSupported;
default:
EVLOG_warning << "Could not convert to ConfigurationStatus";
return types::ocpp::ConfigurationStatus::Rejected;
}
}
types::ocpp::GetConfigurationResponse to_everest(const ocpp::v16::GetConfigurationResponse& response) {
types::ocpp::GetConfigurationResponse _response;
std::vector<types::ocpp::KeyValue> configuration_keys;
std::vector<std::string> unknown_keys;
if (response.configurationKey.has_value()) {
for (const auto& item : response.configurationKey.value()) {
configuration_keys.push_back(to_everest(item));
}
}
if (response.unknownKey.has_value()) {
for (const auto& item : response.unknownKey.value()) {
unknown_keys.push_back(item.get());
}
}
_response.configuration_keys = configuration_keys;
_response.unknown_keys = unknown_keys;
return _response;
}
types::ocpp::GetConfigurationResponse ocpp_1_6_charge_pointImpl::handle_get_configuration_key(Array& keys) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle get configuration key command";
types::ocpp::GetConfigurationResponse response;
for (const auto& key : keys) {
response.unknown_keys.push_back(key);
}
return response;
}
ocpp::v16::GetConfigurationRequest request;
std::vector<ocpp::CiString<50>> _keys;
for (const auto& key : keys) {
_keys.push_back(key);
}
request.key = _keys;
const auto response = this->mod->charge_point->get_configuration_key(request);
return to_everest(response);
}
types::ocpp::ConfigurationStatus ocpp_1_6_charge_pointImpl::handle_set_configuration_key(std::string& key,
std::string& value) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle set configuration key command";
return types::ocpp::ConfigurationStatus::Rejected;
}
const auto response = this->mod->charge_point->set_configuration_key(key, value);
return to_everest(response);
}
void ocpp_1_6_charge_pointImpl::handle_monitor_configuration_keys(Array& keys) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle monitor configuration keys command";
return;
}
for (const auto& key : keys) {
this->mod->charge_point->register_configuration_key_changed_callback(
key,
[this](const ocpp::v16::KeyValue key_value) { this->publish_configuration_key(to_everest(key_value)); });
}
}
void ocpp_1_6_charge_pointImpl::handle_security_event(std::string& type, std::string& info) {
this->mod->charge_point->on_security_event(type, info);
}
} // namespace main
} // namespace module

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef MAIN_OCPP_1_6_CHARGE_POINT_IMPL_HPP
#define MAIN_OCPP_1_6_CHARGE_POINT_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ocpp_1_6_charge_point/Implementation.hpp>
#include "../OCPP.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace main {
struct Conf {};
class ocpp_1_6_charge_pointImpl : public ocpp_1_6_charge_pointImplBase {
public:
ocpp_1_6_charge_pointImpl() = delete;
ocpp_1_6_charge_pointImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<OCPP>& mod, Conf& config) :
ocpp_1_6_charge_pointImplBase(ev, "main"), 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 bool handle_stop() override;
virtual bool handle_restart() override;
virtual types::ocpp::GetConfigurationResponse handle_get_configuration_key(Array& keys) override;
virtual types::ocpp::ConfigurationStatus handle_set_configuration_key(std::string& key,
std::string& value) override;
virtual void handle_monitor_configuration_keys(Array& keys) override;
virtual void handle_security_event(std::string& type, std::string& info) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<OCPP>& 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 m;
// 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 main
} // namespace module
#endif // MAIN_OCPP_1_6_CHARGE_POINT_IMPL_HPP

View File

@@ -0,0 +1,134 @@
description: A OCPP charge point / charging station module, currently targeting OCPP-J 1.6
config:
ChargePointConfigPath:
description: >-
Path to the ocpp configuration file. Libocpp defines a JSON schema for this file. Please refer to the documentation
of libocpp for more information about the configuration options.
type: string
default: ocpp-config.json
UserConfigPath:
description: >-
Path to the file of the OCPP user config. The user config is used as an overlay for the original config defined
by the ChargePointConfigPath. Any changes to configuration keys turned out internally or by the CSMS will be
written to the user config file.
type: string
default: user_config.json
DatabasePath:
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/ocpp_1_6_charge_point
EnableExternalWebsocketControl:
description: If true websocket can be disconnected and connected externally. This parameter is for debug and testing purposes.
type: boolean
default: false
PublishChargingScheduleIntervalS:
description:
Interval in seconds in which charging schedules received from OCPP
are be published over MQTT and signalled to connected modules. If the value
is set to 0, charging schedules are only published when changed by CSMS
type: integer
default: 30
PublishChargingScheduleDurationS:
description: Duration in seconds that defines the duration of the requested charging schedules starting from now
type: integer
default: 600
MessageLogPath:
description: Path to directory where logs of all OCPP messages are written to
type: string
default: /tmp/everest_ocpp_logs
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
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:
main:
description: This is a OCPP 1.6 charge point
interface: ocpp_1_6_charge_point
auth_validator:
description: Validates the provided auth token with OCPP
interface: auth_token_validator
auth_provider:
description: Provides auth tokens from OCPP
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:
charger_information:
interface: charger_information
min_connections: 0
max_connections: 1
evse_manager:
interface: evse_manager
min_connections: 1
max_connections: 128
evse_energy_sink:
interface: external_energy_limits
min_connections: 0
max_connections: 129
reservation:
interface: reservation
min_connections: 1
max_connections: 1
auth:
interface: auth
min_connections: 1
max_connections: 1
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
display_message:
interface: display_message
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:
- Kai-Uwe Hermann
- Piet Gömpel

View File

@@ -0,0 +1,337 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "ocppImpl.hpp"
#include "ocpp/v16/messages/ChangeAvailability.hpp"
#include <everest/conversions/ocpp/ocpp_conversions.hpp>
namespace module {
namespace ocpp_generic {
types::ocpp::KeyValue to_everest(const ocpp::v16::KeyValue& key_value) {
types::ocpp::KeyValue _key_value;
_key_value.key = key_value.key.get();
_key_value.read_only = key_value.readonly;
if (key_value.value.has_value()) {
_key_value.value = key_value.value.value().get();
}
return _key_value;
}
types::ocpp::ConfigurationStatus to_everest(const ocpp::v16::ConfigurationStatus status) {
switch (status) {
case ocpp::v16::ConfigurationStatus::Accepted:
return types::ocpp::ConfigurationStatus::Accepted;
case ocpp::v16::ConfigurationStatus::Rejected:
return types::ocpp::ConfigurationStatus::Rejected;
case ocpp::v16::ConfigurationStatus::RebootRequired:
return types::ocpp::ConfigurationStatus::RebootRequired;
case ocpp::v16::ConfigurationStatus::NotSupported:
return types::ocpp::ConfigurationStatus::NotSupported;
default:
EVLOG_warning << "Could not convert to ConfigurationStatus";
return types::ocpp::ConfigurationStatus::Rejected;
}
}
types::ocpp::GetConfigurationResponse to_everest(const ocpp::v16::GetConfigurationResponse& response) {
types::ocpp::GetConfigurationResponse _response;
std::vector<types::ocpp::KeyValue> configuration_keys;
std::vector<std::string> unknown_keys;
if (response.configurationKey.has_value()) {
for (const auto& item : response.configurationKey.value()) {
configuration_keys.push_back(to_everest(item));
}
}
if (response.unknownKey.has_value()) {
for (const auto& item : response.unknownKey.value()) {
unknown_keys.push_back(item.get());
}
}
_response.configuration_keys = configuration_keys;
_response.unknown_keys = unknown_keys;
return _response;
}
ocpp::v16::AvailabilityStatus to_ocpp(const types::ocpp::ChangeAvailabilityStatusEnumType& status) {
switch (status) {
case types::ocpp::ChangeAvailabilityStatusEnumType::Accepted:
return ocpp::v16::AvailabilityStatus::Accepted;
case types::ocpp::ChangeAvailabilityStatusEnumType::Rejected:
return ocpp::v16::AvailabilityStatus::Rejected;
case types::ocpp::ChangeAvailabilityStatusEnumType::Scheduled:
return ocpp::v16::AvailabilityStatus::Scheduled;
}
throw std::out_of_range("unknown ChangeAvailabilityStatusEnumType");
}
types::ocpp::ChangeAvailabilityStatusEnumType to_everest(const ocpp::v16::AvailabilityStatus& status) {
switch (status) {
case ocpp::v16::AvailabilityStatus::Accepted:
return types::ocpp::ChangeAvailabilityStatusEnumType::Accepted;
case ocpp::v16::AvailabilityStatus::Rejected:
return types::ocpp::ChangeAvailabilityStatusEnumType::Rejected;
case ocpp::v16::AvailabilityStatus::Scheduled:
return types::ocpp::ChangeAvailabilityStatusEnumType::Scheduled;
}
throw std::out_of_range("unknown AvailabilityStatus");
}
ocpp::v16::AvailabilityType to_ocpp(const types::ocpp::OperationalStatusEnumType& status) {
switch (status) {
case types::ocpp::OperationalStatusEnumType::Operative:
return ocpp::v16::AvailabilityType::Operative;
case types::ocpp::OperationalStatusEnumType::Inoperative:
return ocpp::v16::AvailabilityType::Inoperative;
}
throw std::out_of_range("unknown OperationalStatusEnumType");
}
types::ocpp::ChangeAvailabilityResponse to_everest(const ocpp::v16::ChangeAvailabilityResponse& response) {
types::ocpp::ChangeAvailabilityResponse everest_response{};
everest_response.status = to_everest(response.status);
return everest_response;
}
void ocppImpl::init() {
}
void ocppImpl::ready() {
}
bool ocppImpl::handle_stop() {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle stop command";
return false;
}
std::lock_guard<std::mutex> lock(this->chargepoint_state_mutex);
mod->charging_schedules_timer->stop();
bool success = mod->charge_point->stop();
if (success) {
this->mod->ocpp_stopped = true;
}
return success;
}
bool ocppImpl::handle_restart() {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle restart command";
return false;
}
std::lock_guard<std::mutex> lock(this->chargepoint_state_mutex);
mod->charging_schedules_timer->interval(std::chrono::seconds(this->mod->config.PublishChargingScheduleIntervalS));
bool success = mod->charge_point->restart();
if (success) {
this->mod->ocpp_stopped = false;
}
return success;
}
void ocppImpl::handle_security_event(types::ocpp::SecurityEvent& event) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle security event command";
return;
}
std::optional<ocpp::DateTime> timestamp;
if (event.timestamp.has_value()) {
timestamp = ocpp_conversions::to_ocpp_datetime_or_now(event.timestamp.value());
}
const auto event_type = ocpp::CiString<50>(event.type, ocpp::StringTooLarge::Truncate);
std::optional<ocpp::CiString<255>> tech_info;
if (event.info.has_value()) {
tech_info = ocpp::CiString<255>(event.info.value(), ocpp::StringTooLarge::Truncate);
}
this->mod->charge_point->on_security_event(event_type, tech_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;
}
std::vector<types::ocpp::GetVariableResult> results;
// prepare ocpp_request to request configuration keys from ocpp::v16::ChargePoint
ocpp::v16::GetConfigurationRequest ocpp_request;
std::vector<ocpp::CiString<50>> configuration_keys;
std::map<std::string, int> configuration_key_indices{};
int request_index = 0;
for (const auto& request : requests) {
// only variable.name is relevant for OCPP1.6
configuration_keys.emplace_back(request.component_variable.variable.name);
configuration_key_indices[request.component_variable.variable.name] = request_index++;
}
ocpp_request.key = configuration_keys;
// request configuration keys from ocpp::v16::ChargePoint
const auto ocpp_response = this->mod->charge_point->get_configuration_key(ocpp_request);
std::map<std::string, types::ocpp::GetVariableRequest> results_map{};
if (ocpp_response.configurationKey.has_value()) {
for (const auto& key_value : ocpp_response.configurationKey.value()) {
// add result for each present configurationKey in the response
types::ocpp::GetVariableResult result;
result.component_variable = {
{""},
{key_value.key.get()}}; // we don't care about the component, only about the variable.name in OCPP1.6
if (key_value.value.has_value()) {
result.value = key_value.value.value().get();
result.status = types::ocpp::GetVariableStatusEnumType::Accepted;
result.attribute_type = types::ocpp::AttributeEnum::Actual;
} else {
result.status = types::ocpp::GetVariableStatusEnumType::UnknownVariable;
}
results.push_back(result);
}
}
if (ocpp_response.unknownKey.has_value()) {
for (const auto& key : ocpp_response.unknownKey.value()) {
// add result for each unknownKey in the response
types::ocpp::GetVariableResult result;
result.component_variable = {
{""}, {key.get()}}; // we don't care about the component, only about the variable.name in OCPP1.6
result.status = types::ocpp::GetVariableStatusEnumType::UnknownVariable;
results.push_back(result);
}
}
std::sort(results.begin(), results.end(),
[&configuration_key_indices](const types::ocpp::GetVariableResult& result_a,
const types::ocpp::GetVariableResult& result_b) {
return configuration_key_indices[result_a.component_variable.variable.name] <
configuration_key_indices[result_b.component_variable.variable.name];
});
return results;
}
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;
}
std::vector<types::ocpp::SetVariableResult> results;
for (const auto& request : requests) {
// add result for each present SetVariableRequest in the response
types::ocpp::SetVariableResult result;
result.component_variable = request.component_variable;
// retrieve key and value from the request
auto key = request.component_variable.variable.name;
auto value = request.value;
auto response = this->mod->charge_point->set_configuration_key(key, value);
switch (response) {
case ocpp::v16::ConfigurationStatus::Accepted:
result.status = types::ocpp::SetVariableStatusEnumType::Accepted;
break;
case ocpp::v16::ConfigurationStatus::RebootRequired:
result.status = types::ocpp::SetVariableStatusEnumType::RebootRequired;
break;
case ocpp::v16::ConfigurationStatus::Rejected:
result.status = types::ocpp::SetVariableStatusEnumType::Rejected;
break;
case ocpp::v16::ConfigurationStatus::NotSupported:
// NotSupported in OCPP1.6 means that the configuration key is not known / not supported, so it's best to go
// with UnknownVariable
result.status = types::ocpp::SetVariableStatusEnumType::UnknownVariable;
break;
}
results.push_back(result);
}
return results;
}
void ocppImpl::handle_monitor_variables(std::vector<types::ocpp::ComponentVariable>& component_variables) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "Could not monitor variables. ChargePoint not initialized";
return;
}
for (const auto& cv : component_variables) {
this->mod->charge_point->register_configuration_key_changed_callback(
cv.variable.name, // we dont care about the component, only about the variable.name in OCPP1.6
[this, cv](const ocpp::v16::KeyValue key_value) {
types::ocpp::EventData event_data;
event_data.component_variable = cv;
event_data.event_id = 0; // irrelevant for OCPP1.6
event_data.timestamp = ocpp::DateTime();
event_data.trigger = types::ocpp::EventTriggerEnum::Alerting; // default for OCPP1.6
event_data.actual_value = key_value.value.value_or("");
event_data.event_notification_type =
types::ocpp::EventNotificationType::CustomMonitor; // default for OCPP1.6
this->publish_event_data(event_data);
});
}
}
types::ocpp::ChangeAvailabilityResponse
ocppImpl::handle_change_availability(types::ocpp::ChangeAvailabilityRequest& request) {
if (this->mod->charge_point == nullptr) {
EVLOG_warning << "ChargePoint not initialized, cannot handle change availability command";
return types::ocpp::ChangeAvailabilityResponse{types::ocpp::ChangeAvailabilityStatusEnumType::Rejected};
}
ocpp::v16::ChangeAvailabilityRequest ocpp_request{};
ocpp_request.type = to_ocpp(request.operational_status);
if (request.evse.has_value()) {
const auto& evse = request.evse.value();
if (!evse.connector_id.has_value()) {
return types::ocpp::ChangeAvailabilityResponse{
types::ocpp::ChangeAvailabilityStatusEnumType::Rejected,
types::ocpp::StatusInfoType{"InvalidInput",
"No connector id specified; if the whole charging station is supposed to "
"be addressed, parameter evse "
"must have no value."}};
}
try {
ocpp_request.connectorId = this->mod->get_ocpp_connector_id(evse.id, evse.connector_id.value());
} catch (const std::out_of_range&) {
return types::ocpp::ChangeAvailabilityResponse{
types::ocpp::ChangeAvailabilityStatusEnumType::Rejected,
types::ocpp::StatusInfoType{
"InvalidInput",
"Could not determine OCPP connector id from provided EVerest EVSE and Connector Ids."}};
}
}
auto response = this->mod->charge_point->on_change_availability(ocpp_request);
return to_everest(response);
}
} // namespace ocpp_generic
} // namespace module

View File

@@ -0,0 +1,71 @@
// 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 "../OCPP.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// 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<OCPP>& mod, Conf& config) :
ocppImplBase(ev, "ocpp_generic"), 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 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
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<OCPP>& 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
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace ocpp_generic
} // namespace module
#endif // OCPP_GENERIC_OCPP_IMPL_HPP

View File

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

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef SESSION_COST_SESSION_COST_IMPL_HPP
#define SESSION_COST_SESSION_COST_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/session_cost/Implementation.hpp>
#include "../OCPP.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<OCPP>& 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<OCPP>& 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