diff --git a/everest-testing/src/everest/testing/core_utils/_configuration/everest_environment_setup.py b/everest-testing/src/everest/testing/core_utils/_configuration/everest_environment_setup.py index a47b734f..32c342ec 100644 --- a/everest-testing/src/everest/testing/core_utils/_configuration/everest_environment_setup.py +++ b/everest-testing/src/everest/testing/core_utils/_configuration/everest_environment_setup.py @@ -88,6 +88,7 @@ class EverestTestEnvironmentSetup: class _EverestEnvironmentTemporaryPaths: """ Paths of the temporary configuration files / data """ certs_dir: Path # used by both OCPP and evse security + ocpp_config_path: Path ocpp_config_file: Path ocpp_user_config_file: Path ocpp_database_dir: Path @@ -142,6 +143,9 @@ def everest_core(self) -> EverestCore: def _create_temporary_directory_structure(self, tmp_path: Path) -> _EverestEnvironmentTemporaryPaths: ocpp_config_dir = tmp_path / "ocpp_config" ocpp_config_dir.mkdir(exist_ok=True) + if self._ocpp_config.ocpp_version == OCPPVersion.ocpp201: + component_config_path = ocpp_config_dir / "component_config" + component_config_path.mkdir(exist_ok=True) certs_dir = tmp_path / "certs" certs_dir.mkdir(exist_ok=True) ocpp_logs_dir = ocpp_config_dir / "logs" @@ -153,6 +157,7 @@ def _create_temporary_directory_structure(self, tmp_path: Path) -> _EverestEnvir logging.info(f"temp ocpp config files directory: {ocpp_config_dir}") return self._EverestEnvironmentTemporaryPaths( + ocpp_config_path=ocpp_config_dir / "component_config", ocpp_config_file=ocpp_config_dir / "config.json", ocpp_user_config_file=ocpp_config_dir / "user_config.json", ocpp_database_dir=ocpp_config_dir, @@ -173,6 +178,7 @@ def _create_ocpp_module_configuration_strategy(self, ) elif self._ocpp_config.ocpp_version == OCPPVersion.ocpp201: ocpp_paths = OCPPModulePaths201( + ChargePointConfigPath=str(temporary_paths.ocpp_config_path), MessageLogPath=str(temporary_paths.ocpp_message_log_directory), CoreDatabasePath=str(temporary_paths.ocpp_database_dir), DeviceModelDatabasePath=str(temporary_paths.ocpp_database_dir / "device_model_storage.db"), @@ -199,12 +205,13 @@ def _setup_libocpp_configuration(self, temporary_paths: _EverestEnvironmentTempo if self._ocpp_config.device_model_component_config_path \ else self._ocpp_config.libocpp_path / 'config/v201/component_config' - liboccp_configuration_helper.generate_ocpp_config( central_system_port=self._ocpp_config.central_system_port, central_system_host=self._ocpp_config.central_system_host, - source_ocpp_config_file=source_ocpp_config, - target_ocpp_config_file=temporary_paths.ocpp_config_file, + source_ocpp_config_path=source_ocpp_config, + target_ocpp_config_path=temporary_paths.ocpp_config_file \ + if self._ocpp_config.ocpp_version == OCPPVersion.ocpp16 \ + else temporary_paths.ocpp_config_path, target_ocpp_user_config_file=temporary_paths.ocpp_user_config_file, configuration_strategies=self._ocpp_config.configuration_strategies ) diff --git a/everest-testing/src/everest/testing/core_utils/_configuration/libocpp_configuration_helper.py b/everest-testing/src/everest/testing/core_utils/_configuration/libocpp_configuration_helper.py index d4589d7d..5e507865 100644 --- a/everest-testing/src/everest/testing/core_utils/_configuration/libocpp_configuration_helper.py +++ b/everest-testing/src/everest/testing/core_utils/_configuration/libocpp_configuration_helper.py @@ -1,5 +1,7 @@ from __future__ import annotations +import os +from glob import glob import json import sys from abc import ABC, abstractmethod @@ -34,30 +36,40 @@ class LibOCPPConfigurationHelperBase(ABC): """ Helper for parsing / adapting the LibOCPP configuration and dumping it a database file. """ def generate_ocpp_config(self, - target_ocpp_config_file: Path, + target_ocpp_config_path: Path, target_ocpp_user_config_file: Path, - source_ocpp_config_file: Path, + source_ocpp_config_path: Path, central_system_host: str, central_system_port: Union[str, int], configuration_strategies: list[OCPPConfigAdjustmentStrategy] | None = None): - config = json.loads(source_ocpp_config_file.read_text()) + config = self._get_config(source_ocpp_config_path) configuration_strategies = configuration_strategies if configuration_strategies else [] for v in [self._get_default_strategy(central_system_port, central_system_host)] + configuration_strategies: config = v.adjust_ocpp_configuration(config) - with target_ocpp_config_file.open("w") as f: - json.dump(config, f) + self._store_config(self, target_ocpp_config_path) target_ocpp_user_config_file.write_text("{}") + @abstractmethod + def _get_config(self, source_ocpp_config_path: Path): + pass + @abstractmethod def _get_default_strategy(self, central_system_port: int | str, central_system_host: str) -> OCPPConfigAdjustmentStrategy: pass + @abstractmethod + def _store_config(self, config, target_ocpp_config_file): + pass + class LibOCPP16ConfigurationHelper(LibOCPPConfigurationHelperBase): + def _get_config(self, source_ocpp_config_path: Path): + return json.loads(source_ocpp_config_path.read_text()) + def _get_default_strategy(self, central_system_port, central_system_host): def adjust_ocpp_configuration(config: dict) -> dict: config = deepcopy(config) @@ -68,6 +80,10 @@ def adjust_ocpp_configuration(config: dict) -> dict: return OCPPConfigAdjustmentStrategyWrapper(adjust_ocpp_configuration) + def _store_config(self, config, target_ocpp_config_file): + with target_ocpp_config_file.open("w") as f: + json.dump(config, f) + class _OCPP201NetworkConnectionProfileAdjustment(OCPPConfigAdjustmentStrategy): """ Adjusts the OCPP 2.0.1 Network Connection Profile by injecting the right host, port and chargepoint id. @@ -96,23 +112,49 @@ def adjust_ocpp_configuration(self, config: dict): @staticmethod def _get_value_from_v201_config(ocpp_config: json, component_name: str, variable_name: str, variable_attribute_type: str): - for component in ocpp_config: - if (component["name"] == component_name): - return component["variables"][variable_name]["attributes"][variable_attribute_type] + for (component, schema) in ocpp_config: + if component == component_name: + attributes = schema["properties"][variable_name]["attributes"] + for attribute in attributes: + if attribute["type"] == variable_attribute_type: + return attribute["value"] @staticmethod def _set_value_in_v201_config(ocpp_config: json, component_name: str, variable_name: str, variable_attribute_type: str, value: str): - for component in ocpp_config: - if (component["name"] == component_name): - component["variables"][variable_name]["attributes"][variable_attribute_type] = value - return + for (component, schema) in ocpp_config: + if component == component_name: + attributes = schema["properties"][variable_name]["attributes"] + for attribute in attributes: + if attribute["type"] == variable_attribute_type: + attribute["value"] = value class LibOCPP201ConfigurationHelper(LibOCPPConfigurationHelperBase): + def _get_config(self, source_ocpp_config_path: Path): + config = {} + file_list_standardized = glob(source_ocpp_config_path / "standardized/*.json") + file_list_custom = glob(source_ocpp_config_path / "custom/*.json") + file_list = file_list_standardized + file_list_custom + for file in file_list: + # Get component from file name + head, tail = os.path.split(file) + component_name, extension = os.path.splitext(tail) + # Store json in dict + with open(file) as f: + config[component_name] = json.load(f) + return config + def _get_default_strategy(self, central_system_port: int | str, central_system_host: str) -> OCPPConfigAdjustmentStrategy: return _OCPP201NetworkConnectionProfileAdjustment(central_system_port, central_system_host) + def _store_config(self, config, target_ocpp_config_path): + # Just store all in the 'standardized' folder + path = target_ocpp_config_path / "standardized" + for key, value in config.items(): + file_name = path / key + '.json' + with file_name.open("w") as f: + json.dump(value, f)