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

Bugfix: fix mappings and global errors #220

Merged
merged 3 commits into from
Dec 10, 2024
Merged
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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.14)

project(everest-framework
VERSION 0.19.0
VERSION 0.19.1
DESCRIPTION "The open operating system for e-mobility charging stations"
LANGUAGES CXX C
)
Expand Down
48 changes: 26 additions & 22 deletions include/utils/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,32 @@ class ConfigBase {
/// \returns the module config cache
std::unordered_map<std::string, ConfigCache> get_module_config_cache();

///
/// \return the cached mapping of module ids to module names
std::unordered_map<std::string, std::string> get_module_names();

///
/// \brief checks if the given \p module_id provides the requirement given in \p requirement_id
///
/// \returns a json object that contains the requirement
nlohmann::json resolve_requirement(const std::string& module_id, const std::string& requirement_id) const;

///
/// \brief resolves all Requirements of the given \p module_id to their Fulfillments
///
/// \returns a map indexed by Requirements
std::map<Requirement, Fulfillment> resolve_requirements(const std::string& module_id) const;

///
/// \returns a list of Requirements for \p module_id
std::list<Requirement> get_requirements(const std::string& module_id) const;

///
/// \brief A Fulfillment is a combination of a Requirement and the module and implementation ids where this is
/// implemented
/// \returns a map of Fulfillments for \p module_id
std::map<std::string, std::vector<Fulfillment>> get_fulfillments(const std::string& module_id) const;

/// \returns the 3 tier model mappings
std::unordered_map<std::string, ModuleTierMappings> get_3_tier_model_mappings();

Expand Down Expand Up @@ -267,28 +293,6 @@ class Config : public ConfigBase {
/// \returns the commands that the modules \p module_name implements from the given implementation \p impl_id
nlohmann::json get_module_cmds(const std::string& module_name, const std::string& impl_id);

///
/// \brief checks if the given \p module_id provides the requirement given in \p requirement_id
///
/// \returns a json object that contains the requirement
nlohmann::json resolve_requirement(const std::string& module_id, const std::string& requirement_id) const;

///
/// \brief resolves all Requirements of the given \p module_id to their Fulfillments
///
/// \returns a map indexed by Requirements
std::map<Requirement, Fulfillment> resolve_requirements(const std::string& module_id) const;

///
/// \returns a list of Requirements for \p module_id
std::list<Requirement> get_requirements(const std::string& module_id) const;

///
/// \brief A Fulfillment is a combination of a Requirement and the module and implementation ids where this is
/// implemented
/// \returns a map of Fulfillments for \p module_id
std::map<std::string, std::vector<Fulfillment>> get_fulfillments(const std::string& module_id) const;

///
/// \brief A RequirementInitialization contains everything needed to initialize a requirement in user code. This
/// includes the Requirement, its Fulfillment and an optional Mapping
Expand Down
160 changes: 82 additions & 78 deletions lib/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,88 @@ std::unordered_map<std::string, ConfigCache> ConfigBase::get_module_config_cache
return this->module_config_cache;
}

std::unordered_map<std::string, std::string> ConfigBase::get_module_names() {
return this->module_names;
}

json ConfigBase::resolve_requirement(const std::string& module_id, const std::string& requirement_id) const {
BOOST_LOG_FUNCTION();

// FIXME (aw): this function should throw, if the requirement id
// isn't even listed in the module manifest
// FIXME (aw): the following if doesn't check for the requirement id
// at all
const auto module_name_it = this->module_names.find(module_id);
if (module_name_it == this->module_names.end()) {
EVLOG_AND_THROW(EverestApiError(fmt::format("Requested requirement id '{}' of module {} not found in config!",
requirement_id, printable_identifier(module_id))));
}

// check for connections for this requirement
const auto& module_config = this->main.at(module_id);
const std::string module_name = module_name_it->second;
const auto& requirement = this->manifests.at(module_name).at("requires").at(requirement_id);
if (!module_config.at("connections").contains(requirement_id)) {
return json::array(); // return an empty array if our config does not contain any connections for this
// requirement id
}

// if only one single connection entry was required, return only this one
// callers can check with is_array() if this is a single connection (legacy) or a connection list
if (requirement.at("min_connections") == 1 && requirement.at("max_connections") == 1) {
return module_config.at("connections").at(requirement_id).at(0);
}
return module_config.at("connections").at(requirement_id);
}

std::map<Requirement, Fulfillment> ConfigBase::resolve_requirements(const std::string& module_id) const {
std::map<Requirement, Fulfillment> requirements;

const auto& module_name = get_module_name(module_id);
for (const auto& req_id : Config::keys(this->manifests.at(module_name).at("requires"))) {
const auto& resolved_req = this->resolve_requirement(module_id, req_id);
if (!resolved_req.is_array()) {
const auto& resolved_module_id = resolved_req.at("module_id");
const auto& resolved_impl_id = resolved_req.at("implementation_id");
const auto req = Requirement{req_id, 0};
requirements[req] = {resolved_module_id, resolved_impl_id, req};
} else {
for (std::size_t i = 0; i < resolved_req.size(); i++) {
const auto& resolved_module_id = resolved_req.at(i).at("module_id");
const auto& resolved_impl_id = resolved_req.at(i).at("implementation_id");
const auto req = Requirement{req_id, i};
requirements[req] = {resolved_module_id, resolved_impl_id, req};
}
}
}

return requirements;
}

std::list<Requirement> ConfigBase::get_requirements(const std::string& module_id) const {
BOOST_LOG_FUNCTION();

std::list<Requirement> res;

for (const auto& [requirement, fulfillment] : this->resolve_requirements(module_id)) {
res.push_back(requirement);
}

return res;
}

std::map<std::string, std::vector<Fulfillment>> ConfigBase::get_fulfillments(const std::string& module_id) const {
BOOST_LOG_FUNCTION();

std::map<std::string, std::vector<Fulfillment>> res;

for (const auto& [requirement, fulfillment] : this->resolve_requirements(module_id)) {
res[requirement.id].push_back(fulfillment);
}

return res;
}

std::unordered_map<std::string, ModuleTierMappings> ConfigBase::get_3_tier_model_mappings() {
return this->tier_mappings;
}
Expand Down Expand Up @@ -1054,84 +1136,6 @@ json Config::get_module_cmds(const std::string& module_name, const std::string&
return this->module_config_cache.at(module_name).cmds.at(impl_id);
}

json Config::resolve_requirement(const std::string& module_id, const std::string& requirement_id) const {
BOOST_LOG_FUNCTION();

// FIXME (aw): this function should throw, if the requirement id
// isn't even listed in the module manifest
// FIXME (aw): the following if doesn't check for the requirement id
// at all
const auto module_name_it = this->module_names.find(module_id);
if (module_name_it == this->module_names.end()) {
EVLOG_AND_THROW(EverestApiError(fmt::format("Requested requirement id '{}' of module {} not found in config!",
requirement_id, printable_identifier(module_id))));
}

// check for connections for this requirement
const auto& module_config = this->main.at(module_id);
const std::string module_name = module_name_it->second;
const auto& requirement = this->manifests.at(module_name).at("requires").at(requirement_id);
if (!module_config.at("connections").contains(requirement_id)) {
return json::array(); // return an empty array if our config does not contain any connections for this
// requirement id
}

// if only one single connection entry was required, return only this one
// callers can check with is_array() if this is a single connection (legacy) or a connection list
if (requirement.at("min_connections") == 1 && requirement.at("max_connections") == 1) {
return module_config.at("connections").at(requirement_id).at(0);
}
return module_config.at("connections").at(requirement_id);
}

std::map<Requirement, Fulfillment> Config::resolve_requirements(const std::string& module_id) const {
std::map<Requirement, Fulfillment> requirements;

const auto& module_name = get_module_name(module_id);
for (const auto& req_id : Config::keys(this->manifests.at(module_name).at("requires"))) {
const auto& resolved_req = this->resolve_requirement(module_id, req_id);
if (!resolved_req.is_array()) {
const auto& resolved_module_id = resolved_req.at("module_id");
const auto& resolved_impl_id = resolved_req.at("implementation_id");
const auto req = Requirement{req_id, 0};
requirements[req] = {resolved_module_id, resolved_impl_id, req};
} else {
for (std::size_t i = 0; i < resolved_req.size(); i++) {
const auto& resolved_module_id = resolved_req.at(i).at("module_id");
const auto& resolved_impl_id = resolved_req.at(i).at("implementation_id");
const auto req = Requirement{req_id, i};
requirements[req] = {resolved_module_id, resolved_impl_id, req};
}
}
}

return requirements;
}

std::list<Requirement> Config::get_requirements(const std::string& module_id) const {
BOOST_LOG_FUNCTION();

std::list<Requirement> res;

for (const auto& [requirement, fulfillment] : this->resolve_requirements(module_id)) {
res.push_back(requirement);
}

return res;
}

std::map<std::string, std::vector<Fulfillment>> Config::get_fulfillments(const std::string& module_id) const {
BOOST_LOG_FUNCTION();

std::map<std::string, std::vector<Fulfillment>> res;

for (const auto& [requirement, fulfillment] : this->resolve_requirements(module_id)) {
res[requirement.id].push_back(fulfillment);
}

return res;
}

RequirementInitialization Config::get_requirement_initialization(const std::string& module_id) const {
BOOST_LOG_FUNCTION();

Expand Down
3 changes: 1 addition & 2 deletions lib/everest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,8 +663,7 @@ void Everest::subscribe_global_all_errors(const error::ErrorCallback& callback,
clear_callback(error);
};

for (const std::string module_id : Config::keys(this->config.get_main_config())) {
const std::string module_name = this->config.get_module_name(module_id);
for (const auto& [module_id, module_name] : this->config.get_module_names()) {
const json provides = this->config.get_manifests().at(module_name).at("provides");
for (const auto& impl : provides.items()) {
const std::string impl_id = impl.key();
Expand Down
13 changes: 12 additions & 1 deletion src/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,21 @@ static std::map<pid_t, std::string> start_modules(ManagerConfig& config, MQTTAbs
const std::string module_name = module.key();
json serialized_mod_config = serialized_config;
serialized_mod_config["module_config"] = json::object();
// add mappings of fulfillments
serialized_mod_config["module_config"][module_name] = serialized_config.at("main").at(module_name);
const auto fulfillments = config.get_fulfillments(module_name);
serialized_mod_config["mappings"] = json::object();
for (const auto& fulfillment_list : fulfillments) {
for (const auto& fulfillment : fulfillment_list.second) {
const auto mappings = config.get_module_3_tier_model_mappings(fulfillment.module_id);
if (mappings.has_value()) {
serialized_mod_config["mappings"][fulfillment.module_id] = mappings.value();
}
}
}
// also add mappings of module
const auto mappings = config.get_module_3_tier_model_mappings(module_name);
if (mappings.has_value()) {
serialized_mod_config["mappings"] = json::object();
serialized_mod_config["mappings"][module_name] = mappings.value();
}
serialized_mod_config.erase("main"); // FIXME: do not put this "main" config in there in the first place
Expand Down