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

Expose mappings of requirements, provides and modules to modules #206

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
630c48c
WIP: make mappings available outside of errors
hikinggrass Sep 27, 2024
0105d03
Add stream operators for (optional) Mapping for easier debugging
hikinggrass Oct 6, 2024
b67b7b1
Add module mapping to module info
hikinggrass Oct 6, 2024
9dd6f7f
Bump version to 0.18
hikinggrass Oct 6, 2024
ad32fd1
Merge remote-tracking branch 'origin/main' into feature/expose-mappings
hikinggrass Oct 15, 2024
557932c
clang-format
hikinggrass Oct 15, 2024
9edf536
Rename RequirementConnection to Fulfillment
hikinggrass Oct 16, 2024
e90e60f
clang-format
hikinggrass Oct 16, 2024
7d0b0e4
Remove get_imple_mapping from ModuleAdapter
hikinggrass Oct 17, 2024
acac5bd
Move mapping member from Requirement to Fulfilment
hikinggrass Oct 17, 2024
9877ce6
Rename function so it doesn't share a name with another one returning…
hikinggrass Oct 17, 2024
b130124
Make more mapping related variables&functions const
hikinggrass Oct 17, 2024
44baf44
Add reworded comment
hikinggrass Oct 17, 2024
501f01f
clang-format
hikinggrass Oct 17, 2024
8c3feca
Add resolve_requirements that is used as a basis for refactored get_r…
hikinggrass Oct 18, 2024
61b9281
Remove code that isn't used
hikinggrass Oct 18, 2024
5d542d3
Introduce RequirementInitializer and RequirementInitialization
hikinggrass Oct 21, 2024
26473f7
Remove redundant mapping from Fulfillment
hikinggrass Oct 21, 2024
000efd5
Turn operator< of Requirement into free function
hikinggrass Oct 21, 2024
cd20aab
Remove constructors of Requirement
hikinggrass Oct 21, 2024
6084d34
Remove ugly void cast
hikinggrass Oct 21, 2024
59644d3
More const auto(&) usage
hikinggrass Oct 21, 2024
2f45d45
Fix everestrs build after removal of Requirement constructor
hikinggrass Oct 21, 2024
b0fc647
Merge branch 'main' into feature/expose-mappings
Pietfried Oct 24, 2024
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.17.2
VERSION 0.18.0
DESCRIPTION "The open operating system for e-mobility charging stations"
LANGUAGES CXX C
)
Expand Down
7 changes: 1 addition & 6 deletions everestpy/src/everest/misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>

#include <framework/runtime.hpp>
#include <utils/types.hpp>

