Skip to content

Commit

Permalink
supported energy services are read from json file
Browse files Browse the repository at this point in the history
  • Loading branch information
ikaratass committed Dec 16, 2022
1 parent 52299d5 commit 79d6f30
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 68 deletions.
32 changes: 15 additions & 17 deletions iso15118/evcc/comm_session_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
StopNotification,
UDPPacketNotification,
)
from iso15118.shared.utils import cancel_task, wait_for_tasks
from iso15118.shared.utils import cancel_task, load_requested_protocols, wait_for_tasks

logger = logging.getLogger(__name__)

Expand All @@ -71,12 +71,12 @@ class EVCCCommunicationSession(V2GCommunicationSession):
"""

def __init__(
self,
transport: Tuple[StreamReader, StreamWriter],
session_handler_queue: asyncio.Queue,
evcc_config: EVCCConfig,
iface: str,
ev_controller: EVControllerInterface,
self,
transport: Tuple[StreamReader, StreamWriter],
session_handler_queue: asyncio.Queue,
evcc_config: EVCCConfig,
iface: str,
ev_controller: EVControllerInterface,
):
# Need to import here to avoid a circular import error
# pylint: disable=import-outside-toplevel
Expand Down Expand Up @@ -149,10 +149,7 @@ def create_sap(self) -> Union[SupportedAppProtocolReq, None]:
app_protocols = []
schema_id = 0
priority = 0
supported_protocols = []
for protocols in self.config.supported_protocols:
if protocols.name in list(map(lambda p: p.name, Protocol)):
supported_protocols.append(Protocol[protocols.name])
supported_protocols = load_requested_protocols(self.config.supported_protocols)

# [V2G-DC-618] For DC charging according to DIN SPEC 70121,
# an SDP server shall send an SECC Discovery Response message with Transport
Expand Down Expand Up @@ -261,11 +258,11 @@ class CommunicationSessionHandler:
# pylint: disable=too-many-instance-attributes

def __init__(
self,
config: EVCCConfig,
iface: str,
codec: IEXICodec,
ev_controller: EVControllerInterface,
self,
config: EVCCConfig,
iface: str,
codec: IEXICodec,
ev_controller: EVControllerInterface,
):
self.list_of_tasks = []
self.udp_client = None
Expand Down Expand Up @@ -424,6 +421,7 @@ async def start_comm_session(self, host: IPv6Address, port: int, is_tls: bool):
(self.tcp_client.reader, self.tcp_client.writer),
self._rcv_queue,
self.config,
self.iface,
self.ev_controller,
)

Expand Down Expand Up @@ -489,7 +487,7 @@ async def process_incoming_udp_packet(self, message: UDPPacketNotification):
# The rationale behind this might be that the EV OEM trades convenience
# (the EV driver can always charge) over security.
if (not secc_signals_tls and self.config.enforce_tls) or (
secc_signals_tls and not self.config.use_tls
secc_signals_tls and not self.config.use_tls
):
logger.error(
"Security mismatch, can't initiate communication session."
Expand Down
49 changes: 26 additions & 23 deletions iso15118/evcc/controller/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
DCChargeParameterDiscoveryReqParams,
)
from iso15118.shared.network import get_nic_mac_address
from iso15118.shared.utils import load_requested_energy_services

logger = logging.getLogger(__name__)

Expand All @@ -113,7 +114,9 @@ def __init__(self, evcc_config: Optional[EVCCConfig] = None):
self.precharge_loop_cycles: int = 0
self._charging_is_completed = False
self._soc = 10

self.supported_energy_services = load_requested_energy_services(
self.config.supported_energy_services
)
self.dc_ev_charge_params: DCEVChargeParams = DCEVChargeParams(
dc_max_current_limit=PVEVMaxCurrentLimit(
multiplier=-3, value=32000, unit=UnitSymbol.AMPERE
Expand Down Expand Up @@ -161,7 +164,7 @@ async def get_evcc_id(self, protocol: Protocol, iface: str) -> str:
raise InvalidProtocolError

async def get_energy_transfer_mode(
self, protocol: Protocol
self, protocol: Protocol
) -> EnergyTransferModeEnum:
"""Overrides EVControllerInterface.get_energy_transfer_mode()."""
if protocol == Protocol.DIN_SPEC_70121:
Expand All @@ -170,10 +173,10 @@ async def get_energy_transfer_mode(

async def get_supported_energy_services(self) -> List[ServiceV20]:
"""Overrides EVControllerInterface.get_energy_transfer_service()."""
return self.config.supported_energy_services
return self.supported_energy_services

async def select_energy_service_v20(
self, services: List[MatchedService]
self, services: List[MatchedService]
) -> SelectedEnergyService:
"""Overrides EVControllerInterface.select_energy_service_v20()."""
matched_energy_services = [
Expand All @@ -194,7 +197,7 @@ async def select_energy_service_v20(
return None

async def select_vas_services_v20(
self, services: List[MatchedService]
self, services: List[MatchedService]
) -> Optional[List[SelectedVAS]]:
"""Overrides EVControllerInterface.select_vas_services_v20()."""
matched_vas_services = [
Expand Down Expand Up @@ -256,7 +259,7 @@ async def get_charge_params_v2(self, protocol: Protocol) -> ChargeParamsV2:
)

async def get_charge_params_v20(
self, selected_service: SelectedEnergyService
self, selected_service: SelectedEnergyService
) -> Union[
ACChargeParameterDiscoveryReqParams,
BPTACChargeParameterDiscoveryReqParams,
Expand Down Expand Up @@ -305,7 +308,7 @@ async def get_charge_params_v20(
)

async def get_scheduled_se_params(
self, selected_energy_service: SelectedEnergyService
self, selected_energy_service: SelectedEnergyService
) -> ScheduledScheduleExchangeReqParams:
"""Overrides EVControllerInterface.get_scheduled_se_params()."""
ev_price_rule = EVPriceRule(
Expand Down Expand Up @@ -356,7 +359,7 @@ async def get_scheduled_se_params(
return scheduled_params

async def get_dynamic_se_params(
self, selected_energy_service: SelectedEnergyService
self, selected_energy_service: SelectedEnergyService
) -> DynamicScheduleExchangeReqParams:
"""Overrides EVControllerInterface.get_dynamic_se_params()."""
dynamic_params = DynamicScheduleExchangeReqParams(
Expand All @@ -373,7 +376,7 @@ async def get_dynamic_se_params(
return dynamic_params

async def process_scheduled_se_params(
self, scheduled_params: ScheduledScheduleExchangeResParams, pause: bool
self, scheduled_params: ScheduledScheduleExchangeResParams, pause: bool
) -> Tuple[Optional[EVPowerProfile], ChargeProgressV20]:
"""Overrides EVControllerInterface.process_scheduled_se_params()."""
is_ready = bool(random.getrandbits(1))
Expand Down Expand Up @@ -420,7 +423,7 @@ async def process_scheduled_se_params(
return ev_power_profile, charge_progress

async def process_dynamic_se_params(
self, dynamic_params: DynamicScheduleExchangeResParams, pause: bool
self, dynamic_params: DynamicScheduleExchangeResParams, pause: bool
) -> Tuple[Optional[EVPowerProfile], ChargeProgressV20]:
"""Overrides EVControllerInterface.process_dynamic_se_params()."""
is_ready = bool(random.getrandbits(1))
Expand Down Expand Up @@ -457,7 +460,7 @@ async def is_cert_install_needed(self) -> bool:
return self.config.is_cert_install_needed

async def process_sa_schedules_dinspec(
self, sa_schedules: List[SAScheduleTupleEntryDINSPEC]
self, sa_schedules: List[SAScheduleTupleEntryDINSPEC]
) -> int:
"""Overrides EVControllerInterface.process_sa_schedules_dinspec()."""
schedule = sa_schedules.pop()
Expand All @@ -482,8 +485,8 @@ async def process_sa_schedules_dinspec(
zero_power = 1
last_profile_entry_details = ProfileEntryDetailsDINSPEC(
start=(
schedule_entry_details.time_interval.start
+ schedule_entry_details.time_interval.duration
schedule_entry_details.time_interval.start
+ schedule_entry_details.time_interval.duration
),
max_power=zero_power,
)
Expand All @@ -492,7 +495,7 @@ async def process_sa_schedules_dinspec(
return schedule.sa_schedule_tuple_id

async def process_sa_schedules_v2(
self, sa_schedules: List[SAScheduleTuple]
self, sa_schedules: List[SAScheduleTuple]
) -> Tuple[ChargeProgressV2, int, ChargingProfile]:
"""Overrides EVControllerInterface.process_sa_schedules()."""
secc_schedule = sa_schedules.pop()
Expand All @@ -517,8 +520,8 @@ async def process_sa_schedules_v2(
zero_power = PVPMax(multiplier=0, value=0, unit=UnitSymbol.WATT)
last_profile_entry_details = ProfileEntryDetails(
start=(
schedule_entry_details.time_interval.start
+ schedule_entry_details.time_interval.duration
schedule_entry_details.time_interval.start
+ schedule_entry_details.time_interval.duration
),
max_power=zero_power,
)
Expand Down Expand Up @@ -546,7 +549,7 @@ async def continue_charging(self) -> bool:
return True

async def store_contract_cert_and_priv_key(
self, contract_cert: bytes, priv_key: bytes
self, contract_cert: bytes, priv_key: bytes
):
"""Overrides EVControllerInterface.store_contract_cert_and_priv_key()."""
# TODO Need to store the contract cert and private key
Expand All @@ -562,7 +565,7 @@ async def is_precharged(self, present_voltage_evse: PVEVSEPresentVoltage) -> boo
return True

async def get_dc_ev_power_delivery_parameter_dinspec(
self,
self,
) -> DCEVPowerDeliveryParameterDINSPEC:
return DCEVPowerDeliveryParameterDINSPEC(
dc_ev_status=await self.get_dc_ev_status_dinspec(),
Expand Down Expand Up @@ -631,7 +634,7 @@ async def get_ac_charge_params_v20(self) -> ACChargeParameterDiscoveryReqParams:
)

async def get_ac_bpt_charge_params_v20(
self,
self,
) -> BPTACChargeParameterDiscoveryReqParams:
"""Overrides EVControllerInterface.get_bpt_ac_charge_params_v20()."""
ac_charge_params_v20 = (await self.get_ac_charge_params_v20()).dict()
Expand All @@ -646,7 +649,7 @@ async def get_ac_bpt_charge_params_v20(
)

async def get_scheduled_ac_charge_loop_params(
self,
self,
) -> ScheduledACChargeLoopReqParams:
"""Overrides EVControllerInterface.get_scheduled_ac_charge_loop_params()."""
return ScheduledACChargeLoopReqParams(
Expand All @@ -655,7 +658,7 @@ async def get_scheduled_ac_charge_loop_params(
)

async def get_bpt_scheduled_ac_charge_loop_params(
self,
self,
) -> BPTScheduledACChargeLoopReqParams:
"""Overrides EVControllerInterface.get_bpt_scheduled_ac_charge_loop_params()."""
return BPTScheduledACChargeLoopReqParams(
Expand All @@ -677,7 +680,7 @@ async def get_dynamic_ac_charge_loop_params(self) -> DynamicACChargeLoopReqParam
)

async def get_bpt_dynamic_ac_charge_loop_params(
self,
self,
) -> BPTDynamicACChargeLoopReqParams:
"""Overrides EVControllerInterface.get_bpt_dynamic_ac_charge_loop_params()."""
return BPTDynamicACChargeLoopReqParams(
Expand Down Expand Up @@ -727,7 +730,7 @@ async def get_dc_charge_params_v20(self) -> DCChargeParameterDiscoveryReqParams:
)

async def get_dc_bpt_charge_params_v20(
self,
self,
) -> BPTDCChargeParameterDiscoveryReqParams:
"""Overrides EVControllerInterface.get_bpt_dc_charge_params_v20()."""
dc_charge_params_v20 = (await self.get_dc_charge_params_v20()).dict()
Expand Down
35 changes: 16 additions & 19 deletions iso15118/evcc/evcc_config.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import json
import logging
from dataclasses import dataclass, field, asdict, fields
from dataclasses import dataclass, fields
from enum import Enum
from typing import List, Optional

import dacite
from aiofile import async_open
from dacite import from_dict, Config

from iso15118.shared.messages.enums import Protocol, UINT_16_MAX, ServiceV20
from iso15118.shared.messages.enums import UINT_16_MAX

logger = logging.getLogger(__name__)

Expand All @@ -24,8 +23,7 @@ class SupportedProtocolOption(Enum):

@dataclass
class EVCCConfig:
supported_energy_services: List[ServiceV20] = None
supports_eim: bool = True
supported_energy_services: List[str] = None
is_cert_install_needed: bool = True
# Indicates the security level (either TCP (unencrypted) or TLS (encrypted))
# the EVCC shall send in the SDP request
Expand All @@ -35,8 +33,6 @@ class EVCCConfig:
enforce_tls: bool = False
supported_protocols: Optional[List[str]] = None
max_supporting_points: Optional[int] = None
is_cert_install_needed: bool = True
supported_energy_services: Optional[List[str]] = None

def __post_init__(self):
# Supported protocols, used for SupportedAppProtocol (SAP). The order in which
Expand All @@ -50,12 +46,8 @@ def __post_init__(self):
"ISO_15118_20_AC",
"DIN_SPEC_70121",
]
for protocol in self.supported_protocols:
if protocol not in list(map(lambda p: p.name, Protocol)):
raise Exception("Wrong attribute for supported protocol in config file."
f"Should be in list "
f"{list(map(lambda p: p.name, Protocol))}")

if self.supported_energy_services is None:
self.supported_energy_services = ["AC"]
# Indicates the maximum number of entries the EVCC supports within the
# sub-elements of a ScheduleTuple (e.g. PowerScheduleType and PriceRuleType in
# ISO 15118-20 as well as PMaxSchedule and SalesTariff in ISO 15118-2).
Expand All @@ -64,15 +56,18 @@ def __post_init__(self):
self.max_supporting_points = 1024

if not 0 <= self.max_supporting_points <= 1024:
raise Exception("Wrong range for max_supporting_points in config file. "
"Should be in [0..1024]")
raise Exception(
"Wrong range for max_supporting_points in config file. "
"Should be in [0..1024]"
)
# How often shall SDP (SECC Discovery Protocol) retries happen before reverting
# to using nominal duty cycle PWM-based charging?
if self.sdp_retry_cycles is None:
self.sdp_retry_cycles = 1
if self.sdp_retry_cycles < 0:
raise Exception("Wrong range for sdp_retry_cycles in config file. "
"Should be in [0..]")
raise Exception(
"Wrong range for sdp_retry_cycles in config file. " "Should be in [0..]"
)
# Indicates the security level (either TCP (unencrypted) or TLS (encrypted))
# the EVCC shall send in the SDP request
if self.use_tls is None:
Expand All @@ -95,8 +90,10 @@ def __post_init__(self):
if self.max_contract_certs is None:
self.max_contract_certs = 3
if not 1 < self.max_contract_certs < UINT_16_MAX:
raise Exception("Wrong range for max_contract_certs in config file. "
"Should be in [1..UINT_16_MAX]")
raise Exception(
"Wrong range for max_contract_certs in config file. "
"Should be in [1..UINT_16_MAX]"
)


async def load_from_file(file_name: str) -> EVCCConfig:
Expand Down
3 changes: 0 additions & 3 deletions iso15118/evcc/evcc_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
from typing import Optional

import environs
from marshmallow.validate import Range

from iso15118.shared.messages.enums import UINT_16_MAX, Protocol
from iso15118.shared.network import validate_nic
from iso15118.shared.settings import shared_settings

Expand Down
6 changes: 3 additions & 3 deletions iso15118/evcc/states/sap_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ async def process_message(
BodyBaseDINSPEC,
] = SessionSetupReqV2(
evcc_id=await self.comm_session.ev_controller.get_evcc_id(
Protocol.ISO_15118_2, self.comm_session.config.iface
Protocol.ISO_15118_2, self.comm_session.iface
)
)
next_ns: Namespace = Namespace.ISO_V2_MSG_DEF
Expand All @@ -105,7 +105,7 @@ async def process_message(

next_msg = SessionSetupReqDINSPEC(
evcc_id=await self.comm_session.ev_controller.get_evcc_id(
Protocol.DIN_SPEC_70121, self.comm_session.config.iface
Protocol.DIN_SPEC_70121, self.comm_session.iface
)
)

Expand All @@ -121,7 +121,7 @@ async def process_message(
next_msg = SessionSetupReqV20(
header=header,
evcc_id=await self.comm_session.ev_controller.get_evcc_id(
self.comm_session.protocol, self.comm_session.config.iface
self.comm_session.protocol, self.comm_session.iface
),
)
next_ns = Namespace.ISO_V20_COMMON_MSG
Expand Down
Loading

0 comments on commit 79d6f30

Please sign in to comment.