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

feat: interface added for pause and terminate #155

Merged
merged 10 commits into from
Nov 21, 2022
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
12 changes: 12 additions & 0 deletions iso15118/secc/controller/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
CpState,
EnergyTransferModeEnum,
Protocol,
SessionStopAction,
)
from iso15118.shared.messages.iso15118_2.datatypes import (
ACEVSEChargeParameter,
Expand Down Expand Up @@ -699,3 +700,14 @@ async def get_15118_ev_certificate(
- ISO 15118-20 and ISO 15118-2
"""
raise NotImplementedError

@abstractmethod
async def update_data_link(self, action: SessionStopAction) -> None:
"""
Called when EV requires termination or pausing of the charging session.
Args:
action : SessionStopAction
Relevant for:
- ISO 15118-20 and ISO 15118-2
"""
raise NotImplementedError
7 changes: 7 additions & 0 deletions iso15118/secc/controller/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
Namespace,
PriceAlgorithm,
Protocol,
SessionStopAction,
UnitSymbol,
)
from iso15118.shared.messages.iso15118_2.body import Body, CertificateInstallationRes
Expand Down Expand Up @@ -910,3 +911,9 @@ async def get_15118_ev_certificate(
).decode("utf-8")

return base64_encode_cert_install_res

async def update_data_link(self, action: SessionStopAction) -> None:
"""
Overrides EVSEControllerInterface.update_data_link().
"""
pass
10 changes: 7 additions & 3 deletions iso15118/secc/states/iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
CertificateChain,
ChargeProgress,
ChargeService,
ChargingSession,
DHPublicKey,
EncryptedPrivateKey,
EnergyTransferModeList,
Expand Down Expand Up @@ -123,7 +124,7 @@
verify_certs,
verify_signature,
)
from iso15118.shared.states import Base64, State, Terminate
from iso15118.shared.states import Base64, Pause, State, Terminate

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -1869,9 +1870,12 @@ async def process_message(
f"EV Requested to {session_status} the communication session",
self.comm_session.writer.get_extra_info("peername"),
)

if msg.body.session_stop_req.charging_session == ChargingSession.PAUSE:
next_state = Pause
else:
next_state = Terminate
self.create_next_message(
Terminate,
next_state,
SessionStopRes(response_code=ResponseCode.OK),
Timeouts.V2G_SECC_SEQUENCE_TIMEOUT,
Namespace.ISO_V2_MSG_DEF,
Expand Down
11 changes: 7 additions & 4 deletions iso15118/shared/comm_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
ISOV20PayloadTypes,
Namespace,
Protocol,
SessionStopAction,
)
from iso15118.shared.messages.iso15118_2.datatypes import EnergyTransferModeEnum
from iso15118.shared.messages.iso15118_2.msgdef import V2GMessage as V2GMessageV2
Expand Down Expand Up @@ -380,9 +381,9 @@ async def stop(self, reason: str):
"""
if self.current_state.next_state == Pause:
self.save_session_info()
terminate_or_pause = "Pause"
terminate_or_pause = SessionStopAction.PAUSE
else:
terminate_or_pause = "Terminate"
terminate_or_pause = SessionStopAction.TERMINATE

logger.info(
f"The data link will {terminate_or_pause} in 2 seconds and "
Expand All @@ -391,8 +392,10 @@ async def stop(self, reason: str):
logger.info(f"Reason: {reason}")

await asyncio.sleep(2)
# TODO Signal data link layer to either terminate or pause the data
# link connection
# Signal data link layer to either terminate or pause the data
# link connection
if hasattr(self.comm_session, "evse_controller"):
await self.comm_session.evse_controller.update_data_link(terminate_or_pause)
logger.info(f"{terminate_or_pause}d the data link")
await asyncio.sleep(3)
try:
Expand Down
5 changes: 5 additions & 0 deletions iso15118/shared/messages/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,3 +436,8 @@ class CpState(str, Enum):
E = "E"
F = "F"
UNKNOWN = "UNKNOWN"


class SessionStopAction(str, Enum):
TERMINATE = "terminate"
PAUSE = "pause"
42 changes: 25 additions & 17 deletions tests/secc/states/test_iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
PaymentDetails,
PowerDelivery,
ServiceDiscovery,
SessionStop,
Terminate,
WeldingDetection,
)
Expand All @@ -25,8 +26,13 @@
EVSEProcessing,
)
from iso15118.shared.messages.iso15118_2.body import ResponseCode
from iso15118.shared.messages.iso15118_2.datatypes import ACEVSEStatus, CertificateChain
from iso15118.shared.messages.iso15118_2.datatypes import (
ACEVSEStatus,
CertificateChain,
ChargingSession,
)
from iso15118.shared.security import get_random_bytes
from iso15118.shared.states import Pause
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,
Expand All @@ -38,6 +44,7 @@
get_dummy_v2g_message_power_delivery_req_charge_stop,
get_dummy_v2g_message_service_discovery_req,
get_dummy_v2g_message_welding_detection_req,
get_dummy_v2g_session_stop_req,
get_power_delivery_req_charging_profile_in_boundary_invalid,
get_power_delivery_req_charging_profile_in_limits,
get_power_delivery_req_charging_profile_not_in_limits_span_over_sa,
Expand All @@ -55,6 +62,8 @@ def _comm_session(self, comm_secc_session_mock):
self.comm_session = comm_secc_session_mock
self.comm_session.config = Config()
self.comm_session.is_tls = False
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()

async def test_current_demand_to_power_delivery_when_power_delivery_received(
self,
Expand Down Expand Up @@ -104,8 +113,6 @@ async def test_payment_details_next_state_on_payment_details_req_auth(
is_authorized_return_value: AuthorizationStatus,
expected_next_state: StateSECC,
):
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()

self.comm_session.selected_auth_option = AuthEnum.PNC_V2
mock_is_authorized = AsyncMock(return_value=is_authorized_return_value)
Expand Down Expand Up @@ -183,8 +190,6 @@ async def test_authorization_next_state_on_authorization_request(
expected_response_code: ResponseCode,
expected_evse_processing: EVSEProcessing,
):
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()

self.comm_session.selected_auth_option = auth_type
mock_is_authorized = AsyncMock(return_value=is_authorized_return_value)
Expand All @@ -211,8 +216,6 @@ async def test_authorization_next_state_on_authorization_request(
)

async def test_authorization_req_gen_challenge_invalid(self):
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()
self.comm_session.selected_auth_option = AuthEnum.PNC_V2
self.comm_session.contract_cert_chain = Mock()
self.comm_session.gen_challenge = get_random_bytes(16)
Expand All @@ -230,8 +233,6 @@ async def test_authorization_req_gen_challenge_invalid(self):
)

async def test_authorization_req_gen_challenge_valid(self):
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()
self.comm_session.selected_auth_option = AuthEnum.PNC_V2
self.comm_session.gen_challenge = get_random_bytes(16)
id = "aReq"
Expand Down Expand Up @@ -455,8 +456,6 @@ async def test_power_delivery_state_c(
):

power_delivery = PowerDelivery(self.comm_session)
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()
mock_get_cp_state = AsyncMock(return_value=get_state_return_value)
self.comm_session.evse_controller.get_cp_state = mock_get_cp_state
await power_delivery.process_message(
Expand All @@ -467,8 +466,6 @@ async def test_power_delivery_state_c(
async def test_service_discovery_req_unexpected_state(self):
self.comm_session.selected_auth_option = AuthEnum.PNC_V2
self.comm_session.config.free_charging_service = False
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()
service_discovery = ServiceDiscovery(self.comm_session)
await service_discovery.process_message(
message=get_dummy_v2g_message_service_discovery_req()
Expand All @@ -484,8 +481,6 @@ async def test_service_discovery_req_unexpected_state(self):

async def test_charging_status_evse_status(self):
charging_status = ChargingStatus(self.comm_session)
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()
self.comm_session.selected_schedule = 1
await charging_status.process_message(message=get_dummy_charging_status_req())

Expand All @@ -498,8 +493,6 @@ async def test_charging_status_evse_status(self):

async def test_charging_status_evse_status_altered(self):
charging_status = ChargingStatus(self.comm_session)
self.comm_session.writer = Mock()
self.comm_session.writer.get_extra_info = Mock()
self.comm_session.selected_schedule = 1

async def get_ac_evse_status_patch():
Expand All @@ -513,3 +506,18 @@ async def get_ac_evse_status_patch():
await charging_status.process_message(message=get_dummy_charging_status_req())
charging_status_res = charging_status.message.body.charging_status_res
assert charging_status_res.ac_evse_status == await get_ac_evse_status_patch()

@pytest.mark.parametrize(
"charging_session, expected_next_state",
[
(ChargingSession.PAUSE, Pause),
(ChargingSession.TERMINATE, Terminate),
],
)
async def test_session_stop_req(self, charging_session, expected_next_state):
# V2G2-718
session_stop = SessionStop(self.comm_session)
await session_stop.process_message(
message=get_dummy_v2g_session_stop_req(charging_session)
)
assert session_stop.next_state == expected_next_state
9 changes: 9 additions & 0 deletions tests/secc/states/test_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,12 @@ def get_dummy_charging_status_req() -> V2GMessage:
header=MessageHeader(session_id=MOCK_SESSION_ID),
body=Body(charging_status_req=charging_status_req),
)


def get_dummy_v2g_session_stop_req(charging_session: ChargingSession) -> V2GMessage:
session_stop_req = SessionStopReq(charging_session=charging_session)

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