Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 2c30797
Author: Ibrahim KARATAS <[email protected]>
Date:   Mon Oct 31 17:19:10 2022 +0000

    Fix/genchallange invalid (#154)

    * genchallance will only sent on first AuthorizationReq message

    * test_authorization_req_gen_challenge_invalid unit test modified

commit 0d34ee5
Author: Ibrahim KARATAS <[email protected]>
Date:   Mon Oct 31 16:36:56 2022 +0000

    genchallange check has been added for Authorization (#135)

    * The genchallange check has been added for Authorization

commit 6192609
Author: Chad <[email protected]>
Date:   Mon Oct 31 14:43:25 2022 +0000

    fix: cleanup template dockerfile (#109)

commit 5185483
Author: Roman Stanchak <[email protected]>
Date:   Mon Oct 24 05:35:44 2022 -0400

    fix: use utcnow() to check certificate validity (#151)

commit 72e8cc9
Author: Chad <[email protected]>
Date:   Mon Oct 17 17:29:49 2022 +0100

    feat: run code qual and tests in gha (#147)

    * feat: run code qual and tests in gha

    * fix: install deps in gha

    * fix: add isort make command

commit 8718dd5
Author: santiagosalamandri <[email protected]>
Date:   Mon Oct 17 15:02:36 2022 +0100

    Bump 0.13.0 (#149)

    * feat: Bump to 0.13.0

commit d596370
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 13:59:40 2022 +0100

    feat: Pass status event as a parameter to start servers. Reduce check status delay to 10 mS

commit 8d260e2
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 13:56:38 2022 +0100

    feat: Remove event atribute and pass it as a parameter

commit da2ee49
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 13:55:04 2022 +0100

    chore: rename EVSEServiceStatus to ServiceStatus

commit 3898096
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 12:11:47 2022 +0100

    feat: Add starting status

commit 657d451
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 12:11:11 2022 +0100

    feat: Add a task to check the servers status

commit 222b741
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 12:09:30 2022 +0100

    feat: Add ready status events to servers

commit f196eb6
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 12:08:21 2022 +0100

    feat: Implemented set_status method

commit c076bb2
Author: Santiago Salamandri <[email protected]>
Date:   Mon Oct 17 12:07:38 2022 +0100

    feat: add evse status enum and set_status abstract method

commit b0e4f25
Author: Chad <[email protected]>
Date:   Wed Oct 12 13:54:56 2022 +0100

    fix: remove sphinx dependency (#141)

commit 15c3a5a
Author: André <[email protected]>
Date:   Wed Oct 12 11:39:27 2022 +0100

    get from the evse controller the ac evse status (#146)

    * get from the evse controller the ac evse status

    * removed unused iimport

    * added test for charging status
  • Loading branch information
shalinnijel2 committed Nov 2, 2022
1 parent 17aef6d commit 0ca196f
Show file tree
Hide file tree
Showing 19 changed files with 400 additions and 474 deletions.
4 changes: 1 addition & 3 deletions .github/actions/setup-python-build-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,4 @@ runs:
python -m pip install --upgrade pip
- name: Install Poetry
shell: bash
run: |
pip install --user poetry
pip install "mypy==0.910"
run: pip install --user poetry
23 changes: 18 additions & 5 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,21 @@ jobs:
run: |
cp .env.dev.local .env
- name: Code Quality Check
shell: bash
run: |
cp template.Dockerfile iso15118/secc/Dockerfile
docker-compose build secc
- name: Install dependencies
run: make install-local

# TODO: Fix MyPy issues https://github.com/SwitchEV/iso15118/issues/93
# - name: Mypy
# run: make mypy

- name: Black
run: make black

- name: Flake8
run: make flake8

- name: isort
run: make isort

- name: Tests
run: make test
6 changes: 0 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ instance/
# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

Expand Down Expand Up @@ -104,9 +101,6 @@ venv.bak/
# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.13.0] - 2022-10-17

### Fixed

* Fix/serviceDiscoveryreq is not allowed after receiving first one by @ikaratass in https://github.com/SwitchEV/iso15118/pull/143
* fix: remove sphinx dependency by @mdwcrft in https://github.com/SwitchEV/iso15118/pull/141
* Fix: create_certs to generate jks certs for Keysight by @shalinnijel2 in https://github.com/SwitchEV/iso15118/pull/134
### Added

* feat: Add Service status in https://github.com/SwitchEV/iso15118/pull/148
* get from the evse controller the ac evse status by @tropxy in https://github.com/SwitchEV/iso15118/pull/146

## [0.12.0] - 2022-10-03

### Changed
Expand Down
14 changes: 9 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -97,23 +97,27 @@ test-secc:

# Run pytest
test:
pytest tests
poetry run pytest -vv --cov-config .coveragerc --cov-report term-missing --durations=3 --cov=.

# Run mypy checks
mypy:
mypy --config-file mypy.ini iso15118 tests
poetry run mypy --config-file mypy.ini iso15118 tests

# Reformat with isort and black
reformat:
isort iso15118 tests && black --line-length=88 iso15118 tests
poetry run isort iso15118 tests && poetry run black --line-length=88 iso15118 tests

# Run black checks
black:
black --check --diff --line-length=88 iso15118 tests
poetry run black --check --diff --line-length=88 iso15118 tests

# Run isort checks
isort:
poetry run isort --check-only iso15118 tests

# Run flake8 checks
flake8:
flake8 --config .flake8 iso15118 tests
poetry run flake8 --config .flake8 iso15118 tests

# Run black, isort, mypy, & flake8
code-quality: reformat mypy flake8
Expand Down
2 changes: 1 addition & 1 deletion iso15118/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.12.0"
__version__ = "0.13.0"
42 changes: 37 additions & 5 deletions iso15118/secc/comm_session_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from dataclasses import dataclass
from typing import Coroutine, 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 @@ -103,7 +103,7 @@ def __init__(
self.selected_auth_option: Optional[AuthEnum] = None
# The generated challenge sent in PaymentDetailsRes. Its copy is expected in
# AuthorizationReq (applies to Plug & Charge identification mode only)
self.gen_challenge: bytes = bytes(0)
self.gen_challenge: Optional[bytes] = None
# In ISO 15118-2, the EVCCID is the MAC address, given as bytes.
# In ISO 15118-20, the EVCCID is like a VIN number, given as str.
self.evcc_id: Union[bytes, str, None] = None
Expand Down Expand Up @@ -180,6 +180,9 @@ def __init__(
self.evse_controllers = evse_controllers
self.list_of_tasks: List[Coroutine] = []

# 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 @@ -200,19 +203,27 @@ async def __init__.
constructor.
"""

self.list_of_tasks.append(self.check_status_task())
for _, controller in self.evse_controllers.items():
rcv_queue = asyncio.Queue()
iface = controller.config.cs_config.network_interface
udp_ready_event: asyncio.Event = asyncio.Event()
tls_ready_event: asyncio.Event = asyncio.Event()
self.status_event_list.append([udp_ready_event, tls_ready_event])

udp_server = UDPServer(rcv_queue, iface)
tcp_server = TCPServer(rcv_queue, iface)

tasks = [
self.get_from_rcv_queue(rcv_queue),
udp_server.start(),
tcp_server.start_tls(),
udp_server.start(udp_ready_event),
tcp_server.start_tls(tls_ready_event),
]

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

self.list_of_tasks.extend(tasks)
self.evse_connections[iface] = EVSEConnection(
Expand All @@ -226,6 +237,27 @@ async def __init__.

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
17 changes: 17 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.secc.controller.evse_config import EVSEConfig
Expand Down Expand Up @@ -73,6 +74,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 @@ -93,6 +102,7 @@ def reset_ev_data_context(self):
# | COMMON FUNCTIONS (FOR ALL ENERGY TRANSFER MODES) |
# ============================================================================


@abstractmethod
async def get_evse_id(self) -> str:
"""
Expand All @@ -106,6 +116,13 @@ async def get_evse_id(self) -> str:
"""
raise NotImplementedError

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

@abstractmethod
async def get_supported_energy_transfer_modes(
self, protocol: Protocol
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 @@ -16,6 +16,7 @@
EVChargeParamsLimits,
EVDataContext,
EVSEControllerInterface,
ServiceStatus,
)
from iso15118.shared.exceptions import EncryptionError, PrivateKeyReadError
from iso15118.shared.exi_codec import EXI
Expand Down Expand Up @@ -199,6 +200,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) -> str:
"""Overrides EVSEControllerInterface.get_evse_id()."""
Expand Down
2 changes: 1 addition & 1 deletion iso15118/secc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from iso15118.secc import SECCHandler
from iso15118.secc.controller.evse_config import build_evse_configs
from iso15118.secc.controller.interface import ServiceStatus
from iso15118.secc.controller.simulator import SimEVSEController
from iso15118.secc.secc_settings import Config
from iso15118.shared.exificient_exi_codec import ExificientEXICodec
Expand All @@ -29,7 +30,6 @@ async def main():
evse_controllers = await build_evse_controllers(
config.cs_config_file_path, config.cs_limits_file_path
)

await SECCHandler(
config=config, evse_controllers=evse_controllers, exi_codec=ExificientEXICodec()
).start()
Expand Down
31 changes: 19 additions & 12 deletions iso15118/secc/states/iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@
SupportedAppProtocolReq,
SupportedAppProtocolRes,
)
from iso15118.shared.messages.datatypes import (
DCEVSEChargeParameter,
DCEVSEStatus,
EVSENotification,
)
from iso15118.shared.messages.datatypes import DCEVSEChargeParameter, DCEVSEStatus
from iso15118.shared.messages.din_spec.msgdef import V2GMessage as V2GMessageDINSPEC
from iso15118.shared.messages.enums import (
AuthEnum,
Expand Down Expand Up @@ -916,10 +912,10 @@ async def process_message(
AuthorizationStatus.ACCEPTED,
AuthorizationStatus.ONGOING,
]:

self.comm_session.gen_challenge = get_random_bytes(16)
payment_details_res = PaymentDetailsRes(
response_code=ResponseCode.OK,
gen_challenge=get_random_bytes(16),
gen_challenge=self.comm_session.gen_challenge,
evse_timestamp=time.time(),
)

Expand Down Expand Up @@ -1031,6 +1027,20 @@ async def process_message(

if not self.signature_verified_once:
self.signature_verified_once = True

# [V2G2-475] The message 'AuthorizationRes' shall contain
# the ResponseCode 'FAILED_ChallengeInvalid' if the challenge
# response contained in the AuthorizationReq message in attribute
# GenChallenge is not valid versus the provided GenChallenge
# in PaymentDetailsRes.
if authorization_req.gen_challenge != self.comm_session.gen_challenge:
self.stop_state_machine(
"[V2G2-475] GenChallenge is not the same in PaymentDetailsRes",
message,
ResponseCode.FAILED_CHALLENGE_INVALID,
)
return

if not verify_signature(
signature=msg.header.signature,
elements_to_sign=[
Expand Down Expand Up @@ -1823,15 +1833,12 @@ async def process_message(

# We don't care about signed meter values from the EVCC, but if you
# do, then set receipt_required to True and set the field meter_info
evse_controller = self.comm_session.evse_controller
charging_status_res = ChargingStatusRes(
response_code=ResponseCode.OK,
evse_id=await self.comm_session.evse_controller.get_evse_id(),
sa_schedule_tuple_id=self.comm_session.selected_schedule,
ac_evse_status=ACEVSEStatus(
notification_max_delay=0,
evse_notification=EVSENotification.NONE,
rcd=False,
),
ac_evse_status=await evse_controller.get_ac_evse_status(),
# TODO Could maybe request an OCPP setting that determines
# whether or not a receipt is required and when
# (probably only makes sense at the beginning and end of
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 @@ -87,7 +87,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 @@ -104,6 +104,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
2 changes: 1 addition & 1 deletion iso15118/shared/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ def check_validity(certs: List[Certificate]):
Raises:
CertNotYetValidError, CertExpiredError
"""
now = datetime.now()
now = datetime.utcnow()
for cert in certs:
if cert.not_valid_before > now:
raise CertNotYetValidError(cert.subject.__str__())
Expand Down
Loading

0 comments on commit 0ca196f

Please sign in to comment.