From e9c7992742b802e6dd189e7b91339f02f2e48200 Mon Sep 17 00:00:00 2001 From: Sebastian Lukas Date: Tue, 1 Oct 2024 15:49:09 +0200 Subject: [PATCH] Adding D20 dynamic mode support D20 dynamic mode works now for IsoMux Signed-off-by: Sebastian Lukas --- dependencies.yaml | 4 +- interfaces/ISO15118_charger.yaml | 11 +++ modules/Evse15118D20/Evse15118D20.hpp | 3 + .../charger/ISO15118_chargerImpl.cpp | 74 ++++++++++++++++++- modules/Evse15118D20/manifest.yaml | 14 ++++ modules/EvseManager/EvseManager.cpp | 42 +++++++++++ .../IsoMux/charger/ISO15118_chargerImpl.cpp | 30 ++++++++ types/iso15118_charger.yaml | 74 +++++++++++++++++++ 8 files changed, 247 insertions(+), 5 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 57022a74c..9b0a5e28e 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -46,7 +46,7 @@ libfsm: # libiso15118 libiso15118: git: https://github.com/EVerest/libiso15118.git - git_tag: v0.2.0 + git_tag: a135d7d3cc62a2206b1128b267a388e209b3cd2c cmake_condition: "EVEREST_DEPENDENCY_ENABLED_LIBISO15118" # LEM DCBM 400/600 module @@ -71,7 +71,7 @@ libocpp: # Josev Josev: git: https://github.com/EVerest/ext-switchev-iso15118.git - git_tag: 2024.9.0 + git_tag: 4abfad56331332d56a87e40cc361744f87e65c39 cmake_condition: "EVEREST_ENABLE_PY_SUPPORT AND EVEREST_DEPENDENCY_ENABLED_JOSEV" # libcbv2g libcbv2g: diff --git a/interfaces/ISO15118_charger.yaml b/interfaces/ISO15118_charger.yaml index 0a8789a51..a322aa6e2 100644 --- a/interfaces/ISO15118_charger.yaml +++ b/interfaces/ISO15118_charger.yaml @@ -351,3 +351,14 @@ vars: Parameters that may be displayed on the EVSE (Soc, battery capacity) type: object $ref: /iso15118_charger#/DisplayParameters + d20_dc_dynamic_charge_mode: + description: >- + The parameters the EVCC offers and sets for dynamic control mode + type: object + $ref: /iso15118_charger#/DcChargeDynamicModeValues + dc_ev_present_voltage: + description: Present Voltage measured from the EV + type: number + meter_info_requested: + description: The EV requested meter infos from the EVSE + type: "null" diff --git a/modules/Evse15118D20/Evse15118D20.hpp b/modules/Evse15118D20/Evse15118D20.hpp index c8ea4ce55..7d9bd1360 100644 --- a/modules/Evse15118D20/Evse15118D20.hpp +++ b/modules/Evse15118D20/Evse15118D20.hpp @@ -28,6 +28,9 @@ struct Conf { bool enable_ssl_logging; bool enable_tls_key_logging; bool enable_sdp_server; + bool supported_dynamic_mode; + bool supported_mobility_needs_mode_provided_by_secc; + bool supported_scheduled_mode; }; class Evse15118D20 : public Everest::ModuleBase { diff --git a/modules/Evse15118D20/charger/ISO15118_chargerImpl.cpp b/modules/Evse15118D20/charger/ISO15118_chargerImpl.cpp index f1f400b68..3abefa6c0 100644 --- a/modules/Evse15118D20/charger/ISO15118_chargerImpl.cpp +++ b/modules/Evse15118D20/charger/ISO15118_chargerImpl.cpp @@ -56,6 +56,30 @@ convert_display_parameters(const iso15118::session::feedback::DisplayParameters& in.inlet_hot}; } +types::iso15118_charger::DcChargeDynamicModeValues +convert_dynamic_values(const iso15118::session::feedback::DcChargeDynamicMode& in) { + + std::optional departure_time{std::nullopt}; + if (in.departure_time.has_value()) { + departure_time = static_cast(*in.departure_time); + } + + return {in.target_energy_request, + in.max_energy_request, + in.min_energy_request, + in.max_charge_power, + in.min_charge_power, + in.max_charge_current, + in.max_voltage, + in.min_voltage, + departure_time, + in.max_discharge_power, + in.min_discharge_power, + in.max_discharge_current, + in.max_v2x_energy_request, + in.min_v2x_energy_request}; +} + } // namespace void ISO15118_chargerImpl::init() { @@ -112,17 +136,52 @@ void ISO15118_chargerImpl::ready() { }; const auto callbacks = create_callbacks(); + if (mod->config.supported_dynamic_mode) { + setup_config.control_mobility_modes.push_back( + {iso15118::message_20::ControlMode::Dynamic, iso15118::message_20::MobilityNeedsMode::ProvidedByEvcc}); + if (mod->config.supported_mobility_needs_mode_provided_by_secc) { + setup_config.control_mobility_modes.push_back( + {iso15118::message_20::ControlMode::Dynamic, iso15118::message_20::MobilityNeedsMode::ProvidedBySecc}); + } + } + + if (mod->config.supported_scheduled_mode) { + setup_config.control_mobility_modes.push_back( + {iso15118::message_20::ControlMode::Scheduled, iso15118::message_20::MobilityNeedsMode::ProvidedByEvcc}); + } + + if (setup_config.control_mobility_modes.empty()) { + EVLOG_warning << "Control mobility modes are empty! Setting dynamic mode as default!"; + setup_config.control_mobility_modes.push_back( + {iso15118::message_20::ControlMode::Dynamic, iso15118::message_20::MobilityNeedsMode::ProvidedByEvcc}); + } + controller = std::make_unique(tbd_config, callbacks, setup_config); - controller->loop(); + + try { + controller->loop(); + } catch (const std::exception& e) { + EVLOG_error << e.what(); + } } iso15118::session::feedback::Callbacks ISO15118_chargerImpl::create_callbacks() { iso15118::session::feedback::Callbacks callbacks; - callbacks.dc_charge_target = [this](const iso15118::session::feedback::DcChargeTarget& charge_target) { - publish_dc_ev_target_voltage_current({charge_target.voltage, charge_target.current}); + callbacks.dc_pre_charge_target_voltage = [this](float target_voltage) { + publish_dc_ev_target_voltage_current({target_voltage, 0}); + }; + + callbacks.dc_charge_scheduled_mode = [this](const iso15118::session::feedback::DcChargeScheduledMode& + dc_charge_schedule) { + publish_dc_ev_target_voltage_current({dc_charge_schedule.target_voltage, dc_charge_schedule.target_current}); }; + callbacks.dc_charge_dynamic_mode = + [this](const iso15118::session::feedback::DcChargeDynamicMode& dc_charge_dynamic) { + publish_d20_dc_dynamic_charge_mode(convert_dynamic_values(dc_charge_dynamic)); + }; + callbacks.dc_max_limits = [this](const iso15118::session::feedback::DcMaximumLimits& max_limits) { publish_dc_ev_maximum_limits({max_limits.current, max_limits.power, max_limits.voltage}); }; @@ -173,6 +232,15 @@ iso15118::session::feedback::Callbacks ISO15118_chargerImpl::create_callbacks() publish_display_parameters(convert_display_parameters(parameters)); }; + callbacks.dc_present_voltage = [this](float present_voltage) { publish_dc_ev_present_voltage(present_voltage); }; + + callbacks.meter_info_requested = [this](bool meter_info_requested) { + if (meter_info_requested) { + EVLOG_info << "Meter info is requested from EV"; + publish_meter_info_requested(nullptr); + } + }; + return callbacks; } diff --git a/modules/Evse15118D20/manifest.yaml b/modules/Evse15118D20/manifest.yaml index e13f8203a..18834a3e2 100644 --- a/modules/Evse15118D20/manifest.yaml +++ b/modules/Evse15118D20/manifest.yaml @@ -43,6 +43,20 @@ config: Enable the built-in SDP server type: boolean default: true + supported_dynamic_mode: + description: The EVSE should support dynamic mode + type: boolean + default: true + supported_mobility_needs_mode_provided_by_secc: + description: >- + The EVSE should support the mobility needs mode provided by the SECC. + Mobility needs mode provided by the EVCC is always provided. + type: boolean + default: false + supported_scheduled_mode: + description: The EVSE should support scheduled mode + type: boolean + default: false provides: charger: interface: ISO15118_charger diff --git a/modules/EvseManager/EvseManager.cpp b/modules/EvseManager/EvseManager.cpp index 33d03439f..0782f8729 100644 --- a/modules/EvseManager/EvseManager.cpp +++ b/modules/EvseManager/EvseManager.cpp @@ -397,6 +397,48 @@ void EvseManager::ready() { } }); + r_hlc[0]->subscribe_d20_dc_dynamic_charge_mode( + [this](types::iso15118_charger::DcChargeDynamicModeValues values) { + constexpr auto PRE_CHARGE_MAX_POWER = 800.0f; + + bool target_changed{false}; + + if (values.min_voltage > latest_target_voltage) { + latest_target_voltage = values.min_voltage + 10; // TODO(sl): Check if okay + target_changed = true; + } + if (values.max_voltage < latest_target_voltage) { + latest_target_voltage = values.max_voltage - 10; // TODO(sl): Check if okay + target_changed = true; + } + + const double latest_target_power = latest_target_voltage * latest_target_current; + + if (latest_target_power <= PRE_CHARGE_MAX_POWER or values.min_charge_power > latest_target_power or + values.max_charge_power < latest_target_power) { + latest_target_current = static_cast(values.max_charge_power) / latest_target_voltage; + if (values.max_charge_current < latest_target_current) { + latest_target_current = values.max_charge_current; + } + target_changed = true; + } + + if (target_changed) { + apply_new_target_voltage_current(); + if (not contactor_open) { + powersupply_DC_on(); + } + + { + Everest::scoped_lock_timeout lock(ev_info_mutex, + Everest::MutexDescription::EVSE_publish_ev_info); + ev_info.target_voltage = latest_target_voltage; + ev_info.target_current = latest_target_current; + p_evse->publish_ev_info(ev_info); + } + } + }); + // Car requests DC contactor open. We don't actually open but switch off DC supply. // opening will be done by Charger on C->B CP event. r_hlc[0]->subscribe_dc_open_contactor([this] { diff --git a/modules/IsoMux/charger/ISO15118_chargerImpl.cpp b/modules/IsoMux/charger/ISO15118_chargerImpl.cpp index cbd1a2a3e..53a045b7c 100644 --- a/modules/IsoMux/charger/ISO15118_chargerImpl.cpp +++ b/modules/IsoMux/charger/ISO15118_chargerImpl.cpp @@ -435,6 +435,36 @@ void ISO15118_chargerImpl::init() { publish_display_parameters(o); } }); + + mod->r_iso20->subscribe_d20_dc_dynamic_charge_mode([this](const auto o) { + if (mod->selected_iso20()) { + publish_d20_dc_dynamic_charge_mode(o); + } + }); + + mod->r_iso2->subscribe_dc_ev_present_voltage([this](const auto o) { + if (not mod->selected_iso20()) { + publish_dc_ev_present_voltage(o); + } + }); + + mod->r_iso20->subscribe_dc_ev_present_voltage([this](const auto o) { + if (mod->selected_iso20()) { + publish_dc_ev_present_voltage(o); + } + }); + + mod->r_iso2->subscribe_meter_info_requested([this]() { + if (not mod->selected_iso20()) { + publish_meter_info_requested(nullptr); + } + }); + + mod->r_iso20->subscribe_meter_info_requested([this]() { + if (mod->selected_iso20()) { + publish_meter_info_requested(nullptr); + } + }); } void ISO15118_chargerImpl::ready() { diff --git a/types/iso15118_charger.yaml b/types/iso15118_charger.yaml index 89bbc1df3..6bd319648 100644 --- a/types/iso15118_charger.yaml +++ b/types/iso15118_charger.yaml @@ -586,3 +586,77 @@ types: inlet_hot: description: Inlet temperature is too high type: boolean + DcChargeDynamicModeValues: + description: Parameters for dynamic control mode + type: object + additionalProperties: false + required: + - target_energy_request + - max_energy_request + - min_energy_request + - max_charge_power + - min_charge_power + - max_charge_current + - max_voltage + - min_voltage + properties: + departure_time: + description: The time when the EV wants to finish charging + type: number + minimum: 0 + target_energy_request: + description: Energy request to fulfil the target SoC + type: number + minimum: 0 + max_energy_request: + description: Maximum acceptable energy level of the EV + type: number + minimum: 0 + min_energy_request: + description: Energy request to fulfil the minimum SoC + type: number + minimum: 0 + max_charge_power: + description: Maximum charge power allowed by the EV + type: number + minimum: 0 + min_charge_power: + description: Minimum charge power allowed by the EV + type: number + minimum: 0 + max_charge_current: + description: Maximum charge current allowed by the EV + type: number + minimum: 0 + max_voltage: + description: Maximum voltage allowed by the EV + type: number + minimum: 0 + min_voltage: + description: Minimum voltage allowd by the EV + type: number + minimum: 0 + max_discharge_power: + description: Maximum discharge current allowed by the EV + type: number + minimum: 0 + min_discharge_power: + description: Minimum discharge current allowed by the EVodo + type: number + minimum: 0 + max_discharge_current: + description: Maximum discharge current allowed by the EV + type: number + minimum: 0 + max_v2x_energy_request: + description: >- + Energy which may be charged until the PresentSOC has left the range + dedicated for cycling activity. + type: number + minimum: 0 + min_v2x_energy_request: + description: >- + Energy which needs to be charged until the PresentSOC has left the + range dedicated for cycling activity. + type: number + minimum: 0