Skip to content
Draft
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
13 changes: 13 additions & 0 deletions modules/OCPP201/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ target_sources(${MODULE_NAME}
"device_model/everest_device_model_storage.cpp"
"device_model/composed_device_model_storage.cpp"
"device_model/definitions.cpp"
"device_model/mapping/variable_mapping.cpp"
)

# Install mapping.yaml
install(
FILES device_model/mapping/mapping.yaml
DESTINATION ${CMAKE_INSTALL_DATADIR}/everest/modules/${MODULE_NAME}
)

# Install schema
install(
FILES device_model/mapping/mapping_schema.json
DESTINATION ${CMAKE_INSTALL_DATADIR}/everest/modules/${MODULE_NAME}
)

if(EVEREST_CORE_BUILD_TESTING)
Expand Down
16 changes: 15 additions & 1 deletion modules/OCPP201/OCPP201.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <conversions.hpp>
#include <device_model/composed_device_model_storage.hpp>
#include <device_model/mapping/variable_mapping.hpp>

Check warning on line 12 in modules/OCPP201/OCPP201.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/OCPP201.cpp#L12

Include file: <device_model/mapping/variable_mapping.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <error_handling.hpp>
#include <evse_security_ocpp.hpp>
#include <external_energy_limits.hpp>
Expand Down Expand Up @@ -420,6 +421,17 @@
}
}();

const auto mapping_file_path = [&]() {
const auto config_mapping_file_path = fs::path(this->config.MappingFilePath);
if (config_mapping_file_path.is_relative()) {
return this->ocpp_share_path / config_mapping_file_path;
} else {
return config_mapping_file_path;
}
}();

const auto mapping_schema_path = this->ocpp_share_path / "mapping_schema.yaml";

if (!fs::exists(this->config.MessageLogPath)) {
try {
fs::create_directory(this->config.MessageLogPath);
Expand Down Expand Up @@ -838,14 +850,16 @@

std::map<int32_t, int32_t> evse_connector_structure = this->get_connector_structure();

auto variable_mapping = std::make_unique<VariableMapping>(mapping_file_path, mapping_schema_path);

// initialize libocpp device model
auto libocpp_device_model_storage = std::make_shared<ocpp::v2::DeviceModelStorageSqlite>(
device_model_database_path, device_model_database_migration_path, device_model_config_path);

// initialize everest device model
this->everest_device_model_storage = std::make_shared<device_model::EverestDeviceModelStorage>(
r_evse_manager, this->evse_hardware_capabilities_map, everest_device_model_database_path,
device_model_database_migration_path, get_config_service_client());
device_model_database_migration_path, std::move(variable_mapping), get_config_service_client());

// initialize composed device model, this will be provided to the ChargePoint constructor
auto composed_device_model_storage = std::make_unique<module::device_model::ComposedDeviceModelStorage>();
Expand Down
1 change: 1 addition & 0 deletions modules/OCPP201/OCPP201.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
int RequestCompositeScheduleDurationS;
std::string RequestCompositeScheduleUnit;
int DelayOcppStart;
std::string MappingFilePath;

Check warning on line 56 in modules/OCPP201/OCPP201.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/OCPP201.hpp#L56

struct member 'Conf::MappingFilePath' is never used.
};

