Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relaxed contactor status check for DC. #402

Merged
merged 4 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion iso15118/secc/controller/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ async def is_contactor_opened(self) -> bool:
raise NotImplementedError

@abstractmethod
async def is_contactor_closed(self) -> bool:
async def is_contactor_closed(self) -> Optional[bool]:
"""
Sends a command to the SECC to get the contactor status is closed

Expand Down
2 changes: 1 addition & 1 deletion iso15118/secc/controller/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ async def service_renegotiation_supported(self) -> bool:
"""Overrides EVSEControllerInterface.service_renegotiation_supported()."""
return False

async def is_contactor_closed(self) -> bool:
async def is_contactor_closed(self) -> Optional[bool]:
"""Overrides EVSEControllerInterface.is_contactor_closed()."""
return True

Expand Down
85 changes: 52 additions & 33 deletions iso15118/secc/states/din_spec_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,9 @@ 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_req_was_received: bool = False
self.cable_check_started: bool = False
self.contactors_closed_for_cable_check: Optional[bool] = None

async def process_message(
self,
Expand Down Expand Up @@ -467,11 +469,58 @@ async def process_message(
)
return

evse_processing: EVSEProcessing = EVSEProcessing.ONGOING
response_code: ResponseCode = ResponseCode.OK
next_state = None
if not self.cable_check_req_was_received:
# First CableCheckReq received. Start cable check.
await self.comm_session.evse_controller.start_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:
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,
]:
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

self.comm_session.evse_controller.ev_data_context.present_soc = (
cable_check_req.dc_ev_status.ev_ress_soc
)
Expand All @@ -483,36 +532,6 @@ async def process_message(
# [V2G-DC-418] Stay in CableCheck state until EVSEProcessing is complete.
# Until EVSEProcessing is completed, EV will send identical
# CableCheckReq message.

evse_processing: EVSEProcessing = EVSEProcessing.ONGOING
response_code: ResponseCode = ResponseCode.OK
next_state: Type["State"] = None
if isolation_level in [
IsolationLevel.VALID,
IsolationLevel.WARNING,
]:
# 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 await self.comm_session.evse_controller.is_contactor_closed():
self.stop_state_machine(
"Contactor didnt close for Cable Check",
message,
ResponseCode.FAILED,
)
return

if isolation_level == IsolationLevel.WARNING:
logger.warning(
"Isolation resistance measured by EVSE is in Warning-Range"
)
next_state = PreCharge
evse_processing = EVSEProcessing.FINISHED
elif isolation_level in [IsolationLevel.FAULT, IsolationLevel.NO_IMD]:
response_code = ResponseCode.FAILED
next_state = Terminate
evse_processing = EVSEProcessing.FINISHED

cable_check_res: CableCheckRes = CableCheckRes(
response_code=response_code,
dc_evse_status=await self.comm_session.evse_controller.get_dc_evse_status(),
Expand Down
53 changes: 31 additions & 22 deletions iso15118/secc/states/iso15118_20_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -1586,6 +1586,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.cable_check_started = False
self.contactors_closed_for_cable_check: Optional[bool] = None

async def process_message(
self,
Expand All @@ -1608,42 +1610,49 @@ async def process_message(
await SessionStop(self.comm_session).process_message(message, message_exi)
return

if not self.cable_check_req_was_received:
# First DCCableCheckReq received. Start cable check.
await self.comm_session.evse_controller.start_cable_check()
self.cable_check_req_was_received = True

next_state = None
processing = EVSEProcessing.ONGOING
isolation_level = (
await self.comm_session.evse_controller.get_cable_check_status()
)

if isolation_level in [IsolationLevel.VALID, IsolationLevel.WARNING]:
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 await self.comm_session.evse_controller.is_contactor_closed():
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:
self.stop_state_machine(
"Contactor didnt close for Cable Check",
message,
ResponseCode.FAILED,
)
return

if isolation_level == IsolationLevel.WARNING:
logger.warning(
"Isolation resistance measured by EVSE is in Warning range"
if self.cable_check_started:
isolation_level = (
await self.comm_session.evse_controller.get_cable_check_status()
)
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

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
else:
await self.comm_session.evse_controller.start_cable_check()
self.cable_check_started = True

dc_cable_check_res = DCCableCheckRes(
header=MessageHeader(
Expand Down
82 changes: 47 additions & 35 deletions iso15118/secc/states/iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -2187,6 +2187,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

async def process_message(
self,
Expand Down Expand Up @@ -2214,52 +2216,62 @@ async def process_message(
)
return

if not self.cable_check_req_was_received:
# First CableCheckReq received. Start cable check.
await self.comm_session.evse_controller.start_cable_check()
self.cable_check_req_was_received = 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

evse_processing = EVSEProcessing.ONGOING
next_state = None
if isolation_level in [
IsolationLevel.VALID,
IsolationLevel.WARNING,
]:
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
if not await self.comm_session.evse_controller.is_contactor_closed():
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:
self.stop_state_machine(
"Contactor didnt close for Cable Check",
message,
ResponseCode.FAILED,
)
return

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,
]:
self.stop_state_machine(
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.NO_IMD,
IsolationLevel.INVALID,
]:
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

self.comm_session.evse_controller.ev_data_context.present_soc = (
cable_check_req.dc_ev_status.ev_ress_soc
)

cable_check_res = CableCheckRes(
response_code=ResponseCode.OK,
Expand Down
Loading