diff --git a/doc/everest_api_specs/auth_token_validator_API/asyncapi.yaml b/doc/everest_api_specs/auth_token_validator_API/asyncapi.yaml index 5e09c83337..1ce394f534 100644 --- a/doc/everest_api_specs/auth_token_validator_API/asyncapi.yaml +++ b/doc/everest_api_specs/auth_token_validator_API/asyncapi.yaml @@ -3,7 +3,7 @@ asyncapi: 3.0.0 id: 'pionix:de:everest:auth_token_validator_API' info: title: 'EVerest API definition for auth token validator - CLIENT side (MODULE)' - version: 1.0.0 + version: 1.0.1 description: >- API for EVerest API clients implementing auth token validator. @@ -206,7 +206,7 @@ components: EnergyTransferMode: description: >- Possible energy transfer modes. The modes AC_single_phase_core to DC_unique apply to DIN70121 and ISO15118-2. - The other modes DC to WPT apply to ISO15118-20. + The other modes DC to MCS_BPT apply to ISO15118-20 (and amendment). type: string enum: - AC_single_phase_core @@ -224,6 +224,8 @@ components: - DC_ACDP - DC_ACDP_BPT - WPT + - MCS + - MCS_BPT ValidationResult: description: >- Result object containing authorization status enum value and an optional diff --git a/doc/everest_api_specs/evse_manager_consumer_API/asyncapi.yaml b/doc/everest_api_specs/evse_manager_consumer_API/asyncapi.yaml index c8b2712722..63c5dc4dd2 100644 --- a/doc/everest_api_specs/evse_manager_consumer_API/asyncapi.yaml +++ b/doc/everest_api_specs/evse_manager_consumer_API/asyncapi.yaml @@ -3,7 +3,7 @@ asyncapi: 3.0.0 id: 'pionix:de:everest:evse_manager_consumer_API' info: title: 'EVerest API definition for the use of the EVSE manager' - version: 1.0.0 + version: 1.0.1 description: >- API to allow EVerest API clients to use the EvseManager module. diff --git a/lib/everest/everest_api_types/include/everest_api_types/evse_manager/API.hpp b/lib/everest/everest_api_types/include/everest_api_types/evse_manager/API.hpp index d76c4beecd..eda39ea15f 100644 --- a/lib/everest/everest_api_types/include/everest_api_types/evse_manager/API.hpp +++ b/lib/everest/everest_api_types/include/everest_api_types/evse_manager/API.hpp @@ -174,6 +174,7 @@ enum class ConnectorTypeEnum { cCCS1, cCCS2, cG105, + cMCS, cTesla, cType1, cType2, diff --git a/lib/everest/everest_api_types/include/everest_api_types/iso15118_charger/API.hpp b/lib/everest/everest_api_types/include/everest_api_types/iso15118_charger/API.hpp index 0cf8c60bb0..1203098e87 100644 --- a/lib/everest/everest_api_types/include/everest_api_types/iso15118_charger/API.hpp +++ b/lib/everest/everest_api_types/include/everest_api_types/iso15118_charger/API.hpp @@ -30,6 +30,8 @@ enum class EnergyTransferMode { DC_ACDP, DC_ACDP_BPT, WPT, + MCS, + MCS_BPT, }; enum class Status { diff --git a/lib/everest/everest_api_types/src/everest_api_types/evse_manager/json_codec.cpp b/lib/everest/everest_api_types/src/everest_api_types/evse_manager/json_codec.cpp index e221934cde..4083d8088d 100644 --- a/lib/everest/everest_api_types/src/everest_api_types/evse_manager/json_codec.cpp +++ b/lib/everest/everest_api_types/src/everest_api_types/evse_manager/json_codec.cpp @@ -864,6 +864,9 @@ void to_json(json& j, ConnectorTypeEnum const& k) noexcept { case ConnectorTypeEnum::cG105: j = "cG105"; return; + case ConnectorTypeEnum::cMCS: + j = "cMCS"; + return; case ConnectorTypeEnum::cTesla: j = "cTesla"; return; @@ -939,6 +942,10 @@ void from_json(json const& j, ConnectorTypeEnum& k) { k = ConnectorTypeEnum::cG105; return; } + if (s == "cMCS") { + k = ConnectorTypeEnum::cMCS; + return; + } if (s == "cTesla") { k = ConnectorTypeEnum::cTesla; return; diff --git a/lib/everest/everest_api_types/src/everest_api_types/evse_manager/wrapper.cpp b/lib/everest/everest_api_types/src/everest_api_types/evse_manager/wrapper.cpp index b2b1e38681..d19bbc1f2c 100644 --- a/lib/everest/everest_api_types/src/everest_api_types/evse_manager/wrapper.cpp +++ b/lib/everest/everest_api_types/src/everest_api_types/evse_manager/wrapper.cpp @@ -530,6 +530,8 @@ ConnectorTypeEnum_Internal to_internal_api(ConnectorTypeEnum_External const& val return TarT::cCCS2; case SrcT::cG105: return TarT::cG105; + case SrcT::cMCS: + return TarT::cMCS; case SrcT::cTesla: return TarT::cTesla; case SrcT::cType1: @@ -583,6 +585,8 @@ ConnectorTypeEnum_External to_external_api(ConnectorTypeEnum_Internal const& val return TarT::cCCS2; case SrcT::cG105: return TarT::cG105; + case SrcT::cMCS: + return TarT::cMCS; case SrcT::cTesla: return TarT::cTesla; case SrcT::cType1: diff --git a/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/json_codec.cpp b/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/json_codec.cpp index 331356659e..a0aab9e299 100644 --- a/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/json_codec.cpp +++ b/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/json_codec.cpp @@ -100,6 +100,14 @@ void from_json(json const& j, EnergyTransferMode& k) { k = EnergyTransferMode::WPT; return; } + if (s == "MCS") { + k = EnergyTransferMode::MCS; + return; + } + if (s == "MCS_BPT") { + k = EnergyTransferMode::MCS_BPT; + return; + } throw std::out_of_range( "Provided string " + s + @@ -153,6 +161,12 @@ void to_json(json& j, EnergyTransferMode const& k) noexcept { case EnergyTransferMode::WPT: j = "WPT"; return; + case EnergyTransferMode::MCS: + j = "MCS"; + return; + case EnergyTransferMode::MCS_BPT: + j = "MCS_BPT"; + return; } j = "INVALID_VALUE__everest::lib::API::V1_0::types::iso15118_charger::EnergyTransferMode"; } diff --git a/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/wrapper.cpp b/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/wrapper.cpp index 42a6cce4a6..3f086d6a22 100644 --- a/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/wrapper.cpp +++ b/lib/everest/everest_api_types/src/everest_api_types/iso15118_charger/wrapper.cpp @@ -111,6 +111,10 @@ EnergyTransferMode_Internal to_internal_api(EnergyTransferMode_External const& v return TarT::DC_ACDP_BPT; case SrcT::WPT: return TarT::WPT; + case SrcT::MCS: + return TarT::MCS; + case SrcT::MCS_BPT: + return TarT::MCS_BPT; } throw std::out_of_range("Unexpected value for EnergyTransferMode_External"); @@ -151,6 +155,10 @@ EnergyTransferMode_External to_external_api(EnergyTransferMode_Internal const& v return TarT::DC_ACDP_BPT; case SrcT::WPT: return TarT::WPT; + case SrcT::MCS: + return TarT::MCS; + case SrcT::MCS_BPT: + return TarT::MCS_BPT; } throw std::out_of_range("Unexpected value for EnergyTransferMode_Internal"); diff --git a/lib/everest/everest_api_types/tests/expected_types_file_hashes.csv b/lib/everest/everest_api_types/tests/expected_types_file_hashes.csv index b20ae6c1b5..c4699360d4 100644 --- a/lib/everest/everest_api_types/tests/expected_types_file_hashes.csv +++ b/lib/everest/everest_api_types/tests/expected_types_file_hashes.csv @@ -8,9 +8,9 @@ types/energy.yaml,82b8d902618eb3927a7e34e8a641cb2a69db93e404c2b61320c62eab3c290d types/energy_price_information.yaml,b2284d75a2fc695150cb72e7262b4efa11a05b11b1cca382451643a9bb2be624 types/error_history.yaml,6c49548f8020e807ca17f710d6cb69e53399b9da4ed2aaa5247dd695f42c3b50 types/evse_board_support.yaml,cbbe41414102d45b4d24db661b4da2bbaef544c4f050db0981ba6e44946a3f79 -types/evse_manager.yaml,28582fa5814c9d98993e1698cbf18111b685081ace83197efc6d791042692a40 +types/evse_manager.yaml,1239ef6fdc57cbec1f6788003c9c72bd80b899ca3a544cef03a3cf1a674b01d3 types/evse_security.yaml,a80c92d4663bda33412bd32788ba446bc0eafeebcddad706db38e1f988bd133b -types/iso15118.yaml,c4885e4caac846034f7719b2900ce6c909039c7cbc82a1166a2a1371a32a0b03 +types/iso15118.yaml,b05ccce77acf2b25774f9d34317f241cec786f02636cb943b122277160d6df08 types/isolation_monitor.yaml,45d98b5072fa5d02a476860fe7d45a7b02f8a05eb6be94327f32dbe159d4ec40 types/money.yaml,4a7001c50216fcf96caac4fe16db17e23ce5149d000be1de4ac1f0135f5bdb0b types/ocpp.yaml,2181f239b0366de85189dbe6cc210ad6b3ab7b6bdeb9ea8aad20fe4410b93bd7 diff --git a/modules/EVSE/Evse15118D20/charger/ISO15118_chargerImpl.cpp b/modules/EVSE/Evse15118D20/charger/ISO15118_chargerImpl.cpp index 6c37981e9b..2da75da80b 100644 --- a/modules/EVSE/Evse15118D20/charger/ISO15118_chargerImpl.cpp +++ b/modules/EVSE/Evse15118D20/charger/ISO15118_chargerImpl.cpp @@ -105,6 +105,10 @@ types::iso15118::EnergyTransferMode get_energy_transfer_mode(const dt::ServiceCa requested_energy_transfer = EnergyTransferMode::DC_BPT; } else if (service_category == dt::ServiceCategory::DC_ACDP_BPT) { requested_energy_transfer = EnergyTransferMode::DC_ACDP_BPT; + } else if (service_category == dt::ServiceCategory::MCS) { + requested_energy_transfer = EnergyTransferMode::MCS; + } else if (service_category == dt::ServiceCategory::MCS_BPT) { + requested_energy_transfer = EnergyTransferMode::MCS_BPT; } return requested_energy_transfer; @@ -856,6 +860,12 @@ void ISO15118_chargerImpl::handle_update_energy_transfer_modes( case types::iso15118::EnergyTransferMode::WPT: services.push_back(dt::ServiceCategory::WPT); break; + case types::iso15118::EnergyTransferMode::MCS: + services.push_back(dt::ServiceCategory::MCS); + break; + case types::iso15118::EnergyTransferMode::MCS_BPT: + services.push_back(dt::ServiceCategory::MCS_BPT); + break; } } diff --git a/modules/EVSE/EvseManager/EvseManager.cpp b/modules/EVSE/EvseManager/EvseManager.cpp index 3cbc34808c..6935b414f0 100644 --- a/modules/EVSE/EvseManager/EvseManager.cpp +++ b/modules/EVSE/EvseManager/EvseManager.cpp @@ -56,6 +56,14 @@ bool almost_eq(double a, double b) { void EvseManager::init() { + if (!config.connector_type.empty()) { + try { + connector_type = types::evse_manager::string_to_connector_type_enum(config.connector_type); + } catch (const std::out_of_range& e) { + EVLOG_warning << "Unknown/invalid connector type: " << config.connector_type; + } + } + store = std::unique_ptr(new PersistentStore(r_store, info.id)); random_delay_enabled = config.uk_smartcharging_random_delay_enable; @@ -129,10 +137,20 @@ void EvseManager::init() { // subscribe to run time updates for real initial values (and changes e.g. due to de-rating) r_powersupply_DC[0]->subscribe_capabilities([this](const auto& caps) { update_powersupply_capabilities(caps); - const bool dc_was_updated = update_supported_energy_transfers(types::iso15118::EnergyTransferMode::DC); + + auto mode = types::iso15118::EnergyTransferMode::DC; + auto bpt_mode = types::iso15118::EnergyTransferMode::DC_BPT; + + if (connector_type.has_value() and + connector_type.value() == types::evse_manager::ConnectorTypeEnum::cMCS) { + mode = types::iso15118::EnergyTransferMode::MCS; + bpt_mode = types::iso15118::EnergyTransferMode::MCS_BPT; + } + + const bool dc_was_updated = update_supported_energy_transfers(mode); const bool dc_bpt_was_updated = - caps.bidirectional ? update_supported_energy_transfers(types::iso15118::EnergyTransferMode::DC_BPT) - : false; + caps.bidirectional ? update_supported_energy_transfers(bpt_mode) : false; + if (dc_was_updated || dc_bpt_was_updated) { this->p_evse->publish_supported_energy_transfer_modes(supported_energy_transfers); } diff --git a/modules/EVSE/EvseManager/EvseManager.hpp b/modules/EVSE/EvseManager/EvseManager.hpp index 7d4cbe38dc..b63f24ecd7 100644 --- a/modules/EVSE/EvseManager/EvseManager.hpp +++ b/modules/EVSE/EvseManager/EvseManager.hpp @@ -199,6 +199,8 @@ class EvseManager : public Everest::ModuleBase { void cancel_reservation(bool signal_event); bool is_reserved(); + std::optional connector_type; + /// /// \brief Reserve this evse. /// \param id The reservation id. diff --git a/modules/EVSE/EvseManager/evse/evse_managerImpl.cpp b/modules/EVSE/EvseManager/evse/evse_managerImpl.cpp index ebd4a38307..0cc7817f52 100644 --- a/modules/EVSE/EvseManager/evse/evse_managerImpl.cpp +++ b/modules/EVSE/EvseManager/evse/evse_managerImpl.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Pionix GmbH and Contributors to EVerest #include "evse_managerImpl.hpp" +#include #include #include @@ -323,13 +324,7 @@ types::evse_manager::Evse evse_managerImpl::handle_get_evse() { types::evse_manager::Connector connector; // EvseManager currently only supports a single connector with id: 1; connector.id = 1; - if (!this->mod->config.connector_type.empty()) { - try { - connector.type = types::evse_manager::string_to_connector_type_enum(this->mod->config.connector_type); - } catch (const std::out_of_range& e) { - EVLOG_warning << "Evse with id " << evse.id << ": connector type invalid: " << e.what(); - } - } + connector.type = mod->connector_type; connectors.push_back(connector); evse.connectors = connectors; @@ -446,12 +441,41 @@ void evse_managerImpl::handle_set_plug_and_charge_configuration( types::evse_manager::UpdateAllowedEnergyTransferModesResult evse_managerImpl::handle_update_allowed_energy_transfer_modes( std::vector& allowed_energy_transfer_modes) { - // TODO(mlitre): Add check for incompatible type - if (!mod->r_hlc.empty() && mod->r_hlc[0]) { - mod->r_hlc[0]->call_update_energy_transfer_modes(allowed_energy_transfer_modes); - return types::evse_manager::UpdateAllowedEnergyTransferModesResult::Accepted; + std::vector filtered_energy_transfer_modes; + + if (mod->r_hlc.empty() or !mod->r_hlc[0]) { + return types::evse_manager::UpdateAllowedEnergyTransferModesResult::NoHlc; } - return types::evse_manager::UpdateAllowedEnergyTransferModesResult::NoHlc; + + filtered_energy_transfer_modes.reserve(allowed_energy_transfer_modes.size()); + + // TODO(mlitre): Add check for incompatible type(s), for now we just filter out + // MCS related stuff and only if a connector type was configured at all; + // also TODO: for DC we can check whether BPT can be supported in case DC supply supports it + std::copy_if(allowed_energy_transfer_modes.begin(), allowed_energy_transfer_modes.end(), + std::back_inserter(filtered_energy_transfer_modes), [&](types::iso15118::EnergyTransferMode m) { + if (!mod->connector_type.has_value()) { + return true; + } + + // for MCS we only allow MCS types + if (mod->connector_type == types::evse_manager::ConnectorTypeEnum::cMCS) { + return m == types::iso15118::EnergyTransferMode::MCS or + m == types::iso15118::EnergyTransferMode::MCS_BPT; + } + + // for everything else, we disallow MCS + return m != types::iso15118::EnergyTransferMode::MCS and + m != types::iso15118::EnergyTransferMode::MCS_BPT; + }); + + // check whether at least one mode has survived our filtering + if (!filtered_energy_transfer_modes.size()) { + return types::evse_manager::UpdateAllowedEnergyTransferModesResult::IncompatibleEnergyTransfer; + } + + mod->r_hlc[0]->call_update_energy_transfer_modes(filtered_energy_transfer_modes); + return types::evse_manager::UpdateAllowedEnergyTransferModesResult::Accepted; } } // namespace evse diff --git a/modules/EVSE/OCPP201/conversions.cpp b/modules/EVSE/OCPP201/conversions.cpp index 71020e9b7a..c81abc5f24 100644 --- a/modules/EVSE/OCPP201/conversions.cpp +++ b/modules/EVSE/OCPP201/conversions.cpp @@ -837,6 +837,12 @@ ocpp::v2::EnergyTransferModeEnum to_ocpp_energy_transfer_mode(const types::iso15 return ocpp::v2::EnergyTransferModeEnum::DC_ACDP_BPT; case types::iso15118::EnergyTransferMode::WPT: return ocpp::v2::EnergyTransferModeEnum::WPT; + + // revisit: OCPP does not yet know about MCS + case types::iso15118::EnergyTransferMode::MCS: + return ocpp::v2::EnergyTransferModeEnum::DC; + case types::iso15118::EnergyTransferMode::MCS_BPT: + return ocpp::v2::EnergyTransferModeEnum::DC_BPT; } throw std::out_of_range("Could not convert EnergyTransferMode"); @@ -1810,6 +1816,14 @@ std::vector to_everest_allowed_energy_trans value.reserve(allowed_energy_transfer_modes.size()); for (const auto& mode : allowed_energy_transfer_modes) { value.push_back(to_everest_allowed_energy_transfer_mode(mode)); + // revisit: at the moment, OCPP does not yet know about MCS types + // so in case of DC we allow also the corresponding MCS modes + if (mode == ocpp::v2::EnergyTransferModeEnum::DC) { + value.push_back(types::iso15118::EnergyTransferMode::MCS); + } + if (mode == ocpp::v2::EnergyTransferModeEnum::DC_BPT) { + value.push_back(types::iso15118::EnergyTransferMode::MCS_BPT); + } } return value; } diff --git a/types/evse_manager.yaml b/types/evse_manager.yaml index 57297e9c60..c8c290d07a 100644 --- a/types/evse_manager.yaml +++ b/types/evse_manager.yaml @@ -497,6 +497,7 @@ types: cCCS1: Combined Charging System 1 a.k.a. Combo 1 cCCS2: Combined Charging System 2 a.k.a. Combo 2 cG105: JARI G105-1993 a.k.a. CHAdeMO + cMCS: Megawatt Charging System (captive cabled) (IEC 63379 Configuration HH) cTesla: Tesla Connector cType1: IEC62196-2 Type 1 connector a.k.a. J1772 cType2: IEC62196-2 Type 2 connector a.k.a. Mennekes connector @@ -522,6 +523,7 @@ types: - cCCS1 - cCCS2 - cG105 + - cMCS - cTesla - cType1 - cType2 diff --git a/types/iso15118.yaml b/types/iso15118.yaml index 7160eb1fb0..4a7669c3f1 100644 --- a/types/iso15118.yaml +++ b/types/iso15118.yaml @@ -27,6 +27,8 @@ types: - DC_ACDP - DC_ACDP_BPT - WPT + - MCS + - MCS_BPT IsolationStatus: description: The different charger isolation status type: string