From f7f5af96ad903c7872bc052d9c1a3d7c7ac70e95 Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Thu, 2 May 2024 12:24:13 +0100 Subject: [PATCH 1/8] Updated version numbers --- CHANGELOG.md | 7 +++++++ iso15118/__init__.py | 2 +- pyproject.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0860c06..5f7f2c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.28.0] - 2024-05-02 +* fix typo in SECC interface.py by @M4GNV5 in https://github.com/SwitchEV/iso15118/pull/398 +* Update .env.dev.local, ISO_15118_20_DC is not implemented for use by @lwollinger in https://github.com/SwitchEV/iso15118/pull/395 +* Share cpd params with CS. by @shalinnijel2 in https://github.com/SwitchEV/iso15118/pull/400 +* Share display params with CS. by @shalinnijel2 in https://github.com/SwitchEV/iso15118/pull/401 +* Relaxed contactor status check for DC. by @shalinnijel2 in https://github.com/SwitchEV/iso15118/pull/402 + ## [0.27.0] - 2024-04-17 * ScheduleExchangeRes parsing fix by @heavyweight87 in https://github.com/SwitchEV/iso15118/pull/391 * Moved contactor status check for dc to be after cable check by @shalinnijel2 in https://github.com/SwitchEV/iso15118/pull/396 diff --git a/iso15118/__init__.py b/iso15118/__init__.py index cde6d897..67612e9c 100644 --- a/iso15118/__init__.py +++ b/iso15118/__init__.py @@ -1 +1 @@ -__version__ = "0.27.0" +__version__ = "0.28.1" diff --git a/pyproject.toml b/pyproject.toml index a838a6c6..e4d741b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "iso15118" -version = "0.27.0" +version = "0.28.0" description = "Implementation of DIN SPEC 70121, ISO 15118-2 and -20 specs for SECC" authors = ["André Duarte ", "Dr. Marc Mültin ", From be4e99aa8a89706691b9b8c2dc3170db021f3807 Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Thu, 2 May 2024 12:26:07 +0100 Subject: [PATCH 2/8] Updated version numbers --- iso15118/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iso15118/__init__.py b/iso15118/__init__.py index 67612e9c..df7db864 100644 --- a/iso15118/__init__.py +++ b/iso15118/__init__.py @@ -1 +1 @@ -__version__ = "0.28.1" +__version__ = "0.28.0" From 345e060b849863692579284092b3e07f5d27a06f Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Wed, 8 May 2024 17:34:29 +0100 Subject: [PATCH 3/8] fix contactor status check in cable check --- iso15118/evcc/controller/simulator.py | 20 ++++++++++---------- iso15118/secc/states/iso15118_20_states.py | 11 ++++++----- 2 files changed, 16 insertions(+), 15 deletions(-) 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/iso15118_20_states.py b/iso15118/secc/states/iso15118_20_states.py index a8b1be01..858738f2 100644 --- a/iso15118/secc/states/iso15118_20_states.py +++ b/iso15118/secc/states/iso15118_20_states.py @@ -1613,14 +1613,15 @@ 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 + #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 + if not self.cable_check_started: self.contactors_closed_for_cable_check = ( await self.comm_session.evse_controller.is_contactor_closed() ) - self.cable_check_req_was_received = True + # 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: From 9d85f029926bc42188b46e258eb9244075574b40 Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Fri, 10 May 2024 09:55:36 +0100 Subject: [PATCH 4/8] Updated tests. Updated cable check logic. --- iso15118/secc/states/din_spec_states.py | 4 +--- iso15118/secc/states/iso15118_20_states.py | 3 --- iso15118/secc/states/iso15118_2_states.py | 4 +--- tests/dinspec/secc/test_dinspec_secc_states.py | 14 +++----------- .../secc/states/test_iso15118_2_states.py | 15 +++------------ .../secc/test_iso15118_20_dc_states.py | 14 +++----------- 6 files changed, 11 insertions(+), 43 deletions(-) diff --git a/iso15118/secc/states/din_spec_states.py b/iso15118/secc/states/din_spec_states.py index 3afb80d8..d077d0fc 100644 --- a/iso15118/secc/states/din_spec_states.py +++ b/iso15118/secc/states/din_spec_states.py @@ -438,7 +438,6 @@ 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.cable_check_started: bool = False self.contactors_closed_for_cable_check: Optional[bool] = None @@ -472,14 +471,13 @@ 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: + if not self.contactors_closed_for_cable_check: # 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: diff --git a/iso15118/secc/states/iso15118_20_states.py b/iso15118/secc/states/iso15118_20_states.py index 858738f2..d61bc19f 100644 --- a/iso15118/secc/states/iso15118_20_states.py +++ b/iso15118/secc/states/iso15118_20_states.py @@ -1585,7 +1585,6 @@ 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.cable_check_started = False self.contactors_closed_for_cable_check: Optional[bool] = None @@ -1613,7 +1612,6 @@ 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 @@ -1621,7 +1619,6 @@ async def process_message( 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: diff --git a/iso15118/secc/states/iso15118_2_states.py b/iso15118/secc/states/iso15118_2_states.py index fd35995c..727ca870 100644 --- a/iso15118/secc/states/iso15118_2_states.py +++ b/iso15118/secc/states/iso15118_2_states.py @@ -2186,7 +2186,6 @@ 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 @@ -2219,14 +2218,13 @@ async def process_message( next_state = None evse_processing = EVSEProcessing.ONGOING - if not self.cable_check_req_was_received: + if not self.cable_check_started: # 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: diff --git a/tests/dinspec/secc/test_dinspec_secc_states.py b/tests/dinspec/secc/test_dinspec_secc_states.py index de1dad59..2f5ce756 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,7 +195,6 @@ 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 contactor_status = AsyncMock(return_value=is_contactor_closed) 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..bd722de0 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,14 +1158,12 @@ 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 contactor_status = AsyncMock(return_value=is_contactor_closed) 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..342974cd 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,14 +249,12 @@ 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 contactor_status = AsyncMock(return_value=is_contactor_closed) From 5f5b61270d35e8320e0a2f93599885c15c32443b Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Mon, 13 May 2024 14:06:58 +0100 Subject: [PATCH 5/8] Minor refactor to cable check --- iso15118/secc/states/din_spec_states.py | 72 +++++++++---------- iso15118/secc/states/iso15118_20_states.py | 63 ++++++++-------- iso15118/secc/states/iso15118_2_states.py | 67 +++++++++-------- .../dinspec/secc/test_dinspec_secc_states.py | 1 - .../secc/states/test_iso15118_2_states.py | 1 - .../secc/test_iso15118_20_dc_states.py | 1 - 6 files changed, 96 insertions(+), 109 deletions(-) diff --git a/iso15118/secc/states/din_spec_states.py b/iso15118/secc/states/din_spec_states.py index d077d0fc..74ef8345 100644 --- a/iso15118/secc/states/din_spec_states.py +++ b/iso15118/secc/states/din_spec_states.py @@ -439,7 +439,6 @@ class CableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_SECC_SEQUENCE_TIMEOUT) self.cable_check_started: bool = False - self.contactors_closed_for_cable_check: Optional[bool] = None async def process_message( self, @@ -471,62 +470,57 @@ async def process_message( evse_processing: EVSEProcessing = EVSEProcessing.ONGOING response_code: ResponseCode = ResponseCode.OK next_state = None - if not self.contactors_closed_for_cable_check: + if not self.cable_check_started: # 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 = ( + contactors_closed_for_cable_check: Optional[bool] = ( await self.comm_session.evse_controller.is_contactor_closed() ) - if self.contactors_closed_for_cable_check is not None: - if not self.contactors_closed_for_cable_check: - self.stop_state_machine( - "Contactor didnt close for Cable Check", - 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, - ]: + if contactors_closed_for_cable_check is not None: + if not contactors_closed_for_cable_check: 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 + else: + 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( + f"Isolation Failure: {isolation_level}", + message, + ResponseCode.FAILED, + ) + return 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 d61bc19f..27ca607e 100644 --- a/iso15118/secc/states/iso15118_20_states.py +++ b/iso15118/secc/states/iso15118_20_states.py @@ -1586,7 +1586,6 @@ class DCCableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT) self.cable_check_started = False - self.contactors_closed_for_cable_check: Optional[bool] = None async def process_message( self, @@ -1612,45 +1611,45 @@ async def process_message( next_state = None processing = EVSEProcessing.ONGOING - # 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 not self.cable_check_started: - self.contactors_closed_for_cable_check = ( - await self.comm_session.evse_controller.is_contactor_closed() - ) - - if self.contactors_closed_for_cable_check is not None: - if not self.contactors_closed_for_cable_check: - self.stop_state_machine( - "Contactor didnt close for Cable Check", - 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" - ) - next_state = DCPreCharge - processing = EVSEProcessing.FINISHED - elif isolation_level in [IsolationLevel.INVALID, IsolationLevel.FAULT]: + # 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 + 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 not contactors_closed_for_cable_check: self.stop_state_machine( - f"Isolation Failure: {isolation_level}", + "Contactor didnt close for Cable Check", message, ResponseCode.FAILED, ) return - else: + + # Start cable check as contactors are now closed. await self.comm_session.evse_controller.start_cable_check() self.cable_check_started = True + else: + 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" + ) + 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 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 727ca870..8179daa8 100644 --- a/iso15118/secc/states/iso15118_2_states.py +++ b/iso15118/secc/states/iso15118_2_states.py @@ -2187,7 +2187,6 @@ class CableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_SECC_SEQUENCE_TIMEOUT) self.cable_check_started = False - self.contactors_closed_for_cable_check: Optional[bool] = None async def process_message( self, @@ -2222,50 +2221,48 @@ async def process_message( # 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 = ( + contactors_closed_for_cable_check = ( await self.comm_session.evse_controller.is_contactor_closed() ) - if self.contactors_closed_for_cable_check is not None: - if not self.contactors_closed_for_cable_check: - self.stop_state_machine( - "Contactor didnt close for Cable Check", - 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.NO_IMD, - IsolationLevel.INVALID, - ]: + if contactors_closed_for_cable_check is not None: + if not contactors_closed_for_cable_check: 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 + else: + 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( + f"Isolation Failure: {isolation_level}", + message, + ResponseCode.FAILED, + ) + return 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 2f5ce756..3ab51b4a 100644 --- a/tests/dinspec/secc/test_dinspec_secc_states.py +++ b/tests/dinspec/secc/test_dinspec_secc_states.py @@ -195,7 +195,6 @@ async def test_15118_dinspec_dc_cable_check( ) dc_cable_check = CableCheck(self.comm_session) - dc_cable_check.contactors_closed_for_cable_check = is_contactor_closed dc_cable_check.cable_check_started = cable_check_started contactor_status = AsyncMock(return_value=is_contactor_closed) self.comm_session.evse_controller.is_contactor_closed = contactor_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 bd722de0..bbe16fea 100644 --- a/tests/iso15118_2/secc/states/test_iso15118_2_states.py +++ b/tests/iso15118_2/secc/states/test_iso15118_2_states.py @@ -1164,7 +1164,6 @@ async def test_15118_2_dc_cable_check( expected_state: Type[State], ): dc_cable_check = CableCheck(self.comm_session) - dc_cable_check.contactors_closed_for_cable_check = is_contactor_closed dc_cable_check.cable_check_started = cable_check_started contactor_status = AsyncMock(return_value=is_contactor_closed) self.comm_session.evse_controller.is_contactor_closed = contactor_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 342974cd..32f83dfa 100644 --- a/tests/iso15118_20/secc/test_iso15118_20_dc_states.py +++ b/tests/iso15118_20/secc/test_iso15118_20_dc_states.py @@ -255,7 +255,6 @@ async def test_15118_20_dc_cable_check( expected_state: Type[State], ): dc_cable_check = DCCableCheck(self.comm_session) - dc_cable_check.contactors_closed_for_cable_check = is_contactor_closed dc_cable_check.cable_check_started = cable_check_started contactor_status = AsyncMock(return_value=is_contactor_closed) self.comm_session.evse_controller.is_contactor_closed = contactor_status From a623eee314857c3f2fee82f5351c70ae884ed765 Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Mon, 13 May 2024 14:08:21 +0100 Subject: [PATCH 6/8] Fix flake errors --- iso15118/secc/states/din_spec_states.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iso15118/secc/states/din_spec_states.py b/iso15118/secc/states/din_spec_states.py index 74ef8345..6e4aed85 100644 --- a/iso15118/secc/states/din_spec_states.py +++ b/iso15118/secc/states/din_spec_states.py @@ -474,9 +474,9 @@ async def process_message( # 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 - contactors_closed_for_cable_check: Optional[bool] = ( - await self.comm_session.evse_controller.is_contactor_closed() - ) + 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 not contactors_closed_for_cable_check: From 7fff7b7fe85e2d3935b7771b8629f365a81beda3 Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Thu, 16 May 2024 20:00:07 +0100 Subject: [PATCH 7/8] start_cable_check is called now as we enter the state. --- iso15118/secc/states/din_spec_states.py | 41 ++++++++++-------- iso15118/secc/states/iso15118_20_states.py | 43 +++++++++++-------- iso15118/secc/states/iso15118_2_states.py | 39 +++++++++-------- .../dinspec/secc/test_dinspec_secc_states.py | 1 + .../secc/states/test_iso15118_2_states.py | 1 + .../secc/test_iso15118_20_dc_states.py | 1 + 6 files changed, 72 insertions(+), 54 deletions(-) diff --git a/iso15118/secc/states/din_spec_states.py b/iso15118/secc/states/din_spec_states.py index 6e4aed85..d132c5d7 100644 --- a/iso15118/secc/states/din_spec_states.py +++ b/iso15118/secc/states/din_spec_states.py @@ -438,6 +438,7 @@ class CableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_SECC_SEQUENCE_TIMEOUT) + self.contactors_closed: bool = False self.cable_check_started: bool = False async def process_message( @@ -470,26 +471,12 @@ async def process_message( evse_processing: EVSEProcessing = EVSEProcessing.ONGOING response_code: ResponseCode = ResponseCode.OK next_state = None - if not self.cable_check_started: - # 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 - 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 not contactors_closed_for_cable_check: - self.stop_state_machine( - "Contactor didnt close for Cable Check", - message, - ResponseCode.FAILED, - ) - return + if not self.cable_check_started: + await self.comm_session.evse_controller.start_cable_check() + self.cable_check_started = True - await self.comm_session.evse_controller.start_cable_check() - self.cable_check_started = True - else: + if self.contactors_closed: isolation_level = ( await self.comm_session.evse_controller.get_cable_check_status() ) # noqa @@ -516,6 +503,24 @@ async def process_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 + 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 not contactors_closed_for_cable_check: + self.stop_state_machine( + "Contactor didnt close for Cable Check", + message, + ResponseCode.FAILED, + ) + return + else: + self.contactors_closed = True self.comm_session.evse_controller.ev_data_context.present_soc = ( cable_check_req.dc_ev_status.ev_ress_soc diff --git a/iso15118/secc/states/iso15118_20_states.py b/iso15118/secc/states/iso15118_20_states.py index 27ca607e..c7d438b9 100644 --- a/iso15118/secc/states/iso15118_20_states.py +++ b/iso15118/secc/states/iso15118_20_states.py @@ -1585,6 +1585,7 @@ class DCCableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT) + self.contactors_closed = False self.cable_check_started = False async def process_message( @@ -1612,26 +1613,11 @@ async def process_message( processing = EVSEProcessing.ONGOING if not self.cable_check_started: - # 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 - 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 not contactors_closed_for_cable_check: - self.stop_state_machine( - "Contactor didnt close for Cable Check", - message, - ResponseCode.FAILED, - ) - return + # Start cable check as contactors are now closed. + await self.comm_session.evse_controller.start_cable_check() + self.cable_check_started = True - # Start cable check as contactors are now closed. - await self.comm_session.evse_controller.start_cable_check() - self.cable_check_started = True - else: + if self.contactors_closed: isolation_level = ( await self.comm_session.evse_controller.get_cable_check_status() ) @@ -1650,6 +1636,25 @@ async def process_message( ResponseCode.FAILED, ) return + 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 + 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 not contactors_closed_for_cable_check: + self.stop_state_machine( + "Contactor didnt close for Cable Check", + message, + ResponseCode.FAILED, + ) + return + else: + self.contactors_closed = True 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 8179daa8..7e1f9d83 100644 --- a/iso15118/secc/states/iso15118_2_states.py +++ b/iso15118/secc/states/iso15118_2_states.py @@ -2187,6 +2187,7 @@ class CableCheck(StateSECC): def __init__(self, comm_session: SECCCommunicationSession): super().__init__(comm_session, Timeouts.V2G_SECC_SEQUENCE_TIMEOUT) self.cable_check_started = False + self.contactors_closed = False async def process_message( self, @@ -2218,24 +2219,10 @@ async def process_message( evse_processing = EVSEProcessing.ONGOING if not self.cable_check_started: - # 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 - contactors_closed_for_cable_check = ( - await self.comm_session.evse_controller.is_contactor_closed() - ) + await self.comm_session.evse_controller.start_cable_check() + self.cable_check_started = True - if contactors_closed_for_cable_check is not None: - if not contactors_closed_for_cable_check: - self.stop_state_machine( - "Contactor didnt close for Cable Check", - message, - ResponseCode.FAILED, - ) - return - await self.comm_session.evse_controller.start_cable_check() - self.cable_check_started = True - else: + if self.contactors_closed: isolation_level = ( await self.comm_session.evse_controller.get_cable_check_status() ) # noqa @@ -2263,6 +2250,24 @@ async def process_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 + contactors_closed_for_cable_check = ( + await self.comm_session.evse_controller.is_contactor_closed() + ) + + if contactors_closed_for_cable_check is not None: + if not contactors_closed_for_cable_check: + self.stop_state_machine( + "Contactor didnt close for Cable Check", + message, + ResponseCode.FAILED, + ) + return + else: + self.contactors_closed = 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 3ab51b4a..c93cef89 100644 --- a/tests/dinspec/secc/test_dinspec_secc_states.py +++ b/tests/dinspec/secc/test_dinspec_secc_states.py @@ -196,6 +196,7 @@ async def test_15118_dinspec_dc_cable_check( dc_cable_check = CableCheck(self.comm_session) 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 bbe16fea..785831d6 100644 --- a/tests/iso15118_2/secc/states/test_iso15118_2_states.py +++ b/tests/iso15118_2/secc/states/test_iso15118_2_states.py @@ -1165,6 +1165,7 @@ async def test_15118_2_dc_cable_check( ): dc_cable_check = CableCheck(self.comm_session) 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 32f83dfa..62bc9c37 100644 --- a/tests/iso15118_20/secc/test_iso15118_20_dc_states.py +++ b/tests/iso15118_20/secc/test_iso15118_20_dc_states.py @@ -256,6 +256,7 @@ async def test_15118_20_dc_cable_check( ): dc_cable_check = DCCableCheck(self.comm_session) 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) From 6d0795be902d9a855f991945af5e7cb9bf39ecc7 Mon Sep 17 00:00:00 2001 From: Shalin Nijel <89510971+shalinnijel2@users.noreply.github.com> Date: Mon, 20 May 2024 12:37:00 +0100 Subject: [PATCH 8/8] Changed to positive logic in contactor status check. --- iso15118/secc/states/din_spec_states.py | 7 ++++--- iso15118/secc/states/iso15118_20_states.py | 7 ++++--- iso15118/secc/states/iso15118_2_states.py | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/iso15118/secc/states/din_spec_states.py b/iso15118/secc/states/din_spec_states.py index d132c5d7..63eb341b 100644 --- a/iso15118/secc/states/din_spec_states.py +++ b/iso15118/secc/states/din_spec_states.py @@ -507,20 +507,21 @@ async def process_message( # 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 not contactors_closed_for_cable_check: + if contactors_closed_for_cable_check: + self.contactors_closed = True + else: self.stop_state_machine( "Contactor didnt close for Cable Check", message, ResponseCode.FAILED, ) return - else: - self.contactors_closed = True self.comm_session.evse_controller.ev_data_context.present_soc = ( cable_check_req.dc_ev_status.ev_ress_soc diff --git a/iso15118/secc/states/iso15118_20_states.py b/iso15118/secc/states/iso15118_20_states.py index c7d438b9..46638aa5 100644 --- a/iso15118/secc/states/iso15118_20_states.py +++ b/iso15118/secc/states/iso15118_20_states.py @@ -1641,20 +1641,21 @@ async def process_message( # 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 not contactors_closed_for_cable_check: + if contactors_closed_for_cable_check: + self.contactors_closed = True + else: self.stop_state_machine( "Contactor didnt close for Cable Check", message, ResponseCode.FAILED, ) return - else: - self.contactors_closed = True 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 7e1f9d83..b9d2b4dc 100644 --- a/iso15118/secc/states/iso15118_2_states.py +++ b/iso15118/secc/states/iso15118_2_states.py @@ -2254,20 +2254,21 @@ async def process_message( # 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 contactors_closed_for_cable_check is not None: - if not contactors_closed_for_cable_check: + if contactors_closed_for_cable_check: + self.contactors_closed = True + else: self.stop_state_machine( "Contactor didnt close for Cable Check", message, ResponseCode.FAILED, ) return - else: - self.contactors_closed = True self.comm_session.evse_controller.ev_data_context.present_soc = ( cable_check_req.dc_ev_status.ev_ress_soc