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/service_status/ab#2950 #148

Merged
merged 8 commits into from
Oct 17, 2022
40 changes: 36 additions & 4 deletions iso15118/secc/comm_session_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from asyncio.streams import StreamReader, StreamWriter
from typing import Dict, List, Optional, Tuple, Union

from iso15118.secc.controller.interface import EVSEControllerInterface
from iso15118.secc.controller.interface import EVSEControllerInterface, ServiceStatus
from iso15118.secc.failed_responses import (
init_failed_responses_din_spec_70121,
init_failed_responses_iso_v2,
Expand Down Expand Up @@ -168,6 +168,9 @@ def __init__(
self.config = config
self.evse_controller = evse_controller

# List of server status events
self.status_event_list: List[asyncio.Event] = []

# Set the selected EXI codec implementation
EXI().set_exi_codec(codec)

Expand All @@ -189,21 +192,50 @@ async def __init__.
"""

self.udp_server = UDPServer(self._rcv_queue, self.config.iface)
udp_ready_event: asyncio.Event = asyncio.Event()
self.status_event_list.append(udp_ready_event)

self.tcp_server = TCPServer(self._rcv_queue, self.config.iface)
tls_ready_event: asyncio.Event = asyncio.Event()
self.status_event_list.append(tls_ready_event)

self.list_of_tasks = [
self.get_from_rcv_queue(self._rcv_queue),
self.udp_server.start(),
self.tcp_server.start_tls(),
self.udp_server.start(udp_ready_event),
self.tcp_server.start_tls(tls_ready_event),
self.check_status_task(),
]

if not self.config.enforce_tls:
self.list_of_tasks.append(self.tcp_server.start_no_tls())
tcp_ready_event: asyncio.Event = asyncio.Event()
self.status_event_list.append(tcp_ready_event)
self.list_of_tasks.append(self.tcp_server.start_no_tls(tcp_ready_event))

logger.info("Communication session handler started")

await wait_for_tasks(self.list_of_tasks)

def check_events(self) -> bool:
result: bool = True
for event in self.status_event_list:
if event.is_set() is False:
result = False
break
return result

async def check_ready_status(self) -> None:
# Wait until all flags are set
while self.check_events() is False:
await asyncio.sleep(0.01)

async def check_status_task(self) -> None:
try:
await asyncio.wait_for(self.check_ready_status(), timeout=10)
await self.evse_controller.set_status(ServiceStatus.READY)
except asyncio.TimeoutError:
logger.error("Timeout: Servers failed to startup")
await self.evse_controller.set_status(ServiceStatus.ERROR)

async def get_from_rcv_queue(self, queue: asyncio.Queue):
"""
Waits for an incoming message from the transport layer
Expand Down
16 changes: 16 additions & 0 deletions iso15118/secc/controller/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List, Optional, Union

from iso15118.shared.messages.datatypes import (
Expand Down Expand Up @@ -72,6 +73,14 @@ class EVDataContext:
soc: Optional[int] = None # 0-100


class ServiceStatus(str, Enum):
READY = "ready"
STARTING = "starting"
STOPPING = "stopping"
ERROR = "error"
BUSY = "busy"


@dataclass
class EVChargeParamsLimits:
ev_max_voltage: Optional[Union[PVEVMaxVoltageLimit, PVEVMaxVoltage]] = None
Expand All @@ -91,6 +100,13 @@ def reset_ev_data_context(self):
# | COMMON FUNCTIONS (FOR ALL ENERGY TRANSFER MODES) |
# ============================================================================

@abstractmethod
async def set_status(self, status: ServiceStatus) -> None:
"""
Sets the new status for the EVSE Controller
"""
raise NotImplementedError

@abstractmethod
async def get_evse_id(self, protocol: Protocol) -> str:
"""
Expand Down
3 changes: 3 additions & 0 deletions iso15118/secc/controller/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
EVChargeParamsLimits,
EVDataContext,
EVSEControllerInterface,
ServiceStatus,
)
from iso15118.shared.exceptions import EncryptionError, PrivateKeyReadError
from iso15118.shared.exi_codec import EXI
Expand Down Expand Up @@ -195,6 +196,8 @@ def reset_ev_data_context(self):
# ============================================================================
# | COMMON FUNCTIONS (FOR ALL ENERGY TRANSFER MODES) |
# ============================================================================
async def set_status(self, status: ServiceStatus) -> None:
logger.debug(f"New Status: {status}")

async def get_evse_id(self, protocol: Protocol) -> str:
if protocol == Protocol.DIN_SPEC_70121:
Expand Down
2 changes: 2 additions & 0 deletions iso15118/secc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging

from iso15118.secc import SECCHandler
from iso15118.secc.controller.interface import ServiceStatus
from iso15118.secc.controller.simulator import SimEVSEController
from iso15118.shared.exificient_exi_codec import ExificientEXICodec

Expand All @@ -14,6 +15,7 @@ async def main():
the SECC (Supply Equipment Communication Controller)
"""
sim_evse_controller = await SimEVSEController.create()
await sim_evse_controller.set_status(ServiceStatus.STARTING)
await SECCHandler(
exi_codec=ExificientEXICodec(), evse_controller=sim_evse_controller
).start()
Expand Down
12 changes: 7 additions & 5 deletions iso15118/secc/transport/tcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ def __init__(self, session_handler_queue: asyncio.Queue, iface: str) -> None:
while self.port_no_tls == self.port_tls:
self.port_tls = get_tcp_port()

async def start_tls(self):
async def start_tls(self, ready_event: asyncio.Event):
"""
Uses the `server_factory` to start a TLS based server
"""
await self.server_factory(tls=True)
await self.server_factory(ready_event, tls=True)

async def start_no_tls(self):
async def start_no_tls(self, ready_event: asyncio.Event):
"""
Uses the `server_factory` to start a regular TCO based server (No TLS)
"""
await self.server_factory(tls=False)
await self.server_factory(ready_event, tls=False)

async def server_factory(self, tls: bool) -> None:
async def server_factory(self, ready_event: asyncio.Event, tls: bool) -> None:
"""
Factory method to spawn a new server.

Expand Down Expand Up @@ -112,6 +112,8 @@ async def server_factory(self, tls: bool) -> None:
f"port {port}"
)

ready_event.set()

try:
# Shield the task so we can handle the cancellation
# closing the opening connections
Expand Down
3 changes: 2 additions & 1 deletion iso15118/secc/transport/udp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async def __init__.

return sock

async def start(self):
async def start(self, ready_event: asyncio.Event):
"""UDP server tasks to start"""
# Get a reference to the event loop as we plan to use a low-level API
# (see loop.create_datagram_endpoint())
Expand All @@ -99,6 +99,7 @@ async def start(self):
f"{SDP_MULTICAST_GROUP}%{self.iface} "
f"and port {SDP_SERVER_PORT}"
)
ready_event.set()
tasks = [self.rcv_task()]
await wait_for_tasks(tasks)

Expand Down