Skip to content

Commit

Permalink
Cs contactor (#63)
Browse files Browse the repository at this point in the history
* improving contactor open/close
* tests are added for contactor open/close
* reformatted
  • Loading branch information
ikaratass authored Jun 17, 2022
1 parent 728b175 commit 1b0f4bc
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 32 deletions.
Empty file modified iso15118/evcc/main.py
100644 → 100755
Empty file.
5 changes: 3 additions & 2 deletions iso15118/secc/controller/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ async def get_service_parameter_list(
async def stop_charger(self) -> None:
raise NotImplementedError

async def open_contactor(self):
@abstractmethod
async def open_contactor(self) -> Contactor:
"""
Sends a command to the SECC to open the contactor to terminate energy flow
Expand All @@ -350,7 +351,7 @@ async def open_contactor(self):
raise NotImplementedError

@abstractmethod
async def close_contactor(self):
async def close_contactor(self) -> Contactor:
"""
Sends a command to the SECC to open the contactor to initiate energy flow
Expand Down
6 changes: 4 additions & 2 deletions iso15118/secc/controller/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,13 +522,15 @@ async def service_renegotiation_supported(self) -> bool:
"""Overrides EVSEControllerInterface.service_renegotiation_supported()."""
return False

async def close_contactor(self):
async def close_contactor(self) -> Contactor:
"""Overrides EVSEControllerInterface.close_contactor()."""
self.contactor = Contactor.CLOSED
return self.contactor

async def open_contactor(self):
async def open_contactor(self) -> Contactor:
"""Overrides EVSEControllerInterface.open_contactor()."""
self.contactor = Contactor.OPENED
return self.contactor

async def get_contactor_state(self) -> Contactor:
"""Overrides EVSEControllerInterface.get_contactor_state()."""
Expand Down
10 changes: 10 additions & 0 deletions iso15118/secc/states/din_spec_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from iso15118.shared.messages.enums import (
AuthEnum,
AuthorizationStatus,
Contactor,
DCEVErrorCode,
EVSEProcessing,
IsolationLevel,
Expand Down Expand Up @@ -644,6 +645,15 @@ async def process_message(
"WeldingDetectionReq/SessionStopReq"
)
next_state = None
await self.comm_session.evse_controller.open_contactor()
contactor_state = await self.comm_session.evse_controller.open_contactor()
if contactor_state != Contactor.OPENED:
self.stop_state_machine(
"Contactor didnt open",
message,
ResponseCode.FAILED_CONTACTOR_ERROR,
)
return
await self.comm_session.evse_controller.stop_charger()

dc_evse_status = await self.comm_session.evse_controller.get_dc_evse_status()
Expand Down
7 changes: 3 additions & 4 deletions iso15118/secc/states/iso15118_20_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -980,13 +980,12 @@ async def process_message(
# "Start" within V2G communication session.
# TODO: We may need to check the CP state is C or D before
# closing the contactors.
await self.comm_session.evse_controller.close_contactor()
contactor_state = (
await self.comm_session.evse_controller.get_contactor_state()
await self.comm_session.evse_controller.close_contactor()
)
if contactor_state == Contactor.OPENED:
if contactor_state != Contactor.CLOSED:
self.stop_state_machine(
"Contactor is still open when about to send PowerDeliveryRes",
"Contactor didnt close",
message,
ResponseCode.FAILED_CONTACTOR_ERROR,
)
Expand Down
51 changes: 28 additions & 23 deletions iso15118/secc/states/iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,22 @@ async def process_message(
next_state: Type[State]
if power_delivery_req.charge_progress == ChargeProgress.START:
await self.comm_session.evse_controller.set_hlc_charging(True)
# [V2G2-847] - The EV shall signal CP State C or D no later than 250ms
# after sending the first PowerDeliveryReq with ChargeProgress equals
# "Start" within V2G Communication SessionPowerDeliveryReq.
# [V2G2-860] - If no error is detected, the SECC shall close the Contactor
# no later than 3s after measuring CP State C or D.
# Before closing the contactor, we may need to check to
# ensure the CP is in state C or D
contactor_state = await self.comm_session.evse_controller.close_contactor()
if contactor_state != Contactor.CLOSED:
self.stop_state_machine(
"Contactor didnt close",
message,
ResponseCode.FAILED_CONTACTOR_ERROR,
)
return

if self.comm_session.selected_charging_type_is_ac:
next_state = ChargingStatus
else:
Expand All @@ -1279,14 +1295,20 @@ async def process_message(
power_delivery_req.sa_schedule_tuple_id
)
self.comm_session.charge_progress_started = True
elif (
power_delivery_req.charge_progress == ChargeProgress.STOP
and self.comm_session.selected_charging_type_is_ac
):
await self.comm_session.evse_controller.set_hlc_charging(False)
next_state = SessionStop
elif power_delivery_req.charge_progress == ChargeProgress.STOP:
next_state = None
if self.comm_session.selected_charging_type_is_ac:
await self.comm_session.evse_controller.set_hlc_charging(False)
next_state = SessionStop

contactor_state = await self.comm_session.evse_controller.open_contactor()
if contactor_state != Contactor.OPENED:
self.stop_state_machine(
"Contactor didnt open",
message,
ResponseCode.FAILED_CONTACTOR_ERROR,
)
return
await self.comm_session.evse_controller.stop_charger()
else:
# ChargeProgress only has three enum values: Start, Stop, and
Expand All @@ -1304,23 +1326,6 @@ async def process_message(
)
return

# [V2G2-847] - The EV shall signal CP State C or D no later than 250ms
# after sending the first PowerDeliveryReq with ChargeProgress equals
# "Start" within V2G Communication SessionPowerDeliveryReq.
# [V2G2-860] - If no error is detected, the SECC shall close the Contactor
# no later than 3s after measuring CP State C or D.
# Before closing the contactor, we may need to check to
# ensure the CP is in state C or D
await self.comm_session.evse_controller.close_contactor()
contactor_state = await self.comm_session.evse_controller.get_contactor_state()
if contactor_state == Contactor.OPENED:
self.stop_state_machine(
"Contactor is still open when about to send PowerDeliveryRes",
message,
ResponseCode.FAILED_CONTACTOR_ERROR,
)
return

ac_evse_status: Optional[ACEVSEStatus] = None
dc_evse_status: Optional[DCEVSEStatus] = None
if self.comm_session.selected_charging_type_is_ac:
Expand Down
1 change: 1 addition & 0 deletions iso15118/shared/messages/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,5 +416,6 @@ class PriceAlgorithm(str, Enum):


class Contactor(IntEnum):
ERROR = 0
OPENED = 1
CLOSED = 2
34 changes: 33 additions & 1 deletion tests/secc/states/test_iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
WeldingDetection,
)
from iso15118.secc.states.secc_state import StateSECC
from iso15118.shared.messages.enums import AuthEnum, AuthorizationStatus
from iso15118.shared.messages.enums import AuthEnum, AuthorizationStatus, Contactor
from tests.secc.states.test_messages import (
get_charge_parameter_discovery_req_message_departure_time_one_hour,
get_charge_parameter_discovery_req_message_no_departure_time,
get_dummy_v2g_message_authorization_req,
get_dummy_v2g_message_power_delivery_req_charge_start,
get_dummy_v2g_message_power_delivery_req_charge_stop,
get_dummy_v2g_message_welding_detection_req,
get_v2g_message_power_delivery_req,
)
Expand Down Expand Up @@ -204,3 +206,33 @@ async def test_charge_parameter_discovery_res_v2g2_761(self):
break

assert found_entry_indicating_start_without_delay is True

async def test_power_delivery_contactor_close(
self,
):
power_delivery = PowerDelivery(self.comm_session)
await power_delivery.process_message(
message=get_dummy_v2g_message_power_delivery_req_charge_start()
)
assert self.comm_session.evse_controller.contactor is Contactor.CLOSED

async def test_power_delivery_contactor_open(
self,
):
power_delivery = PowerDelivery(self.comm_session)
await power_delivery.process_message(
message=get_dummy_v2g_message_power_delivery_req_charge_stop()
)
assert self.comm_session.evse_controller.contactor is Contactor.OPENED

async def test_power_delivery_contactor_get_state(
self,
):
power_delivery = PowerDelivery(self.comm_session)
await power_delivery.process_message(
message=get_dummy_v2g_message_power_delivery_req_charge_start()
)
assert (
await self.comm_session.evse_controller.get_contactor_state()
is Contactor.CLOSED
)
24 changes: 24 additions & 0 deletions tests/secc/states/test_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,27 @@ def get_charge_parameter_discovery_req_message_no_departure_time():
header=MessageHeader(session_id=MOCK_SESSION_ID),
body=Body(charge_parameter_discovery_req=charge_parameter_discovery_req),
)


def get_dummy_v2g_message_power_delivery_req_charge_start():
power_delivery_req = PowerDeliveryReq(
charge_progress=ChargeProgress.START,
sa_schedule_tuple_id=1,
)

return V2GMessage(
header=MessageHeader(session_id=MOCK_SESSION_ID),
body=Body(power_delivery_req=power_delivery_req),
)


def get_dummy_v2g_message_power_delivery_req_charge_stop():
power_delivery_req = PowerDeliveryReq(
charge_progress=ChargeProgress.STOP,
sa_schedule_tuple_id=1,
)

return V2GMessage(
header=MessageHeader(session_id=MOCK_SESSION_ID),
body=Body(power_delivery_req=power_delivery_req),
)

0 comments on commit 1b0f4bc

Please sign in to comment.