diff --git a/iso15118/evcc/controller/simulator.py b/iso15118/evcc/controller/simulator.py index 4c6c1671..8cb9c6dc 100644 --- a/iso15118/evcc/controller/simulator.py +++ b/iso15118/evcc/controller/simulator.py @@ -679,21 +679,21 @@ async def get_scheduled_dc_charge_loop_params( ) -> ScheduledDCChargeLoopReqParams: """Overrides EVControllerInterface.get_scheduled_dc_charge_loop_params().""" return ScheduledDCChargeLoopReqParams( - ev_target_current=RationalNumber(exponent=3, value=40), - ev_target_voltage=RationalNumber(exponent=3, value=60), + ev_target_current=RationalNumber(exponent=1, value=20), + ev_target_voltage=RationalNumber(exponent=1, value=20), ) async def get_dynamic_dc_charge_loop_params(self) -> DynamicDCChargeLoopReqParams: """Overrides EVControllerInterface.get_dynamic_dc_charge_loop_params().""" return DynamicDCChargeLoopReqParams( - ev_target_energy_request=RationalNumber(exponent=3, value=40), - ev_max_energy_request=RationalNumber(exponent=3, value=60), - ev_min_energy_request=RationalNumber(exponent=-2, value=20), - ev_max_charge_power=RationalNumber(exponent=3, value=40), - ev_min_charge_power=RationalNumber(exponent=3, value=300), - ev_max_charge_current=RationalNumber(exponent=3, value=40), - ev_max_voltage=RationalNumber(exponent=3, value=300), - ev_min_voltage=RationalNumber(exponent=3, value=300), + ev_target_energy_request=RationalNumber(exponent=1, value=20), + ev_max_energy_request=RationalNumber(exponent=1, value=20), + ev_min_energy_request=RationalNumber(exponent=0, value=20), + ev_max_charge_power=RationalNumber(exponent=2, value=40), + ev_min_charge_power=RationalNumber(exponent=1, value=40), + ev_max_charge_current=RationalNumber(exponent=0, value=40), + ev_max_voltage=RationalNumber(exponent=1, value=40), + ev_min_voltage=RationalNumber(exponent=0, value=40), ) async def get_bpt_scheduled_dc_charge_loop_params( diff --git a/iso15118/secc/states/din_spec_states.py b/iso15118/secc/states/din_spec_states.py index 3afb80d8..63eb341b 100644 --- a/iso15118/secc/states/din_spec_states.py +++ b/iso15118/secc/states/din_spec_states.py @@ -438,9 +438,8 @@ class CableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_SECC_SEQUENCE_TIMEOUT) - self.cable_check_req_was_received: bool = False + self.contactors_closed: bool = False self.cable_check_started: bool = False - self.contactors_closed_for_cable_check: Optional[bool] = None async def process_message( self, @@ -472,63 +471,62 @@ async def process_message( evse_processing: EVSEProcessing = EVSEProcessing.ONGOING response_code: ResponseCode = ResponseCode.OK next_state = None - if not self.cable_check_req_was_received: - # Requirement in 6.4.3.106 of the IEC 61851-23 - # Any relays in the DC output circuit of the DC station shall - # be closed during the insulation test - self.contactors_closed_for_cable_check = ( - await self.comm_session.evse_controller.is_contactor_closed() - ) - self.cable_check_req_was_received = True - if self.contactors_closed_for_cable_check is not None: - if not self.contactors_closed_for_cable_check: + if not self.cable_check_started: + await self.comm_session.evse_controller.start_cable_check() + self.cable_check_started = True + + if self.contactors_closed: + isolation_level = ( + await self.comm_session.evse_controller.get_cable_check_status() + ) # noqa + + evse_processing = EVSEProcessing.ONGOING + next_state = None + if isolation_level in [ + IsolationLevel.VALID, + IsolationLevel.WARNING, + ]: + if isolation_level == IsolationLevel.WARNING: + logger.warning( + "Isolation resistance measured by EVSE is in Warning-Range" + ) + evse_processing = EVSEProcessing.FINISHED + next_state = PreCharge + elif isolation_level in [ + IsolationLevel.FAULT, + IsolationLevel.INVALID, + ]: self.stop_state_machine( - "Contactor didnt close for Cable Check", + f"Isolation Failure: {isolation_level}", message, ResponseCode.FAILED, ) return - - if self.cable_check_started: - isolation_level = ( - await self.comm_session.evse_controller.get_cable_check_status() - ) # noqa - - evse_processing = EVSEProcessing.ONGOING - next_state = None - if isolation_level in [ - IsolationLevel.VALID, - IsolationLevel.WARNING, - ]: - if isolation_level == IsolationLevel.WARNING: - logger.warning( - "Isolation resistance measured by EVSE is in Warning-Range" - ) - evse_processing = EVSEProcessing.FINISHED - next_state = PreCharge - elif isolation_level in [ - IsolationLevel.FAULT, - IsolationLevel.INVALID, - ]: + else: + # Requirement in 6.4.3.106 of the IEC 61851-23 + # Any relays in the DC output circuit of the DC station shall + # be closed during the insulation test + # If None is returned, then contactor close operation is ongoing. + contactors_closed_for_cable_check: Optional[ + bool + ] = await self.comm_session.evse_controller.is_contactor_closed() + + if contactors_closed_for_cable_check is not None: + if contactors_closed_for_cable_check: + self.contactors_closed = True + else: self.stop_state_machine( - f"Isolation Failure: {isolation_level}", + "Contactor didnt close for Cable Check", message, ResponseCode.FAILED, ) return - else: - await self.comm_session.evse_controller.start_cable_check() - self.cable_check_started = True self.comm_session.evse_controller.ev_data_context.present_soc = ( cable_check_req.dc_ev_status.ev_ress_soc ) - isolation_level = ( - await self.comm_session.evse_controller.get_cable_check_status() - ) # noqa - # [V2G-DC-418] Stay in CableCheck state until EVSEProcessing is complete. # Until EVSEProcessing is completed, EV will send identical # CableCheckReq message. diff --git a/iso15118/secc/states/iso15118_20_states.py b/iso15118/secc/states/iso15118_20_states.py index a8b1be01..46638aa5 100644 --- a/iso15118/secc/states/iso15118_20_states.py +++ b/iso15118/secc/states/iso15118_20_states.py @@ -1585,9 +1585,8 @@ class DCCableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT) - self.cable_check_req_was_received = False + self.contactors_closed = False self.cable_check_started = False - self.contactors_closed_for_cable_check: Optional[bool] = None async def process_message( self, @@ -1613,46 +1612,50 @@ async def process_message( next_state = None processing = EVSEProcessing.ONGOING - if not self.cable_check_req_was_received: - # Requirement in 6.4.3.106 of the IEC 61851-23 - # Any relays in the DC output circuit of the DC station shall - # be closed during the insulation test - self.contactors_closed_for_cable_check = ( - await self.comm_session.evse_controller.is_contactor_closed() + if not self.cable_check_started: + # Start cable check as contactors are now closed. + await self.comm_session.evse_controller.start_cable_check() + self.cable_check_started = True + + if self.contactors_closed: + isolation_level = ( + await self.comm_session.evse_controller.get_cable_check_status() ) - self.cable_check_req_was_received = True - if self.contactors_closed_for_cable_check is not None: - if not self.contactors_closed_for_cable_check: + if isolation_level in [IsolationLevel.VALID, IsolationLevel.WARNING]: + if isolation_level == IsolationLevel.WARNING: + logger.warning( + "Isolation resistance measured by EVSE is in Warning range" + ) + next_state = DCPreCharge + processing = EVSEProcessing.FINISHED + elif isolation_level in [IsolationLevel.INVALID, IsolationLevel.FAULT]: self.stop_state_machine( - "Contactor didnt close for Cable Check", + f"Isolation Failure: {isolation_level}", message, ResponseCode.FAILED, ) return - - if self.cable_check_started: - isolation_level = ( - await self.comm_session.evse_controller.get_cable_check_status() - ) - - if isolation_level in [IsolationLevel.VALID, IsolationLevel.WARNING]: - if isolation_level == IsolationLevel.WARNING: - logger.warning( - "Isolation resistance measured by EVSE is in Warning range" + else: + if not self.contactors_closed: + # Requirement in 6.4.3.106 of the IEC 61851-23 + # Any relays in the DC output circuit of the DC station shall + # be closed during the insulation test + # If None is returned, then contactor close operation is ongoing. + contactors_closed_for_cable_check: Optional[ + bool + ] = await self.comm_session.evse_controller.is_contactor_closed() + + if contactors_closed_for_cable_check is not None: + if contactors_closed_for_cable_check: + self.contactors_closed = True + else: + self.stop_state_machine( + "Contactor didnt close for Cable Check", + message, + ResponseCode.FAILED, ) - next_state = DCPreCharge - processing = EVSEProcessing.FINISHED - elif isolation_level in [IsolationLevel.INVALID, IsolationLevel.FAULT]: - self.stop_state_machine( - f"Isolation Failure: {isolation_level}", - message, - ResponseCode.FAILED, - ) - return - else: - await self.comm_session.evse_controller.start_cable_check() - self.cable_check_started = True + return dc_cable_check_res = DCCableCheckRes( header=MessageHeader( diff --git a/iso15118/secc/states/iso15118_2_states.py b/iso15118/secc/states/iso15118_2_states.py index fd35995c..b9d2b4dc 100644 --- a/iso15118/secc/states/iso15118_2_states.py +++ b/iso15118/secc/states/iso15118_2_states.py @@ -2186,9 +2186,8 @@ class CableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_SECC_SEQUENCE_TIMEOUT) - self.cable_check_req_was_received = False self.cable_check_started = False - self.contactors_closed_for_cable_check: Optional[bool] = None + self.contactors_closed = False async def process_message( self, @@ -2219,55 +2218,57 @@ async def process_message( next_state = None evse_processing = EVSEProcessing.ONGOING - if not self.cable_check_req_was_received: - # Requirement in 6.4.3.106 of the IEC 61851-23 - # Any relays in the DC output circuit of the DC station shall - # be closed during the insulation test - self.contactors_closed_for_cable_check = ( - await self.comm_session.evse_controller.is_contactor_closed() - ) - self.cable_check_req_was_received = True + if not self.cable_check_started: + await self.comm_session.evse_controller.start_cable_check() + self.cable_check_started = True - if self.contactors_closed_for_cable_check is not None: - if not self.contactors_closed_for_cable_check: + if self.contactors_closed: + isolation_level = ( + await self.comm_session.evse_controller.get_cable_check_status() + ) # noqa + + evse_processing = EVSEProcessing.ONGOING + next_state = None + if isolation_level in [ + IsolationLevel.VALID, + IsolationLevel.WARNING, + ]: + if isolation_level == IsolationLevel.WARNING: + logger.warning( + "Isolation resistance measured by EVSE is in Warning-Range" + ) + evse_processing = EVSEProcessing.FINISHED + next_state = PreCharge + elif isolation_level in [ + IsolationLevel.FAULT, + IsolationLevel.NO_IMD, + IsolationLevel.INVALID, + ]: self.stop_state_machine( - "Contactor didnt close for Cable Check", + f"Isolation Failure: {isolation_level}", message, ResponseCode.FAILED, ) return + else: + # Requirement in 6.4.3.106 of the IEC 61851-23 + # Any relays in the DC output circuit of the DC station shall + # be closed during the insulation test + # If None is returned, then contactor close operation is ongoing. + contactors_closed_for_cable_check = ( + await self.comm_session.evse_controller.is_contactor_closed() + ) - if self.cable_check_started: - isolation_level = ( - await self.comm_session.evse_controller.get_cable_check_status() - ) # noqa - - evse_processing = EVSEProcessing.ONGOING - next_state = None - if isolation_level in [ - IsolationLevel.VALID, - IsolationLevel.WARNING, - ]: - if isolation_level == IsolationLevel.WARNING: - logger.warning( - "Isolation resistance measured by EVSE is in Warning-Range" - ) - evse_processing = EVSEProcessing.FINISHED - next_state = PreCharge - elif isolation_level in [ - IsolationLevel.FAULT, - IsolationLevel.NO_IMD, - IsolationLevel.INVALID, - ]: + if contactors_closed_for_cable_check is not None: + if contactors_closed_for_cable_check: + self.contactors_closed = True + else: self.stop_state_machine( - f"Isolation Failure: {isolation_level}", + "Contactor didnt close for Cable Check", message, ResponseCode.FAILED, ) return - else: - await self.comm_session.evse_controller.start_cable_check() - self.cable_check_started = True self.comm_session.evse_controller.ev_data_context.present_soc = ( cable_check_req.dc_ev_status.ev_ress_soc diff --git a/tests/dinspec/secc/test_dinspec_secc_states.py b/tests/dinspec/secc/test_dinspec_secc_states.py index de1dad59..c93cef89 100644 --- a/tests/dinspec/secc/test_dinspec_secc_states.py +++ b/tests/dinspec/secc/test_dinspec_secc_states.py @@ -134,45 +134,39 @@ async def test_power_delivery_req_set_hlc_charging( self.comm_session.evse_controller.set_hlc_charging.assert_called_with(False) @pytest.mark.parametrize( - "cable_check_req_received, " "is_contactor_closed, " "cable_check_started, " "cable_check_status, " "expected_state", [ - (False, None, False, None, None), # First request. + (None, False, None, None), # First request. ( - True, None, False, None, None, ), # Not first request. Contactor status unknown. - (True, True, False, None, None), # Not first request. Contactor closed. - (True, False, False, None, Terminate), # Contactor close failed. + (True, False, None, None), # Not first request. Contactor closed. + (False, False, None, Terminate), # Contactor close failed. ( - True, True, True, IsolationLevel.VALID, PreCharge, ), # noqa Contactor closed. Isolation response received - Valid. Next stage Precharge. ( - True, True, True, IsolationLevel.INVALID, Terminate, ), # noqa Contactor closed. Isolation response received - Invalid. Terminate. ( - True, True, True, IsolationLevel.WARNING, PreCharge, ), # noqa Contactor closed. Isolation response received - Warning. Next stage Precharge. ( - True, True, True, IsolationLevel.FAULT, @@ -182,7 +176,6 @@ async def test_power_delivery_req_set_hlc_charging( ) async def test_15118_dinspec_dc_cable_check( self, - cable_check_req_received: bool, is_contactor_closed: bool, cable_check_started: bool, cable_check_status: IsolationLevel, @@ -202,9 +195,8 @@ async def test_15118_dinspec_dc_cable_check( ) dc_cable_check = CableCheck(self.comm_session) - dc_cable_check.cable_check_req_was_received = cable_check_req_received - dc_cable_check.contactors_closed_for_cable_check = is_contactor_closed dc_cable_check.cable_check_started = cable_check_started + dc_cable_check.contactors_closed = is_contactor_closed contactor_status = AsyncMock(return_value=is_contactor_closed) self.comm_session.evse_controller.is_contactor_closed = contactor_status cable_check_status = AsyncMock(return_value=cable_check_status) diff --git a/tests/iso15118_2/secc/states/test_iso15118_2_states.py b/tests/iso15118_2/secc/states/test_iso15118_2_states.py index ffc65000..785831d6 100644 --- a/tests/iso15118_2/secc/states/test_iso15118_2_states.py +++ b/tests/iso15118_2/secc/states/test_iso15118_2_states.py @@ -1109,52 +1109,45 @@ async def test_sales_tariff_in_free_charging_schedules(self, free_charging_servi ) @pytest.mark.parametrize( - "cable_check_req_received, " "is_contactor_closed, " "cable_check_started, " "cable_check_status, " "expected_state", [ - (False, None, False, None, None), # First request. + (None, False, None, None), # First request. ( - True, None, False, None, None, ), # Not first request. Contactor status unknown. - (True, True, False, None, None), # Not first request. Contactor closed. - (True, False, False, None, Terminate), # Contactor close failed. + (True, False, None, None), # Not first request. Contactor closed. + (False, False, None, Terminate), # Contactor close failed. ( - True, True, True, IsolationLevel.VALID, PreCharge, ), # noqa Contactor closed. Isolation response received - Valid. Next stage Precharge. ( - True, True, True, IsolationLevel.INVALID, Terminate, ), # noqa Contactor closed. Isolation response received - Invalid. Terminate. ( - True, True, True, IsolationLevel.WARNING, PreCharge, ), # noqa Contactor closed. Isolation response received - Warning. Next stage Precharge. ( - True, True, True, IsolationLevel.FAULT, Terminate, ), # noqa Contactor closed. Isolation response received - Fault. Terminate session. ( - True, True, True, IsolationLevel.NO_IMD, @@ -1165,16 +1158,14 @@ async def test_sales_tariff_in_free_charging_schedules(self, free_charging_servi ) async def test_15118_2_dc_cable_check( self, - cable_check_req_received: bool, is_contactor_closed: bool, cable_check_started: bool, cable_check_status: IsolationLevel, expected_state: Type[State], ): dc_cable_check = CableCheck(self.comm_session) - dc_cable_check.cable_check_req_was_received = cable_check_req_received - dc_cable_check.contactors_closed_for_cable_check = is_contactor_closed dc_cable_check.cable_check_started = cable_check_started + dc_cable_check.contactors_closed = is_contactor_closed contactor_status = AsyncMock(return_value=is_contactor_closed) self.comm_session.evse_controller.is_contactor_closed = contactor_status cable_check_status = AsyncMock(return_value=cable_check_status) diff --git a/tests/iso15118_20/secc/test_iso15118_20_dc_states.py b/tests/iso15118_20/secc/test_iso15118_20_dc_states.py index b2ef0ee9..62bc9c37 100644 --- a/tests/iso15118_20/secc/test_iso15118_20_dc_states.py +++ b/tests/iso15118_20/secc/test_iso15118_20_dc_states.py @@ -207,45 +207,39 @@ async def test_15118_20_schedule_exchange_res( assert schedule_exchange.next_state is None @pytest.mark.parametrize( - "cable_check_req_received, " "is_contactor_closed, " "cable_check_started, " "cable_check_status, " "expected_state", [ - (False, None, False, None, None), # First request. + (None, False, None, None), # First request. ( - True, None, False, None, None, ), # Not first request. Contactor status unknown. - (True, True, False, None, None), # Not first request. Contactor closed. - (True, False, False, None, Terminate), # Contactor close failed. + (True, False, None, None), # Not first request. Contactor closed. + (False, False, None, Terminate), # Contactor close failed. ( - True, True, True, IsolationLevel.VALID, DCPreCharge, ), # noqa Contactor closed. Isolation response received - Valid. Next stage Precharge. ( - True, True, True, IsolationLevel.INVALID, Terminate, ), # noqa Contactor closed. Isolation response received - Invalid. Terminate. ( - True, True, True, IsolationLevel.WARNING, DCPreCharge, ), # noqa Contactor closed. Isolation response received - Warning. Next stage Precharge. ( - True, True, True, IsolationLevel.FAULT, @@ -255,16 +249,14 @@ async def test_15118_20_schedule_exchange_res( ) async def test_15118_20_dc_cable_check( self, - cable_check_req_received: bool, is_contactor_closed: bool, cable_check_started: bool, cable_check_status: IsolationLevel, expected_state: Type[State], ): dc_cable_check = DCCableCheck(self.comm_session) - dc_cable_check.cable_check_req_was_received = cable_check_req_received - dc_cable_check.contactors_closed_for_cable_check = is_contactor_closed dc_cable_check.cable_check_started = cable_check_started + dc_cable_check.contactors_closed = is_contactor_closed contactor_status = AsyncMock(return_value=is_contactor_closed) self.comm_session.evse_controller.is_contactor_closed = contactor_status cable_check_status = AsyncMock(return_value=cable_check_status)