class RuntimeSession {
public:
Expand All @@ -28,12 +29,6 @@ class RuntimeSession {
static std::unique_ptr<Everest::Config> create_config_instance(std::shared_ptr<Everest::RuntimeSettings> rs);
};

struct Fulfillment {
std::string module_id;
std::string implementation_id;
Requirement requirement;
};

struct Interface {
std::vector<std::string> variables;
std::vector<std::string> commands;
Expand Down
4 changes: 2 additions & 2 deletions everestrs/everestrs/src/everestrs_sys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ void Module::provide_command(const Runtime& rt, rust::String implementation_id,

void Module::subscribe_variable(const Runtime& rt, rust::String implementation_id, size_t index,
rust::String name) const {
const Requirement req(std::string(implementation_id), index);
const auto req = Requirement{std::string(implementation_id), index};
handle_->subscribe_var(req, std::string(name), [&rt, implementation_id, index, name](json args) {
rt.handle_variable(implementation_id, index, name, json2blob(args));
});
}

JsonBlob Module::call_command(rust::Str implementation_id, size_t index, rust::Str name, JsonBlob blob) const {
const Requirement req(std::string(implementation_id), index);
const auto req = Requirement{std::string(implementation_id), index};
json return_value = handle_->call_cmd(req, std::string(name), json::parse(blob.data.begin(), blob.data.end()));

return json2blob(return_value);
Expand Down
2 changes: 2 additions & 0 deletions include/framework/ModuleAdapter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct ModuleAdapter {
using ExtMqttSubscribeFunc = std::function<UnsubscribeToken(const std::string&, StringHandler)>;
using TelemetryPublishFunc =
std::function<void(const std::string&, const std::string&, const std::string&, const TelemetryMap&)>;
using GetMappingFunc = std::function<std::optional<ModuleTierMappings>()>;

CallFunc call;
PublishFunc publish;
Expand All @@ -107,6 +108,7 @@ struct ModuleAdapter {
ExtMqttSubscribeFunc ext_mqtt_subscribe;
std::vector<cmd> registered_commands;
TelemetryPublishFunc telemetry_publish;
GetMappingFunc get_mapping;

void check_complete() {
// FIXME (aw): I should throw if some of my handlers are not set
Expand Down
12 changes: 12 additions & 0 deletions include/framework/everest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ class Everest {
/// \returns true if telemetry is enabled
bool is_telemetry_enabled();

///
/// \returns the 3 tier model mappings for this module
///
std::optional<ModuleTierMappings> get_3_tier_model_mapping();

///
/// \brief Chccks if all commands of a module that are listed in its manifest are available
///
Expand Down Expand Up @@ -239,6 +244,13 @@ class Everest {
///
void subscribe_global_all_errors(const error::ErrorCallback& callback, const error::ErrorCallback& clear_callback);
};

///
/// \returns the 3 tier model mapping from a \p module_tier_mapping for the given \p impl_id
///
std::optional<Mapping> get_impl_mapping(std::optional<ModuleTierMappings> module_tier_mappings,
const std::string& impl_id);

} // namespace Everest

#endif // FRAMEWORK_EVEREST_HPP
11 changes: 6 additions & 5 deletions include/framework/runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,17 @@ void populate_module_info_path_from_runtime_settings(ModuleInfo&, std::shared_pt

struct ModuleCallbacks {
std::function<void(ModuleAdapter module_adapter)> register_module_adapter;
std::function<std::vector<cmd>(const json& connections)> everest_register;
std::function<std::vector<cmd>(const RequirementInitialization& requirement_init)> everest_register;
std::function<void(ModuleConfigs module_configs, const ModuleInfo& info)> init;
std::function<void()> ready;

ModuleCallbacks() = default;

ModuleCallbacks(const std::function<void(ModuleAdapter module_adapter)>& register_module_adapter,
const std::function<std::vector<cmd>(const json& connections)>& everest_register,
const std::function<void(ModuleConfigs module_configs, const ModuleInfo& info)>& init,
const std::function<void()>& ready);
ModuleCallbacks(
const std::function<void(ModuleAdapter module_adapter)>& register_module_adapter,
const std::function<std::vector<cmd>(const RequirementInitialization& requirement_init)>& everest_register,
const std::function<void(ModuleConfigs module_configs, const ModuleInfo& info)>& init,
const std::function<void()>& ready);
};

struct VersionInformation {
Expand Down
22 changes: 20 additions & 2 deletions include/utils/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,29 @@ class Config {
/// \returns a json object that contains the requirement
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really doubt the value of these comments, you get exactly the same information if you look at the function signature.

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
/// \returns a RequirementInitialization
RequirementInitialization get_requirement_initialization(const std::string& module_id) const;

///
/// \brief checks if the config contains the given \p module_id
///
Expand Down Expand Up @@ -198,11 +216,11 @@ class Config {

//
/// \returns the 3 tier model mappings for the given \p module_id
std::optional<ModuleTierMappings> get_3_tier_model_mappings(const std::string& module_id);
std::optional<ModuleTierMappings> get_module_3_tier_model_mappings(const std::string& module_id) const;

//
/// \returns the 3 tier model mapping for the given \p module_id and \p impl_id
std::optional<Mapping> get_3_tier_model_mapping(const std::string& module_id, const std::string& impl_id);
std::optional<Mapping> get_3_tier_model_mapping(const std::string& module_id, const std::string& impl_id) const;

///
/// \brief turns then given \p module_id into a printable identifier
Expand Down
82 changes: 62 additions & 20 deletions include/utils/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,50 @@ enum class QOS {
QOS2 ///< Exactly once delivery
};

/// \brief A Mapping that can be used to map a module or implementation to a specific EVSE or optionally to a Connector
struct Mapping {
int evse; ///< The EVSE id
std::optional<int> connector; ///< An optional Connector id

Mapping(int evse) : evse(evse) {
}

Mapping(int evse, int connector) : evse(evse), connector(connector) {
}
};

/// \brief Writes the string representation of the given Mapping \p mapping to the given output stream \p os
/// \returns an output stream with the Mapping written to
inline std::ostream& operator<<(std::ostream& os, const Mapping& mapping) {
os << "Mapping(evse: " << mapping.evse;
if (mapping.connector.has_value()) {
os << ", connector: " << mapping.connector.value();
}
os << ")";

return os;
}

/// \brief Writes the string representation of the given Mapping \p mapping to the given output stream \p os
/// \returns an output stream with the Mapping written to
inline std::ostream& operator<<(std::ostream& os, const std::optional<Mapping>& mapping) {
hikinggrass marked this conversation as resolved.
Show resolved Hide resolved
if (mapping.has_value()) {
os << mapping.value();
} else {
os << "Mapping(charging station)";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this default representation make any sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature (at the moment at least) is inspired by the 3-tier mappings from OCPP. So if you have no explicit mapping it's assumed that the module maps to the whole charging station, getting more specific by a mapping to an evse and/or connector

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Still I'm unsure, whether the string Mapping(charging station) on a text console will help users to understand what is meant.

}

return os;
}

/// \brief A 3 tier mapping for a module and its individual implementations
struct ModuleTierMappings {
std::optional<Mapping> module; ///< Mapping of the whole module to an EVSE id and optional Connector id. If this is
///< absent the module is assumed to be mapped to the whole charging station
std::unordered_map<std::string, std::optional<Mapping>>
implementations; ///< Mappings for the individual implementations of the module
};

struct ModuleInfo {
struct Paths {
std::filesystem::path etc;
Expand All @@ -78,38 +122,36 @@ struct ModuleInfo {
Paths paths;
bool telemetry_enabled;
bool global_errors_enabled;
std::optional<Mapping> mapping;
};

struct TelemetryConfig {
int id;
};

/// \brief A Mapping that can be used to map a module or implementation to a specific EVSE or optionally to a Connector
struct Mapping {
int evse; ///< The EVSE id
std::optional<int> connector; ///< An optional Connector id
struct Requirement {
std::string id;
size_t index = 0;
};

Mapping(int evse) : evse(evse) {
}
bool operator<(const Requirement& lhs, const Requirement& rhs);

Mapping(int evse, int connector) : evse(evse), connector(connector) {
}
/// \brief A Fulfillment relates a Requirement to its connected implementation, identified via its module and
/// implementation id.
struct Fulfillment {
std::string module_id;
std::string implementation_id;
Requirement requirement;
hikinggrass marked this conversation as resolved.
Show resolved Hide resolved
};

/// \brief A 3 tier mapping for a module and its individual implementations
struct ModuleTierMappings {
std::optional<Mapping> module; ///< Mapping of the whole module to an EVSE id and optional Connector id. If this is
///< absent the module is assumed to be mapped to the whole charging station
std::unordered_map<std::string, std::optional<Mapping>>
implementations; ///< Mappings for the individual implementations of the module
/// \brief Contains everything that's needed to initialize a requirement in user code
struct RequirementInitializer {
Requirement requirement;
Fulfillment fulfillment;
std::optional<Mapping> mapping;
};

struct Requirement {
Requirement(const std::string& requirement_id_, size_t index_);
bool operator<(const Requirement& rhs) const;
std::string id;
size_t index;
};
using RequirementInitialization = std::map<std::string, std::vector<RequirementInitializer>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uncertain with that name: RequirementInitialization sounds more like the result of the initialization of a requirement than a mapping from each requirement name to its initializer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open for suggestions 😄 that was the best I could come up with at the time 😅


struct ImplementationIdentifier {
ImplementationIdentifier(const std::string& module_id_, const std::string& implementation_id_,
Expand Down
Loading
Loading