class OCPP201 : public Everest::ModuleBase {
Expand Down
15 changes: 14 additions & 1 deletion modules/OCPP201/device_model/composed_device_model_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,20 @@ bool ComposedDeviceModelStorage::register_device_model_storage(
ocpp::v2::DeviceModelMap ComposedDeviceModelStorage::get_device_model() {
ocpp::v2::DeviceModelMap device_model_map;
for (const auto& [name, device_model_storage] : this->device_model_storages) {
device_model_map.merge(device_model_storage->get_device_model());
const auto& partial_device_model = device_model_storage->get_device_model();
for (const auto& [component, variable_map] : partial_device_model) {
auto& existing_variable_map = device_model_map[component]; // Inserts if not present
// Merge variable_map into existing_variable_map
for (const auto& [variable, variable_meta_data] : variable_map) {
if (existing_variable_map.find(variable) != existing_variable_map.end()) {
EVLOG_warning << "Variable " << variable.name << " already exists in component " << component.name
<< " but is also defined in device model storage: " << name;
EVLOG_AND_THROW(std::runtime_error(
"Variable already exists in component. Fix your device model configuration."));
}
existing_variable_map[variable] = variable_meta_data; // Overwrite or insert
}
}
}
return device_model_map;
}
Expand Down
248 changes: 164 additions & 84 deletions modules/OCPP201/device_model/everest_device_model_storage.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include <generated/types/evse_board_support.hpp>
#include <generated/types/powermeter.hpp>

#include <device_model/mapping/variable_mapping.hpp>

Check warning on line 12 in modules/OCPP201/device_model/everest_device_model_storage.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/everest_device_model_storage.hpp#L12

Include file: <device_model/mapping/variable_mapping.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <ocpp/v2/device_model_storage_interface.hpp>
#include <ocpp/v2/device_model_storage_sqlite.hpp>
#include <ocpp/v2/init_device_model_db.hpp>

Check warning on line 15 in modules/OCPP201/device_model/everest_device_model_storage.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/everest_device_model_storage.hpp#L15

Include file: <ocpp/v2/init_device_model_db.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <utils/config_service.hpp>

namespace module::device_model {
Expand All @@ -20,6 +22,7 @@
const std::vector<std::unique_ptr<evse_managerIntf>>& r_evse_manager,
const std::map<int32_t, types::evse_board_support::HardwareCapabilities>& evse_hardware_capabilities_map,
const std::filesystem::path& db_path, const std::filesystem::path& migration_files_path,
std::unique_ptr<VariableMapping> variable_mapping,
std::shared_ptr<Everest::config::ConfigServiceClient> config_service_client);
virtual ~EverestDeviceModelStorage() override = default;
virtual ocpp::v2::DeviceModelMap get_device_model() override;
Expand Down Expand Up @@ -55,11 +58,15 @@
std::shared_ptr<Everest::config::ConfigServiceClient> config_service_client;
std::map<Everest::config::ModuleIdType, everest::config::ModuleConfigurationParameters> module_configs;
std::map<std::string, ModuleTierMappings> mappings;
std::unique_ptr<VariableMapping> variable_mapping;

void init_hw_capabilities(
const std::map<int32_t, types::evse_board_support::HardwareCapabilities>& evse_hardware_capabilities_map);
void update_hw_capabilities(const ocpp::v2::Component& evse_component,
const types::evse_board_support::HardwareCapabilities& hw_capabilities);
void init_everest_config();
std::vector<ocpp::v2::DeviceModelVariable>
build_everest_config_variables(const everest::config::ModuleConfigurationParameters& module_config,
const std::string& module_id, const ocpp::v2::Component& component);
ocpp::v2::DeviceModelMap apply_mappings(const ocpp::v2::DeviceModelMap& device_model_map);
};
} // namespace module::device_model
19 changes: 19 additions & 0 deletions modules/OCPP201/device_model/mapping/mapping.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
mappings:
- ocpp:
component:
name: "TxCtrlr"
variable:
name: "EVConnectionTimeOut"
everest:
module_id: "auth"
configuration_parameter_name: "connection_timeout"
module_implementation_id: "!module"

- ocpp:
component:
name: "AuthCtrlr"
variable:
name: "MasterPassGroupId"
everest:
module_id: "auth"
configuration_parameter_name: "master_pass_group_id"
63 changes: 63 additions & 0 deletions modules/OCPP201/device_model/mapping/mapping_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ComponentVariable to ConfigurationParameterIdentifier Mapping",
"type": "object",
"properties": {
"mappings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ocpp": {
"type": "object",
"properties": {
"component": {
"type": "object",
"properties": {
"name": { "type": "string" },
"instance": { "type": "string" },
"evse": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"connectorId": { "type": "integer" }
},
"required": ["id"],
"additionalProperties": false
}
},
"required": ["name"],
"additionalProperties": false
},
"variable": {
"type": "object",
"properties": {
"name": { "type": "string" },
"instance": { "type": "string" }
},
"required": ["name"],
"additionalProperties": false
}
},
"required": ["component", "variable"],
"additionalProperties": false
},
"everest": {
"type": "object",
"properties": {
"module_id": { "type": "string" },
"configuration_parameter_name": { "type": "string" },
"module_implementation_id": { "type": "string" }
},
"required": ["module_id", "configuration_parameter_name"],
"additionalProperties": false
}
},
"required": ["ocpp", "everest"],
"additionalProperties": false
}
}
},
"required": ["mappings"],
"additionalProperties": false
}
62 changes: 62 additions & 0 deletions modules/OCPP201/device_model/mapping/variable_mapping.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest

#include <device_model/mapping/variable_mapping.hpp>

Check warning on line 4 in modules/OCPP201/device_model/mapping/variable_mapping.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.cpp#L4

Include file: <device_model/mapping/variable_mapping.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <everest/logging.hpp>

Check warning on line 5 in modules/OCPP201/device_model/mapping/variable_mapping.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.cpp#L5

Include file: <everest/logging.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <ocpp/v2/comparators.hpp>

Check warning on line 6 in modules/OCPP201/device_model/mapping/variable_mapping.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.cpp#L6

Include file: <ocpp/v2/comparators.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <utils/yaml_loader.hpp>

Check warning on line 7 in modules/OCPP201/device_model/mapping/variable_mapping.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.cpp#L7

Include file: <utils/yaml_loader.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace ocpp::v2;
using namespace everest::config;

VariableMapping::VariableMapping(const fs::path& mapping_file, const fs::path& schema_file) {
if (!fs::exists(mapping_file)) {
EVLOG_warning << "Mapping file does not exist: " << mapping_file;
return;
}

const auto& mapping = Everest::load_yaml(mapping_file);
const auto& schema = Everest::load_yaml(schema_file);

auto validator = nlohmann::json_schema::json_validator{};
validator.set_root_schema(schema);
validator.validate(mapping);

for (const auto& entry : mapping["mappings"]) {
ComponentVariable cv = entry["ocpp"];
ConfigurationParameterIdentifier cpi = entry["everest"];
user_mapping[cpi] = cv;
}
};

