@@ -307,7 +307,7 @@ void Charger::run_state_machine() {
307307 if (config_context.charge_mode == ChargeMode::DC) {
308308 // Create a copy of the atomic struct
309309 types::iso15118::DcEvseMaximumLimits evse_limit = shared_context.current_evse_max_limits ;
310- if (not (evse_limit. evse_maximum_current_limit > 0 and evse_limit. evse_maximum_power_limit > 0 )) {
310+ if (not power_available ( )) {
311311
312312 // Wait some time here in this state to see if we get energy from the EnergyManager...
313313 if (time_in_current_state < WAIT_FOR_ENERGY_IN_AUTHLOOP_TIMEOUT_MS) {
@@ -593,11 +593,12 @@ void Charger::run_state_machine() {
593593 if (initialize_state) {
594594 signal_simple_event (types::evse_manager::SessionEventEnum::PrepareCharging);
595595 bcb_toggle_reset ();
596+ internal_context.hlc_charge_loop_no_energy_timeout_running = false ;
596597
597598 if (config_context.charge_mode == ChargeMode::DC) {
598599 // Create a copy of the atomic struct
599600 types::iso15118::DcEvseMaximumLimits evse_limit = shared_context.current_evse_max_limits ;
600- if (not (evse_limit. evse_maximum_current_limit > 0 and evse_limit. evse_maximum_power_limit > 0 )) {
601+ if (not power_available ( )) {
601602 signal_hlc_no_energy_available ();
602603 }
603604 }
@@ -628,18 +629,17 @@ void Charger::run_state_machine() {
628629 // used as the car can do BASIC and HLC charging any time. In AC HLC with 5 percent mode, we need to
629630 // wait for both iec_allow and hlc_allow.
630631 if (not power_available () and not hlc_use_5percent_current_session) {
631- // For AC BC: it is ok to wait here.
632- // For AC and DC ISO, continue in case we are in 5% mode. This allows us to go into
633- // Charge loop and report 0A/0W to the EV
632+ // For AC BC: it is ok to wait here.
633+ // For AC and DC ISO, continue in case we are in 5% mode. This allows us to go into
634+ // Charge loop and report 0A/0W to the EV
634635 break ;
635636 }
636637
637638 // Power is available or we are in HLC, PWM is already enabled. Check if we can go to charging
638639 if ((shared_context.iec_allow_close_contactor and not hlc_use_5percent_current_session) or
639640 (shared_context.iec_allow_close_contactor and shared_context.hlc_allow_close_contactor and
640641 hlc_use_5percent_current_session)) {
641-
642- shared_context.current_state = EvseState::Charging;
642+ set_state (EvseState::Charging);
643643 } else {
644644 // We have power and PWM is on, but EV did not proceed to state C yet (and/or HLC is not
645645 // ready)
@@ -661,6 +661,8 @@ void Charger::run_state_machine() {
661661 }
662662 }
663663
664+ // For DC we go into charging when CurrentDemand started (notify_currentdemand_started)
665+
664666 // if (charge_mode == ChargeMode::DC) {
665667 // DC: wait until car requests power on CP (B->C/D).
666668 // Then we close contactor and wait for instructions from HLC.
@@ -695,25 +697,54 @@ void Charger::run_state_machine() {
695697 break ;
696698 }
697699
700+ if (not power_available ()) {
701+ const bool hlc_session_active =
702+ shared_context.hlc_charging_active or hlc_use_5percent_current_session;
703+ if (hlc_session_active) {
704+ if (config_context.hlc_charge_loop_without_energy_timeout_s > 0 ) {
705+ if (not internal_context.hlc_charge_loop_no_energy_timeout_running ) {
706+ internal_context.hlc_charge_loop_no_energy_timeout_running = true ;
707+ internal_context.iso_charge_loop_no_energy_timeout_started =
708+ std::chrono::steady_clock::now ();
709+ session_log.evse (false , fmt::format (" No energy available, starting ISO charge loop timeout of {} seconds" ,
710+ config_context.hlc_charge_loop_without_energy_timeout_s ));
711+ } else {
712+ const auto no_energy_duration_ms =
713+ std::chrono::duration_cast<std::chrono::milliseconds>(
714+ std::chrono::steady_clock::now () -
715+ internal_context.iso_charge_loop_no_energy_timeout_started )
716+ .count ();
717+ if (no_energy_duration_ms >=
718+ config_context.hlc_charge_loop_without_energy_timeout_s * 1000 ) {
719+ session_log.evse (false ,
720+ " No energy available, ISO charge loop timeout reached" );
721+ internal_context.hlc_charge_loop_no_energy_timeout_running = false ;
722+ set_state (EvseState::StoppingCharging);
723+ break ;
724+ }
725+ }
726+ } else {
727+ session_log.evse (false , " No energy available, but no timeout configured, stopping charging immediately" );
728+ set_state (EvseState::StoppingCharging);
729+ break ;
730+ }
731+ } else if (config_context.charge_mode == ChargeMode::AC) {
732+ EVLOG_info << " HLC charging not active" ;
733+ // Stop immediately in basic AC mode
734+ set_state (EvseState::StoppingCharging); // Proxy state to ChargingPausedEVSE
735+ break ;
736+ }
737+ } else {
738+ internal_context.hlc_charge_loop_no_energy_timeout_running = false ;
739+ }
740+
698741 if (config_context.charge_mode == ChargeMode::DC) {
699742 if (initialize_state) {
700743 bsp->allow_power_on (true , types::evse_board_support::Reason::FullPowerCharging);
701744 }
702745 } else {
703746 check_soft_over_current ();
704747
705- if (not power_available ()) {
706- if (shared_context.hlc_charging_active or hlc_use_5percent_current_session) {
707- // We are in HLC and have no energy
708- // TODO: start timer to stop charging
709- } else {
710- EVLOG_info << " HLC charging not active" ;
711- // Stop immediately in basic AC mode
712- set_state (EvseState::StoppingCharging); // Proxy state to ChargingPausedEVSE
713- break ;
714- }
715- }
716-
717748 if (initialize_state) {
718749 // Allow another wake-up sequence
719750 shared_context.legacy_wakeup_done = false ;
@@ -1406,7 +1437,8 @@ void Charger::setup(bool has_ventilation, const ChargeMode _charge_mode, bool _a
14061437 const int _switch_3ph1ph_delay_s, const std::string _switch_3ph1ph_cp_state,
14071438 const int _soft_over_current_timeout_ms, const int _state_F_after_fault_ms,
14081439 const bool fail_on_powermeter_errors, const bool raise_mrec9,
1409- const int sleep_before_enabling_pwm_hlc_mode_ms, const utils::SessionIdType session_id_type) {
1440+ const int sleep_before_enabling_pwm_hlc_mode_ms, const utils::SessionIdType session_id_type,
1441+ const int hlc_charge_loop_without_energy_timeout_s) {
14101442 // set up board support package
14111443 bsp->setup (has_ventilation);
14121444
@@ -1430,6 +1462,7 @@ void Charger::setup(bool has_ventilation, const ChargeMode _charge_mode, bool _a
14301462 config_context.raise_mrec9 = raise_mrec9;
14311463 config_context.sleep_before_enabling_pwm_hlc_mode_ms = sleep_before_enabling_pwm_hlc_mode_ms;
14321464 config_context.session_id_type = session_id_type;
1465+ config_context.hlc_charge_loop_without_energy_timeout_s = hlc_charge_loop_without_energy_timeout_s;
14331466
14341467 if (config_context.charge_mode == ChargeMode::AC and config_context.ac_hlc_enabled )
14351468 EVLOG_info << " AC HLC mode enabled." ;
@@ -1810,15 +1843,21 @@ void Charger::check_soft_over_current() {
18101843// returns whether power is actually available from EnergyManager
18111844// i.e. max_current is in valid range
18121845bool Charger::power_available () {
1813- const auto overrun = duration_cast<seconds>(steady_clock::now () - shared_context.max_current_valid_until ).count ();
1814- if (overrun > 0 ) {
1815- EVLOG_warning << " Power budget expired, falling back to 0. Last update: " << overrun << " seconds ago" ;
1816- if (shared_context.max_current > 0 .) {
1817- shared_context.max_current = 0 .;
1818- signal_max_current (shared_context.max_current );
1846+ if (config_context.charge_mode == ChargeMode::AC) {
1847+ const auto overrun =
1848+ duration_cast<seconds>(steady_clock::now () - shared_context.max_current_valid_until ).count ();
1849+ if (overrun > 0 ) {
1850+ EVLOG_warning << " Power budget expired, falling back to 0. Last update: " << overrun << " seconds ago" ;
1851+ if (shared_context.max_current > 0 .) {
1852+ shared_context.max_current = 0 .;
1853+ signal_max_current (shared_context.max_current );
1854+ }
18191855 }
1856+ return get_max_current_internal () > 5.9 ;
1857+ } else {
1858+ const auto evse_limit = shared_context.current_evse_max_limits ;
1859+ return evse_limit.evse_maximum_current_limit > 0 and evse_limit.evse_maximum_power_limit > 0 ;
18201860 }
1821- return get_max_current_internal () > 5.9 ;
18221861}
18231862
18241863void Charger::request_error_sequence () {
0 commit comments