From 3a326ca1f4cae82d8de42f4824f6116fe0c0c2fa Mon Sep 17 00:00:00 2001 From: Cornelius Claussen Date: Wed, 24 Apr 2024 13:52:36 +0200 Subject: [PATCH] DC Powersupply: allow runtime update of max current/watt caps Signed-off-by: Cornelius Claussen --- interfaces/power_supply_DC.yaml | 8 +++- modules/EvseManager/EvseManager.cpp | 45 ++++++++++--------- modules/EvseManager/EvseManager.hpp | 19 +++++++- .../EvseManager/energy_grid/energyImpl.cpp | 32 ++++++------- types/power_supply_DC.yaml | 20 +++++++++ 5 files changed, 85 insertions(+), 39 deletions(-) diff --git a/interfaces/power_supply_DC.yaml b/interfaces/power_supply_DC.yaml index 9008bedf50..2bbb5e4f9d 100644 --- a/interfaces/power_supply_DC.yaml +++ b/interfaces/power_supply_DC.yaml @@ -1,7 +1,9 @@ description: Interface for power supplies used for DC charging cmds: getCapabilities: - description: Get capabilities of power supply + description: >- + Get maximum capabilities of power supply. It should report the maximum possible values under best conditions, even if it + is e.g. derated at this moment due to high temperature. result: description: Capabilities type: object @@ -46,3 +48,7 @@ vars: description: Fault code. Published when fault happens. type: string $ref: /power_supply_DC#/FaultCode + update_capabilities: + description: Update some capability values at run-time. This can be used to e.g. report lower limits due to temperature de-rating of the PSU. + type: object + $ref: /power_supply_DC#/UpdateCapabilities diff --git a/modules/EvseManager/EvseManager.cpp b/modules/EvseManager/EvseManager.cpp index 2307d0e537..9b725bdecf 100644 --- a/modules/EvseManager/EvseManager.cpp +++ b/modules/EvseManager/EvseManager.cpp @@ -185,6 +185,10 @@ void EvseManager::ready() { powersupply_capabilities = r_powersupply_DC[0]->call_getCapabilities(); + // subscribe to run time updates e.g. due to de-rating + r_powersupply_DC[0]->subscribe_update_capabilities( + [this](auto caps) { update_powersupply_capabilities(caps); }); + updateLocalMaxWattLimit(powersupply_capabilities.max_export_power_W); setup_physical_values.dc_current_regulation_tolerance = @@ -1033,7 +1037,7 @@ bool EvseManager::updateLocalEnergyLimit(types::energy::ExternalLimits l) { // by default we import energy updateLocalMaxCurrentLimit(hw_capabilities.max_current_A_import); } else { - updateLocalMaxWattLimit(powersupply_capabilities.max_export_power_W); + updateLocalMaxWattLimit(get_powersupply_capabilities().max_export_power_W); } } else { // apply external limits if they are lower @@ -1242,6 +1246,7 @@ void EvseManager::cable_check() { ok = false; imd_stop(); } else { + auto caps = get_powersupply_capabilities(); // read out one new isolation resistance isolation_measurement.clear(); types::isolation_monitor::IsolationMeasurement m; @@ -1252,9 +1257,9 @@ void EvseManager::cable_check() { fail_session(); } else { // wait until the voltage is back to safe level - float minvoltage = (config.switch_to_minimum_voltage_after_cable_check - ? powersupply_capabilities.min_export_voltage_V - : config.dc_isolation_voltage_V); + float minvoltage = + (config.switch_to_minimum_voltage_after_cable_check ? caps.min_export_voltage_V + : config.dc_isolation_voltage_V); // We do not want to shut down power supply if (minvoltage < 60) { @@ -1269,8 +1274,8 @@ void EvseManager::cable_check() { } else { // verify it is within ranges. Warning level is <500 Ohm/V_max_output_rating, Fault // is <100 - const double min_resistance_ok = 500. * powersupply_capabilities.max_export_voltage_V; - const double min_resistance_warning = 100. * powersupply_capabilities.max_export_voltage_V; + const double min_resistance_ok = 500. * caps.max_export_voltage_V; + const double min_resistance_warning = 100. * caps.max_export_voltage_V; if (m.resistance_F_Ohm < min_resistance_warning) { session_log.evse( @@ -1350,6 +1355,8 @@ bool EvseManager::powersupply_DC_set(double _voltage, double _current) { current = std::abs(current); } + auto caps = get_powersupply_capabilities(); + if ((config.hack_allow_bpt_with_iso2 or config.sae_j2847_2_bpt_enabled) and current_demand_active and is_actually_exporting_to_grid) { if (not last_is_actually_exporting_to_grid) { @@ -1360,17 +1367,14 @@ bool EvseManager::powersupply_DC_set(double _voltage, double _current) { last_is_actually_exporting_to_grid = is_actually_exporting_to_grid; // Hack: we are exporting to grid but are in ISO-2 mode // check limits of supply - if (powersupply_capabilities.min_import_voltage_V.has_value() and - voltage >= powersupply_capabilities.min_import_voltage_V.value() and - voltage <= powersupply_capabilities.max_import_voltage_V.value()) { + if (caps.min_import_voltage_V.has_value() and voltage >= caps.min_import_voltage_V.value() and + voltage <= caps.max_import_voltage_V.value()) { - if (powersupply_capabilities.max_import_current_A.has_value() and - current > powersupply_capabilities.max_import_current_A.value()) - current = powersupply_capabilities.max_import_current_A.value(); + if (caps.max_import_current_A.has_value() and current > caps.max_import_current_A.value()) + current = caps.max_import_current_A.value(); - if (powersupply_capabilities.min_import_current_A.has_value() and - current < powersupply_capabilities.min_import_current_A.value()) - current = powersupply_capabilities.min_import_current_A.value(); + if (caps.min_import_current_A.has_value() and current < caps.min_import_current_A.value()) + current = caps.min_import_current_A.value(); // Now it is within limits of DC power supply. // now also limit with the limits given by the energymanager. @@ -1398,14 +1402,13 @@ bool EvseManager::powersupply_DC_set(double _voltage, double _current) { } // check limits of supply - if (voltage >= powersupply_capabilities.min_export_voltage_V and - voltage <= powersupply_capabilities.max_export_voltage_V) { + if (voltage >= caps.min_export_voltage_V and voltage <= caps.max_export_voltage_V) { - if (current > powersupply_capabilities.max_export_current_A) - current = powersupply_capabilities.max_export_current_A; + if (current > caps.max_export_current_A) + current = caps.max_export_current_A; - if (current < powersupply_capabilities.min_export_current_A) - current = powersupply_capabilities.min_export_current_A; + if (current < caps.min_export_current_A) + current = caps.min_export_current_A; // Now it is within limits of DC power supply. // now also limit with the limits given by the energymanager. diff --git a/modules/EvseManager/EvseManager.hpp b/modules/EvseManager/EvseManager.hpp index 96c7aa2639..df31ecf35b 100644 --- a/modules/EvseManager/EvseManager.hpp +++ b/modules/EvseManager/EvseManager.hpp @@ -166,7 +166,6 @@ class EvseManager : public Everest::ModuleBase { void charger_was_authorized(); const std::vector>& r_powermeter_billing(); - types::power_supply_DC::Capabilities powersupply_capabilities; // FIXME: this will be removed with proper intergration of BPT on ISO-20 // on DIN SPEC and -2 we claim a positive charging current on ISO protocol, @@ -191,6 +190,21 @@ class EvseManager : public Everest::ModuleBase { std::chrono::time_point random_delay_start_time; std::atomic random_delay_max_duration; std::atomic> timepoint_ready_for_charging; + + types::power_supply_DC::Capabilities get_powersupply_capabilities() { + std::scoped_lock lock(powersupply_capabilities_mutex); + return powersupply_capabilities; + } + + void update_powersupply_capabilities(types::power_supply_DC::UpdateCapabilities caps) { + std::scoped_lock lock(powersupply_capabilities_mutex); + powersupply_capabilities.max_export_current_A = caps.max_export_current_A; + powersupply_capabilities.max_export_power_W = caps.max_export_power_W; + + powersupply_capabilities.max_import_current_A = caps.max_import_current_A; + powersupply_capabilities.max_import_power_W = caps.max_import_power_W; + } + // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 protected: @@ -205,6 +219,9 @@ class EvseManager : public Everest::ModuleBase { // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 // insert your private definitions here + std::mutex powersupply_capabilities_mutex; + types::power_supply_DC::Capabilities powersupply_capabilities; + Everest::timed_mutex_traceable power_mutex; types::powermeter::Powermeter latest_powermeter_data_billing; diff --git a/modules/EvseManager/energy_grid/energyImpl.cpp b/modules/EvseManager/energy_grid/energyImpl.cpp index 89818e572f..2293dbc193 100644 --- a/modules/EvseManager/energy_grid/energyImpl.cpp +++ b/modules/EvseManager/energy_grid/energyImpl.cpp @@ -62,7 +62,7 @@ void energyImpl::clear_import_request_schedule() { entry_import.limits_to_root.ac_min_current_A = hw_caps.min_current_A_import; entry_import.limits_to_root.ac_supports_changing_phases_during_charging = hw_caps.supports_changing_phases_during_charging; - entry_import.conversion_efficiency = mod->powersupply_capabilities.conversion_efficiency_export; + entry_import.conversion_efficiency = mod->get_powersupply_capabilities().conversion_efficiency_export; energy_flow_request.schedule_import.emplace(std::vector({entry_import})); } @@ -80,7 +80,7 @@ void energyImpl::clear_export_request_schedule() { entry_export.limits_to_root.ac_min_current_A = hw_caps.min_current_A_export; entry_export.limits_to_root.ac_supports_changing_phases_during_charging = hw_caps.supports_changing_phases_during_charging; - entry_export.conversion_efficiency = mod->powersupply_capabilities.conversion_efficiency_import; + entry_export.conversion_efficiency = mod->get_powersupply_capabilities().conversion_efficiency_import; energy_flow_request.schedule_export.emplace(std::vector({entry_export})); } @@ -387,6 +387,8 @@ void energyImpl::handle_enforce_limits(types::energy::EnforcedLimits& value) { last_target_voltage = target_voltage; last_actual_voltage = actual_voltage; + auto powersupply_capabilities = mod->get_powersupply_capabilities(); + // tell car our new limits types::iso15118_charger::DC_EVSEMaximumLimits evseMaxLimits; @@ -403,33 +405,31 @@ void energyImpl::handle_enforce_limits(types::energy::EnforcedLimits& value) { value.limits_root_side.value().total_power_W.value() / target_voltage; } } else { - evseMaxLimits.EVSEMaximumCurrentLimit = mod->powersupply_capabilities.max_export_current_A; + evseMaxLimits.EVSEMaximumCurrentLimit = powersupply_capabilities.max_export_current_A; } - if (evseMaxLimits.EVSEMaximumCurrentLimit > mod->powersupply_capabilities.max_export_current_A) - evseMaxLimits.EVSEMaximumCurrentLimit = mod->powersupply_capabilities.max_export_current_A; + if (evseMaxLimits.EVSEMaximumCurrentLimit > powersupply_capabilities.max_export_current_A) + evseMaxLimits.EVSEMaximumCurrentLimit = powersupply_capabilities.max_export_current_A; - if (mod->powersupply_capabilities.max_import_current_A.has_value() && - evseMaxLimits.EVSEMaximumCurrentLimit < - -mod->powersupply_capabilities.max_import_current_A.value()) - evseMaxLimits.EVSEMaximumCurrentLimit = - -mod->powersupply_capabilities.max_import_current_A.value(); + if (powersupply_capabilities.max_import_current_A.has_value() && + evseMaxLimits.EVSEMaximumCurrentLimit < -powersupply_capabilities.max_import_current_A.value()) + evseMaxLimits.EVSEMaximumCurrentLimit = -powersupply_capabilities.max_import_current_A.value(); // now evseMaxLimits.EVSEMaximumCurrentLimit is between // -max_import_current_A ... +max_export_current_A evseMaxLimits.EVSEMaximumPowerLimit = value.limits_root_side.value().total_power_W.value(); - if (evseMaxLimits.EVSEMaximumPowerLimit > mod->powersupply_capabilities.max_export_power_W) - evseMaxLimits.EVSEMaximumPowerLimit = mod->powersupply_capabilities.max_export_power_W; + if (evseMaxLimits.EVSEMaximumPowerLimit > powersupply_capabilities.max_export_power_W) + evseMaxLimits.EVSEMaximumPowerLimit = powersupply_capabilities.max_export_power_W; - if (mod->powersupply_capabilities.max_import_power_W.has_value() && - evseMaxLimits.EVSEMaximumPowerLimit < -mod->powersupply_capabilities.max_import_power_W.value()) - evseMaxLimits.EVSEMaximumPowerLimit = -mod->powersupply_capabilities.max_import_power_W.value(); + if (powersupply_capabilities.max_import_power_W.has_value() && + evseMaxLimits.EVSEMaximumPowerLimit < -powersupply_capabilities.max_import_power_W.value()) + evseMaxLimits.EVSEMaximumPowerLimit = -powersupply_capabilities.max_import_power_W.value(); // now evseMaxLimits.EVSEMaximumPowerLimit is between // -max_import_power_W ... +max_export_power_W - evseMaxLimits.EVSEMaximumVoltageLimit = mod->powersupply_capabilities.max_export_voltage_V; + evseMaxLimits.EVSEMaximumVoltageLimit = powersupply_capabilities.max_export_voltage_V; // FIXME: we tell the ISO stack positive numbers for DIN spec and ISO-2 here in case of exporting to // grid. This needs to be fixed in the transition to -20 for BPT. diff --git a/types/power_supply_DC.yaml b/types/power_supply_DC.yaml index 1cbd892136..c0ce6c01a5 100644 --- a/types/power_supply_DC.yaml +++ b/types/power_supply_DC.yaml @@ -93,6 +93,26 @@ types: type: number minimum: 0.0 maximum: 1.0 + UpdateCapabilities: + description: Runtime update of some of the capabilities values. Can be used to e.g. inform about throttling of a power supply due to temperature de-rating. + type: object + additionalProperties: false + required: + - max_export_current_A + - max_export_power_W + properties: + max_export_current_A: + description: Maximum current that the power supply can output in Ampere + type: number + max_import_current_A: + description: Maximum current that the power supply can output in Ampere + type: number + max_export_power_W: + description: Maximum export power that the power supply can output in Watt + type: number + max_import_power_W: + description: Maximum import power that the power supply can sink in Watt + type: number FaultCode: description: Fault codes type: string