void VariableMapping::add_cv_mapping(const ComponentVariable& everest_component_variable,
const ComponentVariable& ocpp_component_variable) {
this->everest_cv_to_ocpp_cv_mapping[everest_component_variable] = ocpp_component_variable;
this->ocpp_cv_to_everest_cv_mapping[ocpp_component_variable] = everest_component_variable;
}

std::optional<ComponentVariable> VariableMapping::get_ocpp_cv(const ConfigurationParameterIdentifier& identifier) {
auto it = user_mapping.find(identifier);
if (it != user_mapping.end()) {
return it->second;
}
return std::nullopt;
}

std::optional<ocpp::v2::ComponentVariable>
VariableMapping::get_ocpp_cv(const ocpp::v2::ComponentVariable& everest_component_variable) {
auto it = everest_cv_to_ocpp_cv_mapping.find(everest_component_variable);
if (it != everest_cv_to_ocpp_cv_mapping.end()) {
return it->second;
}
return std::nullopt;
}

std::optional<ocpp::v2::ComponentVariable>
VariableMapping::get_everest_cv(const ocpp::v2::ComponentVariable& ocpp_component_variable) {
auto it = ocpp_cv_to_everest_cv_mapping.find(ocpp_component_variable);
if (it != ocpp_cv_to_everest_cv_mapping.end()) {
return it->second;
}
return std::nullopt;
}
47 changes: 47 additions & 0 deletions modules/OCPP201/device_model/mapping/variable_mapping.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest

#pragma once

#include <map>

Check warning on line 6 in modules/OCPP201/device_model/mapping/variable_mapping.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.hpp#L6

Include file: <map> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <ocpp/v2/ocpp_types.hpp>

Check warning on line 7 in modules/OCPP201/device_model/mapping/variable_mapping.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.hpp#L7

Include file: <ocpp/v2/ocpp_types.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <utils/config_service.hpp>

Check warning on line 8 in modules/OCPP201/device_model/mapping/variable_mapping.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.hpp#L8

Include file: <utils/config_service.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.

/// \brief This class is used to map EVerest module configuration parameters to OCPP component variables
class VariableMapping {

public:
/// \brief Constructor that loads the mapping from the given \p mapping_file and validates it against the schema in
/// \p schema_file
VariableMapping(const fs::path& mapping_file, const fs::path& schema_file);

/// \brief EVerest modules are represented as OCPP component variables in the OCPP device model. The following
/// functions adds a bi-directional mapping for the EVerest component variables and (mostly standardized) OCPP
/// component variables.
void add_cv_mapping(const ocpp::v2::ComponentVariable& everest_component_variable,
const ocpp::v2::ComponentVariable& ocpp_component_variable);

/// \brief Gets a component variable from the given ȨVerest configuration parameter \p identifier
/// \return An optional OCPP component variable if the mapping exists, otherwise std::nullopt
std::optional<ocpp::v2::ComponentVariable>
get_ocpp_cv(const everest::config::ConfigurationParameterIdentifier& identifier);

/// \brief Gets a component variable from the given \p everest_component_variable
/// \return An optional OCPP component variable if the mapping exists, otherwise std::nullopt
std::optional<ocpp::v2::ComponentVariable>
get_ocpp_cv(const ocpp::v2::ComponentVariable& everest_component_variable);

/// \brief Gets the EVerest component variable for the given \p ocpp_component_variable
std::optional<ocpp::v2::ComponentVariable>
get_everest_cv(const ocpp::v2::ComponentVariable& ocpp_component_variable);

private:
std::map<everest::config::ConfigurationParameterIdentifier, ocpp::v2::ComponentVariable>
user_mapping; // Maps EVerest configuration parameters to OCPP component variables

Check warning on line 40 in modules/OCPP201/device_model/mapping/variable_mapping.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.hpp#L40

class member 'VariableMapping::user_mapping' is never used.

// EVerest modules are represented as OCPP component variables in the OCPP device model. The following maps are
// bi-directional mappings to map between EVerest component variables and (mostly standardized) OCPP component
// variables.
std::map<ocpp::v2::ComponentVariable, ocpp::v2::ComponentVariable> everest_cv_to_ocpp_cv_mapping;

Check warning on line 45 in modules/OCPP201/device_model/mapping/variable_mapping.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.hpp#L45

class member 'VariableMapping::everest_cv_to_ocpp_cv_mapping' is never used.
std::map<ocpp::v2::ComponentVariable, ocpp::v2::ComponentVariable> ocpp_cv_to_everest_cv_mapping;

Check warning on line 46 in modules/OCPP201/device_model/mapping/variable_mapping.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

modules/OCPP201/device_model/mapping/variable_mapping.hpp#L46

class member 'VariableMapping::ocpp_cv_to_everest_cv_mapping' is never used.
};
Loading
Loading