Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_CONFIG_HPP
|
||||
#define UTILS_CONFIG_HPP
|
||||
|
||||
#include <filesystem>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <nlohmann/json-schema.hpp>
|
||||
|
||||
#include <utils/config/mqtt_settings.hpp>
|
||||
#include <utils/config/settings.hpp>
|
||||
#include <utils/config/storage_userconfig.hpp>
|
||||
#include <utils/config_cache.hpp>
|
||||
|
||||
#include <utils/error.hpp>
|
||||
#include <utils/error/error_type_map.hpp>
|
||||
#include <utils/module_config.hpp>
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using everest::config::ModuleConfig;
|
||||
using everest::config::ModuleConfigurations;
|
||||
|
||||
struct ManagerSettings;
|
||||
struct RuntimeSettings;
|
||||
|
||||
///
|
||||
/// \brief A structure that contains all available schemas
|
||||
///
|
||||
struct Schemas {
|
||||
nlohmann::json config; ///< The config schema
|
||||
nlohmann::json manifest; ///< The manifest scheme
|
||||
nlohmann::json interface; ///< The interface schema
|
||||
nlohmann::json type; ///< The type schema
|
||||
nlohmann::json error_declaration_list; ///< The error-declaration-list schema
|
||||
};
|
||||
|
||||
struct Validators {
|
||||
nlohmann::json_schema::json_validator config;
|
||||
nlohmann::json_schema::json_validator manifest;
|
||||
nlohmann::json_schema::json_validator type;
|
||||
nlohmann::json_schema::json_validator interface;
|
||||
nlohmann::json_schema::json_validator error_declaration_list;
|
||||
};
|
||||
|
||||
struct SchemaValidation {
|
||||
Schemas schemas;
|
||||
Validators validators;
|
||||
};
|
||||
|
||||
struct ImplementationInfo {
|
||||
std::string module_id;
|
||||
std::string module_name;
|
||||
std::string impl_id;
|
||||
std::string impl_intf;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief A simple json schema loader that uses the builtin draft7 schema of
|
||||
/// the json schema validator when it encounters it, throws an exception
|
||||
/// otherwise
|
||||
void loader(const nlohmann::json_uri& uri, nlohmann::json& schema);
|
||||
|
||||
///
|
||||
/// \brief An extension to the default format checker of the json schema
|
||||
/// validator supporting uris
|
||||
void format_checker(const std::string& format, const std::string& value);
|
||||
|
||||
///
|
||||
/// \brief loads and validates a json schema at the provided \p path
|
||||
///
|
||||
/// \returns the loaded json schema as a json object as well as a related schema validator
|
||||
std::tuple<nlohmann::json, nlohmann::json_schema::json_validator> load_schema(const fs::path& path);
|
||||
|
||||
///
|
||||
/// \brief loads the config.json and manifest.json in the schemes subfolder of
|
||||
/// the provided \p schemas_dir
|
||||
///
|
||||
/// \returns the loaded configs and related validators
|
||||
SchemaValidation load_schemas(const fs::path& schemas_dir);
|
||||
|
||||
/// \brief Serializes the given \p module_configuration and related data to JSON.
|
||||
///
|
||||
/// Includes the module's config, mappings (own and connected), and telemetry if present.
|
||||
///
|
||||
/// \param module_id ID of the module to serialize.
|
||||
/// \param module_configurations Map of all module configurations.
|
||||
/// \return JSON object with the serialized module configuration.
|
||||
json get_serialized_module_config(std::string_view module_id, const ModuleConfigurations& module_configurations);
|
||||
|
||||
///
|
||||
/// \brief Base class for configs
|
||||
///
|
||||
class ConfigBase {
|
||||
protected:
|
||||
ModuleConfigurations module_configs;
|
||||
nlohmann::json settings;
|
||||
|
||||
nlohmann::json manifests;
|
||||
nlohmann::json interfaces;
|
||||
nlohmann::json interface_definitions;
|
||||
nlohmann::json types;
|
||||
Schemas schemas;
|
||||
// experimental caches
|
||||
std::map<std::string, std::string, std::less<>> module_names;
|
||||
|
||||
error::ErrorTypeMap error_map;
|
||||
|
||||
const MQTTSettings mqtt_settings;
|
||||
|
||||
public:
|
||||
///
|
||||
/// \brief Create a ConfigBase with the provided \p mqtt_settings
|
||||
explicit ConfigBase(const MQTTSettings& mqtt_settings) : mqtt_settings(mqtt_settings){};
|
||||
|
||||
///
|
||||
/// \brief turns then given \p module_id into a printable identifier
|
||||
///
|
||||
/// \returns a string with the printable identifier
|
||||
std::string printable_identifier(std::string_view module_id) const;
|
||||
|
||||
///
|
||||
/// \brief turns then given \p module_id and \p impl_id into a printable identifier
|
||||
///
|
||||
/// \returns a string with the printable identifier
|
||||
std::string printable_identifier(std::string_view module_id, std::string_view impl_id) const;
|
||||
|
||||
///
|
||||
/// \returns the module name matching the provided \p module_id
|
||||
std::string get_module_name(std::string_view module_id) const;
|
||||
|
||||
///
|
||||
/// \brief turns the given \p module_id and \p impl_id into a mqtt prefix
|
||||
///
|
||||
std::string mqtt_prefix(std::string_view module_id, std::string_view impl_id);
|
||||
|
||||
///
|
||||
/// \brief turns the given \p module_id into a mqtt prefix
|
||||
///
|
||||
std::string mqtt_module_prefix(std::string_view module_id) const;
|
||||
|
||||
///
|
||||
/// \returns a json object that contains the main config
|
||||
const ModuleConfigurations& get_module_configurations() const;
|
||||
|
||||
///
|
||||
/// \brief checks if the config contains the given \p module_id
|
||||
bool contains(std::string_view module_id) const;
|
||||
|
||||
///
|
||||
/// \returns a json object that contains the manifests
|
||||
const nlohmann::json& get_manifests() const;
|
||||
|
||||
///
|
||||
/// \returns a json object that contains the interface definitions
|
||||
const nlohmann::json& get_interface_definitions() const;
|
||||
|
||||
///
|
||||
/// \returns a json object that contains the available interfaces
|
||||
const nlohmann::json& get_interfaces() const;
|
||||
|
||||
///
|
||||
/// \returns a json object that contains the settings
|
||||
const nlohmann::json& get_settings() const;
|
||||
|
||||
///
|
||||
/// \returns an object that contains the schemas
|
||||
const Schemas& get_schemas() const;
|
||||
|
||||
///
|
||||
/// \returns an object that contains the error types
|
||||
const error::ErrorTypes& get_error_types() const;
|
||||
|
||||
///
|
||||
/// \returns a json object that contains the types
|
||||
const nlohmann::json& get_types() const;
|
||||
|
||||
///
|
||||
/// \return the cached mapping of module ids to module names
|
||||
const std::map<std::string, std::string, std::less<>>& get_module_names() const;
|
||||
|
||||
///
|
||||
/// \brief checks if the given \p module_id provides the requirement given in \p requirement_id
|
||||
///
|
||||
/// \returns a json object that contains the requirement
|
||||
std::vector<Fulfillment> resolve_requirement(std::string_view module_id, std::string_view 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(std::string_view module_id) const;
|
||||
|
||||
///
|
||||
/// \returns a list of Requirements for \p module_id
|
||||
std::list<Requirement> get_requirements(std::string_view 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(std::string_view module_id) const;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Config intended to be created by the manager for validation and serialization. Contains config and
|
||||
/// manifest parsing
|
||||
///
|
||||
class ManagerConfig : public ConfigBase {
|
||||
private:
|
||||
const ManagerSettings& ms;
|
||||
Validators validators;
|
||||
std::unique_ptr<nlohmann::json_schema::json_validator> draft7_validator;
|
||||
std::unique_ptr<everest::config::UserConfigStorage> user_config_storage;
|
||||
std::map<everest::config::ConfigurationParameterIdentifier, everest::config::GetConfigurationParameterResponse>
|
||||
database_get_config_parameter_response_cache;
|
||||
|
||||
nlohmann::json apply_user_config_and_defaults();
|
||||
|
||||
///
|
||||
/// \brief loads and validates the manifest of the \p module_config
|
||||
void load_and_validate_manifest(ModuleConfig& module_config);
|
||||
|
||||
///
|
||||
/// \brief loads and validates the given file \p file_path with the schema \p schema
|
||||
///
|
||||
/// \returns the loaded json and how long the validation took in ms
|
||||
std::tuple<nlohmann::json, int64_t> load_and_validate_with_schema(const fs::path& file_path,
|
||||
const nlohmann::json& schema);
|
||||
|
||||
///
|
||||
/// \brief resolves inheritance tree of json interface \p intf_name, throws an exception if variables or
|
||||
/// commands would be overwritten
|
||||
///
|
||||
/// \returns the resulting interface definition
|
||||
nlohmann::json resolve_interface(std::string_view intf_name);
|
||||
|
||||
///
|
||||
/// \brief loads the contents of the interface file referenced by the give \p intf_name from disk and validates
|
||||
/// its contents
|
||||
///
|
||||
/// \returns a json object containing the interface definition
|
||||
nlohmann::json load_interface_file(std::string_view intf_name);
|
||||
|
||||
///
|
||||
/// \brief loads the contents of an error or an error list referenced by the given \p reference.
|
||||
///
|
||||
/// \returns a list of json objects containing the error definitions
|
||||
std::list<nlohmann::json> resolve_error_ref(std::string_view reference);
|
||||
|
||||
///
|
||||
/// \brief replaces all error references in the given \p interface_json with the actual error definitions
|
||||
///
|
||||
/// \returns the interface_json with replaced error references
|
||||
nlohmann::json replace_error_refs(nlohmann::json& interface_json);
|
||||
|
||||
///
|
||||
/// \brief resolves all requirements (connections) of the modules in the main config
|
||||
void resolve_all_requirements();
|
||||
|
||||
///
|
||||
/// \brief parses the provided \p config resolving types, errors, manifests, requirements and 3 tier module
|
||||
/// mappings
|
||||
void parse(ModuleConfigurations& module_configs);
|
||||
|
||||
///
|
||||
/// \brief Parses the 3 tier model mappings in the config
|
||||
/// A "mapping" can be specified in the following way:
|
||||
/// You can set a EVSE id called "evse" and Connector id called "connector" for the whole module.
|
||||
/// Alternatively you can set individual mappings for implementations.
|
||||
/// mapping:
|
||||
/// module:
|
||||
/// evse: 1
|
||||
/// connector: 1
|
||||
/// implementations:
|
||||
/// implementation_id:
|
||||
/// evse: 1
|
||||
/// connector: 1
|
||||
/// If no mappings are found it will be assumed that the module is mapped to the charging station.
|
||||
/// If only a module mapping is defined alle implementations are mapped to this module mapping.
|
||||
/// Implementations can have overwritten mappings.
|
||||
void parse_3_tier_model_mapping();
|
||||
|
||||
public:
|
||||
///
|
||||
/// \brief Create a ManagerConfig from the provided ManagerSettings \p ms
|
||||
explicit ManagerConfig(const ManagerSettings& ms);
|
||||
|
||||
/// \brief Sets the config \p value associated with the \p identifier
|
||||
/// \returns if the setting of the value was successful or not
|
||||
everest::config::SetConfigStatus
|
||||
set_config_value(const everest::config::ConfigurationParameterIdentifier& identifier,
|
||||
const everest::config::ConfigEntry& value);
|
||||
|
||||
/// \brief Gets the configuration parameter associated with the \p identifier
|
||||
/// \returns a result containing the configuration item or an error
|
||||
everest::config::GetConfigurationParameterResponse
|
||||
get_config_value(const everest::config::ConfigurationParameterIdentifier& identifier);
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Contains intended to be used by modules using a pre-parsed and validated config json serialized from
|
||||
/// ManagerConfig
|
||||
///
|
||||
class Config : public ConfigBase {
|
||||
private:
|
||||
ModuleConfig module_config;
|
||||
std::map<std::string, ModuleTierMappings, std::less<>> tier_mappings;
|
||||
std::optional<TelemetryConfig> telemetry_config;
|
||||
std::map<std::string, ConfigCache, std::less<>> module_config_cache;
|
||||
|
||||
void populate_module_config_cache();
|
||||
|
||||
void populate_error_map();
|
||||
|
||||
public:
|
||||
///
|
||||
/// \brief creates a new Config object form the given \p mqtt_settings and \p config
|
||||
explicit Config(const MQTTSettings& mqtt_settings, const nlohmann::json& config);
|
||||
|
||||
///
|
||||
/// \returns object that contains the module config options
|
||||
ModuleConfig get_module_config() const;
|
||||
|
||||
///
|
||||
/// \returns the ErrorTypeMap
|
||||
error::ErrorTypeMap get_error_map() const;
|
||||
|
||||
///
|
||||
/// \returns true if the module \p module_name provides the implementation \p impl_id
|
||||
bool module_provides(std::string_view module_name, std::string_view impl_id);
|
||||
|
||||
///
|
||||
/// \returns the commands that the modules \p module_name implements from the given implementation \p impl_id
|
||||
const nlohmann::json& get_module_cmds(std::string_view module_name, std::string_view impl_id);
|
||||
|
||||
///
|
||||
/// \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(std::string_view module_id) const;
|
||||
|
||||
///
|
||||
/// \returns a map of module config options
|
||||
ModuleConfigs get_module_configs(std::string_view module_id) const;
|
||||
|
||||
//
|
||||
/// \returns the 3 tier model mappings for the given \p module_id
|
||||
std::optional<ModuleTierMappings> get_module_3_tier_model_mappings(std::string_view 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(std::string_view module_id, std::string_view impl_id) const;
|
||||
|
||||
///
|
||||
/// \brief assemble basic information about the module (id, name,
|
||||
/// authors, license)
|
||||
///
|
||||
/// \returns a ModuleInfo object
|
||||
ModuleInfo get_module_info(std::string_view module_id) const;
|
||||
|
||||
///
|
||||
/// \returns a TelemetryConfig if this has been configured
|
||||
std::optional<TelemetryConfig> get_telemetry_config();
|
||||
|
||||
///
|
||||
/// \returns a json object that contains the interface definition
|
||||
nlohmann::json get_interface_definition(std::string_view interface_name) const;
|
||||
|
||||
///
|
||||
/// \brief A json schema loader that can handle type refs and otherwise uses the builtin draft7 schema of
|
||||
/// the json schema validator when it encounters it. Throws an exception
|
||||
/// otherwise
|
||||
void ref_loader(const nlohmann::json_uri& uri, nlohmann::json& schema);
|
||||
|
||||
///
|
||||
/// \brief loads all module manifests relative to the \p main_dir
|
||||
///
|
||||
/// \returns all module manifests as a json object
|
||||
static nlohmann::json load_all_manifests(std::string_view modules_dir, std::string_view schemas_dir);
|
||||
|
||||
///
|
||||
/// \brief Extracts the keys of the provided json \p object
|
||||
///
|
||||
/// \returns a set of object keys
|
||||
static everest::config::Keys keys(const nlohmann::json& object);
|
||||
};
|
||||
} // namespace Everest
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
template <> struct adl_serializer<Everest::Schemas> {
|
||||
static void to_json(nlohmann::json& j, const Everest::Schemas& s);
|
||||
|
||||
static void from_json(const nlohmann::json& j, Everest::Schemas& s);
|
||||
};
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // UTILS_CONFIG_HPP
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_CONFIG_MQTT_SETTINGS_HPP
|
||||
#define UTILS_CONFIG_MQTT_SETTINGS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace Everest {
|
||||
|
||||
/// \brief minimal MQTT connection settings needed for an initial connection of a module to the manager
|
||||
struct MQTTSettings {
|
||||
std::string broker_socket_path; ///< A path to a socket the MQTT broker uses in socket mode. If this is set
|
||||
///< broker_host and broker_port are ignored
|
||||
std::string broker_host; ///< The hostname of the MQTT broker
|
||||
std::uint16_t broker_port = 0; ///< The port the MQTT broker listens on
|
||||
std::string everest_prefix; ///< MQTT topic prefix for the "everest" topic
|
||||
std::string external_prefix; ///< MQTT topic prefix for external topics
|
||||
|
||||
/// \brief Indicates if a Unix Domain Socket is used for connection to the MQTT broker
|
||||
/// \returns true is a UDS is used, false if a connection via host and port is used
|
||||
bool uses_socket() const;
|
||||
};
|
||||
|
||||
/// \brief Creates MQTTSettings with a Unix Domain Socket with the provided \p mqtt_broker_socket_path
|
||||
/// using the \p mqtt_everest_prefix and \p mqtt_external_prefix
|
||||
MQTTSettings create_mqtt_settings(const std::string& mqtt_broker_socket_path, const std::string& mqtt_everest_prefix,
|
||||
const std::string& mqtt_external_prefix);
|
||||
|
||||
/// \brief Creates MQTTSettings for IP based connections with the provided \p mqtt_broker_host
|
||||
/// and \p mqtt_broker_port using the \p mqtt_everest_prefix and \p mqtt_external_prefix
|
||||
MQTTSettings create_mqtt_settings(const std::string& mqtt_broker_host, std::uint16_t mqtt_broker_port,
|
||||
const std::string& mqtt_everest_prefix, const std::string& mqtt_external_prefix);
|
||||
|
||||
/// \brief Populates the given MQTTSettings \p mqtt_settings with a Unix Domain Socket with the provided \p
|
||||
/// mqtt_broker_socket_path using the \p mqtt_everest_prefix and \p mqtt_external_prefix
|
||||
void populate_mqtt_settings(MQTTSettings& mqtt_settings, const std::string& mqtt_broker_socket_path,
|
||||
const std::string& mqtt_everest_prefix, const std::string& mqtt_external_prefix);
|
||||
|
||||
/// \brief Populates the given MQTTSettings \p mqtt_settings for IP based connections with the provided \p
|
||||
/// mqtt_broker_host and \p mqtt_broker_port using the \p mqtt_everest_prefix and \p mqtt_external_prefix
|
||||
void populate_mqtt_settings(MQTTSettings& mqtt_settings, const std::string& mqtt_broker_host,
|
||||
std::uint16_t mqtt_broker_port, const std::string& mqtt_everest_prefix,
|
||||
const std::string& mqtt_external_prefix);
|
||||
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_CONFIG_MQTT_SETTINGS_HPP
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/config/mqtt_settings.hpp>
|
||||
#include <utils/config/storage_sqlite.hpp>
|
||||
#include <utils/config/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
enum class ConfigBootMode {
|
||||
YamlFile = 1, // configuration is loaded from a YAML file
|
||||
Database = 2, // configuration is loaded from a database
|
||||
DatabaseInit = 3 // configuration is preferably loaded from a database, but if no valid config is found, it falls
|
||||
// back to a YAML file and initializes the database
|
||||
};
|
||||
|
||||
/// \brief EVerest framework runtime settings needed to successfully run modules
|
||||
struct RuntimeSettings {
|
||||
fs::path prefix; ///< Prefix for EVerest installation
|
||||
fs::path etc_dir; ///< Directory that contains configs, certificates
|
||||
fs::path data_dir; ///< Directory for general data, definitions for EVerest interfaces, types, errors an schemas
|
||||
fs::path modules_dir; ///< Directory that contains EVerest modules
|
||||
fs::path logging_config_file; ///< Path to the logging configuration file
|
||||
std::string telemetry_prefix; ///< MQTT prefix for telemetry
|
||||
bool telemetry_enabled = false; ///< If telemetry is enabled
|
||||
bool validate_schema = false; ///< If schema validation for all var publishes and cmd calls is enabled
|
||||
bool forward_exceptions = false; ///< If exceptions in cmd handlers should be caught and forwarded to the caller
|
||||
};
|
||||
|
||||
RuntimeSettings create_runtime_settings(const fs::path& prefix, const fs::path& etc_dir, const fs::path& data_dir,
|
||||
const fs::path& modules_dir, const fs::path& logging_config_file,
|
||||
const std::string& telemetry_prefix, bool telemetry_enabled,
|
||||
bool validate_schema, bool forward_exceptions);
|
||||
void populate_runtime_settings(RuntimeSettings& runtime_settings, const fs::path& prefix, const fs::path& etc_dir,
|
||||
const fs::path& data_dir, const fs::path& modules_dir,
|
||||
const fs::path& logging_config_file, const std::string& telemetry_prefix,
|
||||
bool telemetry_enabled, bool validate_schema, bool forward_exceptions);
|
||||
|
||||
struct DatabaseTag {};
|
||||
|
||||
/// \brief Settings needed by the manager to load and validate a config
|
||||
struct ManagerSettings {
|
||||
fs::path configs_dir; ///< Directory that contains EVerest configs
|
||||
fs::path db_dir; ///< Directory that contains the database
|
||||
fs::path schemas_dir; ///< Directory that contains schemas for config, manifest, interfaces, etc.
|
||||
fs::path interfaces_dir; ///< Directory that contains interface definitions
|
||||
fs::path types_dir; ///< Directory that contains type definitions
|
||||
fs::path errors_dir; ///< Directory that contains error definitions
|
||||
fs::path config_file; ///< Path to the loaded config file
|
||||
fs::path www_dir; ///< Directory that contains the everest-admin-panel
|
||||
int controller_port = 0; ///< Websocket port of the controller
|
||||
int controller_rpc_timeout_ms = 0; ///< RPC timeout for controller commands
|
||||
|
||||
std::string run_as_user; ///< Username under which EVerest should run
|
||||
|
||||
std::string version_information; ///< Version information string reported on startup of the manager
|
||||
|
||||
nlohmann::json config; ///< Parsed json of the config_file
|
||||
|
||||
MQTTSettings mqtt_settings; ///< MQTT connection settings
|
||||
RuntimeSettings runtime_settings; ///< Runtime settings needed to successfully run modules
|
||||
ConfigBootMode boot_mode =
|
||||
ConfigBootMode::YamlFile; ///< Source of the config, can be YamlFile, Database or DatabaseInit
|
||||
std::unique_ptr<everest::config::SqliteStorage> storage; ///< Sqlite Storage for settings and module configs
|
||||
|
||||
ManagerSettings() = default;
|
||||
|
||||
/// \brief Constructor that initializes the ManagerSettings with the given prefix and config file. Boot source is
|
||||
/// set to YamlFile.
|
||||
ManagerSettings(const std::string& prefix, const std::string& config);
|
||||
|
||||
/// \brief Constructor that initializes the ManagerSettings with the given database path. Boot source is set to
|
||||
/// Database.
|
||||
ManagerSettings(const std::string& prefix, const std::string& db, DatabaseTag);
|
||||
|
||||
/// \brief Constructor that initializes the ManagerSettings with the given prefix, config file and database path.
|
||||
/// Boot Source is set to DatabaseInit.
|
||||
ManagerSettings(const std::string& prefix, const std::string& config, const std::string& db);
|
||||
|
||||
/// \brief Initializes the ManagerSettings with the given settings and prefix.
|
||||
void init_settings(const everest::config::Settings& settings);
|
||||
|
||||
/// \brief Initializes the ManagerSettings based on the user provided \p config file or fallback options
|
||||
void init_config_file(const std::string& config);
|
||||
|
||||
/// \brief Initializes the ManagerSettings prefix and data_dir base on user provided \p prefix or the default
|
||||
/// prefix.
|
||||
void init_prefix_and_data_dir(const std::string& prefix);
|
||||
};
|
||||
} // namespace Everest
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
template <> struct adl_serializer<Everest::RuntimeSettings> {
|
||||
static void to_json(nlohmann::json& j, const Everest::RuntimeSettings& r);
|
||||
|
||||
static void from_json(const nlohmann::json& j, Everest::RuntimeSettings& r);
|
||||
};
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/config/storage_types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
struct ManagerSettings;
|
||||
}
|
||||
|
||||
namespace everest::config {
|
||||
|
||||
/// \brief EVerest Storage Interface providing read and write access to configurations
|
||||
class StorageInterface {
|
||||
public:
|
||||
virtual ~StorageInterface() = default;
|
||||
|
||||
/// \brief Writes given EVerest \p module_configs to persistent storage
|
||||
/// \param config EVerest config
|
||||
/// \return
|
||||
virtual GenericResponseStatus write_module_configs(const ModuleConfigurations& module_configs) = 0;
|
||||
|
||||
/// \brief Writes EVerest config \p settings to persistent storage
|
||||
/// \param settings EVerest settings configuration
|
||||
/// \return
|
||||
virtual GenericResponseStatus write_settings(const Everest::ManagerSettings& manager_settings) = 0;
|
||||
|
||||
/// \brief Wipes all configuration entries from persistent storage
|
||||
virtual GenericResponseStatus wipe() = 0;
|
||||
|
||||
/// \brief Gets EVerest config from persistent storage
|
||||
/// \return Response with status of operation and config. config is only set if status is OK. Config contains all
|
||||
/// module configurations and manager settings
|
||||
virtual GetModuleConfigsResponse get_module_configs() = 0;
|
||||
|
||||
/// \brief Gets EVerest manager settings from persistent storage
|
||||
/// \return
|
||||
virtual GetSettingsResponse get_settings() = 0;
|
||||
|
||||
/// \brief Gets EVerest config from persistent storage for a single module
|
||||
/// \param module_id
|
||||
/// \return Response with status of operation and module config. config is only set if status is OK
|
||||
virtual GetModuleConfigurationResponse get_module_config(const std::string& module_id) = 0;
|
||||
|
||||
/// \brief Gets single configuration parameter from persistent storage
|
||||
/// \param identifier Identifies configuration parameter
|
||||
/// \return Response with status of operation and configuration parameter. configuration parameter is only set if
|
||||
/// status is OK.
|
||||
virtual GetConfigurationParameterResponse
|
||||
get_configuration_parameter(const ConfigurationParameterIdentifier& identifier) = 0;
|
||||
|
||||
/// \brief Writes single configuration parameter to persistent storage
|
||||
/// \param identifier Identifies configuration parameter
|
||||
/// \param value
|
||||
/// \return Response with status of operation
|
||||
virtual GetSetResponseStatus update_configuration_parameter(const ConfigurationParameterIdentifier& identifier,
|
||||
const std::string& value) = 0;
|
||||
|
||||
/// \brief Writes single configuration parameter including characteristics to persistent storage
|
||||
/// \param identifier Identifies configuration parameter
|
||||
/// \param characteristics of the configuration parameter
|
||||
/// \param value
|
||||
/// \return Response with status of operation
|
||||
virtual GetSetResponseStatus
|
||||
write_configuration_parameter(const ConfigurationParameterIdentifier& identifier,
|
||||
const ConfigurationParameterCharacteristics characteristics,
|
||||
const std::string& value) = 0;
|
||||
};
|
||||
|
||||
} // namespace everest::config
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <everest/database/sqlite/connection.hpp>
|
||||
#include <utils/config/storage.hpp>
|
||||
|
||||
namespace everest::config {
|
||||
|
||||
/// \brief Implements StorageInterface with SQLite
|
||||
class SqliteStorage : public StorageInterface {
|
||||
|
||||
public:
|
||||
/// \brief Constructor
|
||||
/// \param db_path Path to SQLite database file
|
||||
/// \param migration_files_path Path to SQL migration files
|
||||
/// \throws MigrationException if migration fails
|
||||
/// \throws std::runtime_error if database cannot be opened
|
||||
SqliteStorage(const fs::path& db_path, const std::filesystem::path& migration_files_path);
|
||||
|
||||
GenericResponseStatus write_module_configs(const ModuleConfigurations& module_configs) override;
|
||||
GenericResponseStatus write_settings(const Everest::ManagerSettings& manager_settings) override;
|
||||
GenericResponseStatus wipe() override;
|
||||
GetModuleConfigsResponse get_module_configs() override;
|
||||
GetSettingsResponse get_settings() override;
|
||||
GetModuleConfigurationResponse get_module_config(const std::string& module_id) override;
|
||||
GetConfigurationParameterResponse
|
||||
get_configuration_parameter(const ConfigurationParameterIdentifier& identifier) override;
|
||||
GetSetResponseStatus update_configuration_parameter(const ConfigurationParameterIdentifier& identifier,
|
||||
const std::string& value) override;
|
||||
GetSetResponseStatus write_configuration_parameter(const ConfigurationParameterIdentifier& identifier,
|
||||
const ConfigurationParameterCharacteristics characteristics,
|
||||
const std::string& value) override;
|
||||
|
||||
/// \brief Checks if the database contains a valid configuration
|
||||
bool contains_valid_config();
|
||||
|
||||
/// \brief Marks the current configuration as valid
|
||||
/// \param is_valid True if the configuration is valid, false otherwise
|
||||
/// \param config_dump JSON dump of the config file that was used to create the configuration
|
||||
/// \param yaml_file_path Path to the config file that was used to create the configuration
|
||||
void mark_valid(const bool is_valid, const std::string& config_dump,
|
||||
const std::optional<fs::path>& config_file_path);
|
||||
|
||||
private:
|
||||
std::unique_ptr<everest::db::sqlite::ConnectionInterface> db;
|
||||
GenericResponseStatus write_module_data(const ModuleData& module_info);
|
||||
GenericResponseStatus write_module_fulfillment(const std::string& module_id, const Fulfillment& fulfillment);
|
||||
GenericResponseStatus write_module_tier_mapping(const std::string& module_id, const std::string& implementation_id,
|
||||
const int32_t evse_id, const std::optional<int32_t> connector_id);
|
||||
GenericResponseStatus write_access(const std::string& module_id, const Access& access);
|
||||
GenericResponseStatus write_config_access(const std::string& module_id, const ConfigAccess& config_access);
|
||||
GenericResponseStatus write_module_config_access(const std::string& module_id, const std::string& other_module_id,
|
||||
const ModuleConfigAccess& module_config_access);
|
||||
GenericResponseStatus write_setting(const std::string& setting_name, const std::string& value);
|
||||
GetModuleFulfillmentsResponse get_module_fulfillments(const std::string& module_id);
|
||||
GetModuleDataResponse get_module_data(const std::string& module_id);
|
||||
GetModuleTierMappingsResponse get_module_tier_mappings(const std::string& module_id);
|
||||
GetModuleConfigAccessResponse get_module_config_access(const std::string& module_id);
|
||||
GetConfigAccessResponse get_config_access(const std::string& module_id);
|
||||
};
|
||||
|
||||
} // namespace everest::config
|
||||
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
#include <utils/config/types.hpp>
|
||||
|
||||
namespace everest::config {
|
||||
|
||||
enum class GenericResponseStatus {
|
||||
OK,
|
||||
Failed
|
||||
};
|
||||
|
||||
enum class GetSetResponseStatus {
|
||||
OK,
|
||||
Failed,
|
||||
NotFound
|
||||
};
|
||||
|
||||
struct ModuleData {
|
||||
std::string module_id;
|
||||
std::string module_name;
|
||||
bool standalone = false;
|
||||
std::optional<std::vector<std::string>> capabilities;
|
||||
};
|
||||
|
||||
struct GetConfigurationParameterResponse {
|
||||
GetSetResponseStatus status = GetSetResponseStatus::Failed;
|
||||
std::optional<ConfigurationParameter> configuration_parameter;
|
||||
};
|
||||
|
||||
struct GetModuleConfigsResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
ModuleConfigurations module_configs;
|
||||
};
|
||||
|
||||
struct GetSettingsResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
std::optional<Settings> settings;
|
||||
};
|
||||
|
||||
struct GetModuleFulfillmentsResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
std::vector<Fulfillment> module_fulfillments;
|
||||
};
|
||||
|
||||
struct GetModuleTierMappingsResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
ModuleTierMappings module_tier_mappings;
|
||||
};
|
||||
|
||||
struct GetConfigAccessResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
std::optional<ConfigAccess> config_access;
|
||||
};
|
||||
|
||||
struct GetModuleConfigAccessResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
std::map<std::string, everest::config::ModuleConfigAccess> module_config_access;
|
||||
};
|
||||
|
||||
struct GetModuleConfigurationResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
std::optional<ModuleConfig> config;
|
||||
};
|
||||
|
||||
struct GetModuleDataResponse {
|
||||
GenericResponseStatus status = GenericResponseStatus::Failed;
|
||||
std::optional<ModuleData> module_data;
|
||||
};
|
||||
|
||||
struct ConfigurationParameterIdentifier {
|
||||
std::string module_id;
|
||||
std::string configuration_parameter_name;
|
||||
std::optional<std::string> module_implementation_id;
|
||||
|
||||
bool operator<(const ConfigurationParameterIdentifier& rhs) const;
|
||||
};
|
||||
|
||||
} // namespace everest::config
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/config/storage.hpp>
|
||||
|
||||
namespace everest::config {
|
||||
|
||||
/// \brief Implements StorageInterface with YAML user-config
|
||||
class UserConfigStorage : public StorageInterface {
|
||||
|
||||
public:
|
||||
/// \brief Constructor
|
||||
/// \param user_config_path Path to user-config file
|
||||
UserConfigStorage(const fs::path& user_config_path);
|
||||
|
||||
GenericResponseStatus write_module_configs(const ModuleConfigurations& module_configs) override;
|
||||
GenericResponseStatus write_settings(const Everest::ManagerSettings& manager_settings) override;
|
||||
GenericResponseStatus wipe() override;
|
||||
GetModuleConfigsResponse get_module_configs() override;
|
||||
GetSettingsResponse get_settings() override;
|
||||
GetModuleConfigurationResponse get_module_config(const std::string& module_id) override;
|
||||
GetConfigurationParameterResponse
|
||||
get_configuration_parameter(const ConfigurationParameterIdentifier& identifier) override;
|
||||
GetSetResponseStatus update_configuration_parameter(const ConfigurationParameterIdentifier& identifier,
|
||||
const std::string& value) override;
|
||||
GetSetResponseStatus write_configuration_parameter(const ConfigurationParameterIdentifier& identifier,
|
||||
const ConfigurationParameterCharacteristics characteristics,
|
||||
const std::string& value) override;
|
||||
|
||||
const nlohmann::json& get_user_config() const;
|
||||
|
||||
private:
|
||||
fs::path user_config_path;
|
||||
nlohmann::json user_config;
|
||||
};
|
||||
|
||||
} // namespace everest::config
|
||||
@@ -0,0 +1,281 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
class ConfigParseException : public std::exception {
|
||||
public:
|
||||
enum ParseErrorType {
|
||||
MISSING_ENTRY,
|
||||
SCHEMA
|
||||
};
|
||||
ConfigParseException(ParseErrorType err_t, const std::string& entry, const std::string& what = "") :
|
||||
err_t(err_t), entry(entry), what(what){};
|
||||
|
||||
const ParseErrorType err_t;
|
||||
const std::string entry;
|
||||
const std::string what;
|
||||
};
|
||||
|
||||
/// \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) {
|
||||
if (mapping.has_value()) {
|
||||
os << mapping.value();
|
||||
} else {
|
||||
os << "Mapping(charging station)";
|
||||
}
|
||||
|
||||
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::map<std::string, std::optional<Mapping>, std::less<>>
|
||||
implementations; ///< Mappings for the individual implementations of the module
|
||||
};
|
||||
|
||||
/// \brief A Requirement of an EVerest module
|
||||
struct Requirement {
|
||||
std::string id;
|
||||
size_t index = 0;
|
||||
};
|
||||
|
||||
bool operator<(const Requirement& lhs, const Requirement& rhs);
|
||||
|
||||
/// \brief A Fulfillment relates a Requirement to its connected implementation, identified via its module and
|
||||
/// implementation id.
|
||||
struct Fulfillment {
|
||||
std::string module_id; // the id of the module that fulfills the requirement
|
||||
std::string implementation_id; // the id of the implementation that fulfills the requirement
|
||||
Requirement requirement; // the requirement of the module that is fulfilled
|
||||
};
|
||||
|
||||
struct TelemetryConfig {
|
||||
int id;
|
||||
explicit TelemetryConfig(int id) : id(id) {
|
||||
}
|
||||
};
|
||||
|
||||
namespace everest::config {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct ConfigurationParameter;
|
||||
struct ModuleConfig;
|
||||
using ModuleId = std::string;
|
||||
using RequirementId = std::string;
|
||||
using ConfigEntry = std::variant<std::string, bool, int, double>;
|
||||
using ImplementationIdentifier = std::string;
|
||||
using ModuleConnections = std::map<RequirementId, std::vector<Fulfillment>, std::less<>>;
|
||||
using ModuleConfigurations = std::map<ModuleId, ModuleConfig, std::less<>>;
|
||||
using ModuleConfigurationParameters = std::map<ImplementationIdentifier, std::vector<ConfigurationParameter>>;
|
||||
using Keys = std::set<std::string, std::less<>>;
|
||||
|
||||
struct VisitConfigEntry {
|
||||
std::string operator()(const std::string& value) const {
|
||||
return value;
|
||||
};
|
||||
std::string operator()(bool value) const {
|
||||
return value ? "true" : "false";
|
||||
};
|
||||
std::string operator()(int value) const {
|
||||
return std::to_string(value);
|
||||
};
|
||||
std::string operator()(double value) const {
|
||||
return std::to_string(value);
|
||||
};
|
||||
};
|
||||
|
||||
std::string config_entry_to_string(const everest::config::ConfigEntry& entry);
|
||||
|
||||
enum class Mutability {
|
||||
ReadOnly,
|
||||
ReadWrite,
|
||||
WriteOnly
|
||||
};
|
||||
|
||||
enum class Datatype {
|
||||
Unknown,
|
||||
String,
|
||||
Decimal,
|
||||
Integer,
|
||||
Boolean
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
std::optional<fs::path> prefix;
|
||||
std::optional<fs::path> config_file;
|
||||
std::optional<fs::path> configs_dir;
|
||||
std::optional<fs::path> schemas_dir;
|
||||
std::optional<fs::path> modules_dir;
|
||||
std::optional<fs::path> interfaces_dir;
|
||||
std::optional<fs::path> types_dir;
|
||||
std::optional<fs::path> errors_dir;
|
||||
std::optional<fs::path> www_dir;
|
||||
std::optional<fs::path> logging_config_file;
|
||||
std::optional<int> controller_port;
|
||||
std::optional<int> controller_rpc_timeout_ms;
|
||||
std::optional<std::string> mqtt_broker_socket_path;
|
||||
std::optional<std::string> mqtt_broker_host;
|
||||
std::optional<std::uint16_t> mqtt_broker_port;
|
||||
std::optional<std::string> mqtt_everest_prefix;
|
||||
std::optional<std::string> mqtt_external_prefix;
|
||||
std::optional<std::string> telemetry_prefix;
|
||||
std::optional<bool> telemetry_enabled;
|
||||
std::optional<bool> validate_schema;
|
||||
std::optional<std::string> run_as_user;
|
||||
std::optional<bool> forward_exceptions;
|
||||
};
|
||||
|
||||
/// \brief Struct that contains the characteristics of a configuration parameter including its datatype, mutability and
|
||||
/// unit
|
||||
struct ConfigurationParameterCharacteristics {
|
||||
Datatype datatype = Datatype::Unknown;
|
||||
Mutability mutability = Mutability::ReadOnly;
|
||||
std::optional<std::string> unit;
|
||||
};
|
||||
|
||||
/// \brief Struct that contains the name, value and characteristics of a configuration parameter
|
||||
struct ConfigurationParameter {
|
||||
std::string name;
|
||||
ConfigEntry value;
|
||||
ConfigurationParameterCharacteristics characteristics;
|
||||
|
||||
bool validate_type() const;
|
||||
};
|
||||
|
||||
/// \brief Access control information to an individual module config
|
||||
struct ModuleConfigAccess {
|
||||
bool allow_read = false; ///< Allow read access to config items
|
||||
bool allow_write = false; ///< Allow write access to config items
|
||||
|
||||
bool allow_set_read_only = false; ///< If ReadOnly config items can be treated as ReadWrite (this typically requires
|
||||
///< a reboot to have an effect)
|
||||
};
|
||||
|
||||
/// \brief
|
||||
struct ConfigAccess {
|
||||
bool allow_global_read = false; ///< Allow this module to read the config items of all other modules
|
||||
bool allow_global_write = false; ///< Allow this module to write the config items of all other modules
|
||||
bool allow_set_read_only = false; ///< If ReadOnly config items can be treated as ReadWrite (this typically requires
|
||||
///< a reboot to have an effect)
|
||||
std::map<std::string, everest::config::ModuleConfigAccess>
|
||||
modules; ///< Individual access to other modules config. The key represents the other modules module_id
|
||||
///< and the value the associated access rights
|
||||
};
|
||||
|
||||
/// \brief Access control information for a particular module
|
||||
struct Access {
|
||||
std::optional<ConfigAccess> config; ///< Access control to other modules configuration items
|
||||
};
|
||||
|
||||
/// \brief Struct that contains the configuration of an EVerest module
|
||||
struct ModuleConfig {
|
||||
bool standalone = false;
|
||||
std::string module_name;
|
||||
std::string module_id;
|
||||
std::optional<std::vector<std::string>> capabilities;
|
||||
ModuleConfigurationParameters configuration_parameters; // contains: config_module and config_implementations
|
||||
// as well as the upcoming "config" key
|
||||
bool telemetry_enabled = false;
|
||||
std::optional<TelemetryConfig> telemetry_config;
|
||||
ModuleConnections connections;
|
||||
ModuleTierMappings mapping;
|
||||
Access access;
|
||||
};
|
||||
|
||||
enum class SetConfigStatus {
|
||||
Accepted,
|
||||
Rejected,
|
||||
RebootRequired
|
||||
};
|
||||
|
||||
ConfigEntry parse_config_value(Datatype datatype, const std::string& value_str);
|
||||
ModuleConfigurations parse_module_configs(const nlohmann::json& config);
|
||||
Settings parse_settings(const nlohmann::json& settings_json);
|
||||
|
||||
Datatype string_to_datatype(const std::string& str);
|
||||
std::string datatype_to_string(const Datatype datatype);
|
||||
|
||||
Mutability string_to_mutability(const std::string& str);
|
||||
std::string mutability_to_string(const Mutability mutability);
|
||||
|
||||
ModuleTierMappings parse_mapping(const nlohmann::json& mapping_json);
|
||||
|
||||
} // namespace everest::config
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
template <> struct adl_serializer<everest::config::ModuleConfig> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::ModuleConfig& m);
|
||||
static void from_json(const nlohmann::json& j, everest::config::ModuleConfig& m);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<everest::config::ConfigurationParameterCharacteristics> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::ConfigurationParameterCharacteristics& c);
|
||||
static void from_json(const nlohmann::json& j, everest::config::ConfigurationParameterCharacteristics& c);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<everest::config::ConfigEntry> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::ConfigEntry& entry);
|
||||
static void from_json(const nlohmann::json& j, everest::config::ConfigEntry& entry);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<everest::config::ConfigurationParameter> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::ConfigurationParameter& p);
|
||||
static void from_json(const nlohmann::json& j, everest::config::ConfigurationParameter& p);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<everest::config::ModuleConfigAccess> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::ModuleConfigAccess& m);
|
||||
static void from_json(const nlohmann::json& j, everest::config::ModuleConfigAccess& m);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<everest::config::ConfigAccess> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::ConfigAccess& c);
|
||||
static void from_json(const nlohmann::json& j, everest::config::ConfigAccess& c);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<everest::config::Access> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::Access& c);
|
||||
static void from_json(const nlohmann::json& j, everest::config::Access& c);
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_CONFIG_CACHE_HPP
|
||||
#define UTILS_CONFIG_CACHE_HPP
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
struct ConfigCache {
|
||||
std::set<std::string, std::less<>> provides_impl;
|
||||
std::map<std::string, nlohmann::json, std::less<>> cmds;
|
||||
};
|
||||
|
||||
} // namespace Everest
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
template <> struct adl_serializer<Everest::ConfigCache> {
|
||||
static void to_json(nlohmann::json& j, const Everest::ConfigCache& c);
|
||||
|
||||
static void from_json(const nlohmann::json& j, Everest::ConfigCache& c);
|
||||
};
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // UTILS_CONFIG_CACHE_HPP
|
||||
@@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <utils/config.hpp>
|
||||
#include <utils/mqtt_abstraction.hpp>
|
||||
namespace Everest {
|
||||
namespace config {
|
||||
|
||||
constexpr auto MODULE_IMPLEMENTATION_ID = "!module";
|
||||
inline constexpr std::size_t mqtt_get_config_retries = 1;
|
||||
|
||||
/// \brief The type of request or response
|
||||
enum class Type {
|
||||
Get, ///< Identifies a get request or response
|
||||
Set, ///< Identifies a set request or response
|
||||
Unknown ///< Used for unknown requests that could not be parsed
|
||||
};
|
||||
|
||||
/// \brief Possible get request and response sub-types
|
||||
enum class GetType {
|
||||
All, ///< All module configurations that the requesting module has access to
|
||||
Module, ///< The module configuration for the requesting module
|
||||
Value, ///< A specific configuration value identified by a ConfigurationParameterIdentifier
|
||||
AllMappings, ///< All module mappings that the requesting module has access to
|
||||
Unknown ///< Used for unknown requests that could not be parsed
|
||||
|
||||
// TODO: Potential additions in the future:
|
||||
// Delta, // This would need tracking of when the last Request was made
|
||||
};
|
||||
|
||||
/// \brief Represents a get request
|
||||
struct GetRequest {
|
||||
GetType type = GetType::Unknown; ///< The type of get request
|
||||
std::optional<everest::config::ConfigurationParameterIdentifier> identifier; ///< Used for GetType::Value
|
||||
|
||||
// TODO: Potential additions in the future:
|
||||
// optional timestamp for GetType::Delta?
|
||||
// a list of requested modules?
|
||||
};
|
||||
|
||||
/// \brief Represents a response to a get request
|
||||
struct GetResponse {
|
||||
GetType type = GetType::Unknown; ///< The type of get response, the same as in the get request
|
||||
nlohmann::json data; ///< Data associated with this reponse.
|
||||
// FIXME: use proper type(s) for data?
|
||||
};
|
||||
|
||||
/// \brief Represents a set request
|
||||
struct SetRequest {
|
||||
everest::config::ConfigurationParameterIdentifier
|
||||
identifier; ///< An identifier for the configuration parameter to be set
|
||||
std::string value; ///< The string representation of the configuration value to be set
|
||||
// TODO: should value be a ConfigEntry variant type?
|
||||
};
|
||||
|
||||
/// \brief Possible response status values
|
||||
enum class ResponseStatus {
|
||||
Ok, ///< Everything worked
|
||||
Error, ///< There was an error during handling of the request
|
||||
AccessDenied ///< There was an access error during handling of the request
|
||||
};
|
||||
|
||||
/// \brief Possible set response status values
|
||||
enum class SetResponseStatus {
|
||||
Accepted, ///< Configuration value was set successfully
|
||||
Rejected, ///< Configuration value could not be set
|
||||
RebootRequired ///< Configuration value was set successfully but a reboot is required for modules to actually use
|
||||
///< this value
|
||||
};
|
||||
|
||||
/// \brief Represents a response to a set request
|
||||
struct SetResponse {
|
||||
SetResponseStatus status = SetResponseStatus::Rejected; ///< Status of the set response
|
||||
std::string status_info; ///< Can contain additional status information
|
||||
};
|
||||
|
||||
/// \brief Represents a container for various requests that can be made to the ConfigService
|
||||
struct Request {
|
||||
Type type = Type::Unknown; ///< The type of request
|
||||
std::variant<std::monostate, GetRequest, SetRequest> request; ///< The request itself
|
||||
std::string origin; ///< The origin of the request, the module id of the requesting module
|
||||
};
|
||||
|
||||
/// \brief Represents a container for various responses to requests made to the ConfigService
|
||||
struct Response {
|
||||
ResponseStatus status = ResponseStatus::Error; ///< Status of the response
|
||||
std::string status_info; ///< Can contain additional status information
|
||||
std::optional<Type> type; ///< The type of the response, identical to the request, missing when status is Error
|
||||
std::variant<std::monostate, GetResponse, SetResponse> response; ///< The response itself
|
||||
};
|
||||
|
||||
/// \brief Represents a container for getting a configuration parameter
|
||||
struct GetConfigResult {
|
||||
ResponseStatus status = ResponseStatus::Error; ///< Status of the result
|
||||
std::string status_info; ///< Can contain additional status information
|
||||
everest::config::ConfigurationParameter configuration_parameter; ///< The requested configuration parameter
|
||||
};
|
||||
|
||||
/// \brief Represents a container for the result of setting a configuration parameter
|
||||
struct SetConfigResult {
|
||||
ResponseStatus status = ResponseStatus::Error; ///< Status of the result
|
||||
std::string status_info; ///< Can contain additional status information
|
||||
everest::config::SetConfigStatus set_status =
|
||||
everest::config::SetConfigStatus::Rejected; ///< Specific status for the resut of setting the config parameter
|
||||
};
|
||||
|
||||
/// \brief Represents a compound type to identify a specific module instance and its type
|
||||
struct ModuleIdType {
|
||||
std::string module_id; ///< The module id
|
||||
std::string module_type; ///< The associated module type
|
||||
|
||||
bool operator<(const ModuleIdType& rhs) const;
|
||||
};
|
||||
|
||||
class ConfigServiceClient {
|
||||
public:
|
||||
/// \brief ConfigService client using the provided \p mqtt_abstraction for the module identified by \p module_id
|
||||
/// \p module_names is a mapping of all module ids to module names/types for usage in get_module_configs()
|
||||
ConfigServiceClient(std::shared_ptr<MQTTAbstraction> mqtt_abstraction, const std::string& module_id,
|
||||
const std::map<std::string, std::string, std::less<>>& module_names);
|
||||
|
||||
/// \brief Compiles and \returns all module configs that this module has access to
|
||||
std::map<ModuleIdType, everest::config::ModuleConfigurationParameters> get_module_configs();
|
||||
|
||||
/// \brief Compiles and \returns all mappings of modules that this module has access to
|
||||
std::map<std::string, ModuleTierMappings> get_mappings();
|
||||
|
||||
/// \brief Sets the config \p value associated with the \p identifier
|
||||
/// \returns a result containing status and potential error information
|
||||
SetConfigResult set_config_value(const everest::config::ConfigurationParameterIdentifier& identifier,
|
||||
const std::string& value);
|
||||
|
||||
/// \brief Gets the config value associated with the \p identifier
|
||||
/// \returns a result containing the configuration item or an error
|
||||
GetConfigResult get_config_value(const everest::config::ConfigurationParameterIdentifier& identifier);
|
||||
|
||||
private:
|
||||
std::shared_ptr<MQTTAbstraction> mqtt_abstraction;
|
||||
std::string origin;
|
||||
std::map<std::string, std::string, std::less<>> module_names;
|
||||
};
|
||||
|
||||
class ConfigService {
|
||||
public:
|
||||
/// \brief ConfigService using the provided \p mqtt_abstraction to distribute relevant parts of the given \p config
|
||||
/// when another module requests them and has appropriate access rights to them
|
||||
ConfigService(MQTTAbstraction& mqtt_abstraction, std::shared_ptr<ManagerConfig> config);
|
||||
|
||||
private:
|
||||
MQTTAbstraction& mqtt_abstraction;
|
||||
std::shared_ptr<TypedHandler> get_config_token;
|
||||
std::shared_ptr<ManagerConfig> config;
|
||||
};
|
||||
|
||||
namespace conversions {
|
||||
std::string type_to_string(Type type);
|
||||
|
||||
Type string_to_type(const std::string& type);
|
||||
|
||||
std::string get_type_to_string(GetType type);
|
||||
|
||||
GetType string_to_get_type(const std::string& type);
|
||||
|
||||
std::string response_status_to_string(ResponseStatus status);
|
||||
|
||||
ResponseStatus string_to_response_status(const std::string& status);
|
||||
|
||||
std::string set_response_status_to_string(SetResponseStatus status);
|
||||
|
||||
SetResponseStatus string_to_set_response_status(const std::string& status);
|
||||
|
||||
everest::config::SetConfigStatus set_response_status_to_set_config_status(SetResponseStatus status);
|
||||
|
||||
SetResponseStatus set_config_status_to_set_response_status(everest::config::SetConfigStatus status);
|
||||
} // namespace conversions
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const GetType& t);
|
||||
|
||||
void to_json(nlohmann::json& j, const GetRequest& r);
|
||||
|
||||
void from_json(const nlohmann::json& j, GetRequest& r);
|
||||
|
||||
void to_json(nlohmann::json& j, const SetRequest& r);
|
||||
|
||||
void from_json(const nlohmann::json& j, SetRequest& r);
|
||||
|
||||
void to_json(nlohmann::json& j, const GetResponse& r);
|
||||
|
||||
void from_json(const nlohmann::json& j, GetResponse& r);
|
||||
|
||||
void to_json(nlohmann::json& j, const SetResponse& r);
|
||||
|
||||
void from_json(const nlohmann::json& j, SetResponse& r);
|
||||
|
||||
void to_json(nlohmann::json& j, const Request& r);
|
||||
|
||||
void from_json(const nlohmann::json& j, Request& r);
|
||||
|
||||
void to_json(nlohmann::json& j, const Response& r);
|
||||
|
||||
void from_json(const nlohmann::json& j, Response& r);
|
||||
} // namespace config
|
||||
} // namespace Everest
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
template <> struct adl_serializer<everest::config::ConfigurationParameterIdentifier> {
|
||||
static void to_json(nlohmann::json& j, const everest::config::ConfigurationParameterIdentifier& c);
|
||||
static void from_json(const nlohmann::json& j, everest::config::ConfigurationParameterIdentifier& c);
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_CONVERSIONS_HPP
|
||||
#define UTILS_CONVERSIONS_HPP
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename FundamentalType> constexpr bool is_type_compatible(nlohmann::json::value_t json_type);
|
||||
|
||||
template <> constexpr inline bool is_type_compatible<std::nullptr_t>(nlohmann::json::value_t json_type) {
|
||||
return json_type == nlohmann::json::value_t::null;
|
||||
}
|
||||
|
||||
template <> constexpr inline bool is_type_compatible<bool>(nlohmann::json::value_t json_type) {
|
||||
return json_type == nlohmann::json::value_t::boolean;
|
||||
}
|
||||
|
||||
template <> constexpr inline bool is_type_compatible<int>(nlohmann::json::value_t json_type) {
|
||||
return json_type == nlohmann::json::value_t::number_integer ||
|
||||
json_type == nlohmann::json::value_t::number_unsigned;
|
||||
}
|
||||
|
||||
template <> constexpr inline bool is_type_compatible<double>(nlohmann::json::value_t json_type) {
|
||||
return json_type == nlohmann::json::value_t::number_float;
|
||||
}
|
||||
|
||||
template <> constexpr inline bool is_type_compatible<std::string>(nlohmann::json::value_t json_type) {
|
||||
return json_type == nlohmann::json::value_t::string;
|
||||
}
|
||||
|
||||
template <> constexpr inline bool is_type_compatible<nlohmann::json::array_t>(nlohmann::json::value_t json_type) {
|
||||
return json_type == nlohmann::json::value_t::array;
|
||||
}
|
||||
|
||||
template <> constexpr inline bool is_type_compatible<nlohmann::json::object_t>(nlohmann::json::value_t json_type) {
|
||||
return json_type == nlohmann::json::value_t::object;
|
||||
}
|
||||
|
||||
template <typename T> bool json_to_variant_impl(T& /*to*/, const nlohmann::json& /*from*/) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename VariantType, typename CurrentType, typename... Rest>
|
||||
bool json_to_variant_impl(VariantType& to, const nlohmann::json& from) noexcept {
|
||||
if (is_type_compatible<CurrentType>(from.type())) {
|
||||
to = from.get<CurrentType>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return json_to_variant_impl<VariantType, Rest...>(to, from);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Ts> static std::variant<Ts...> json_to_variant(const nlohmann::json& j) {
|
||||
std::variant<Ts...> var;
|
||||
|
||||
if (detail::json_to_variant_impl<std::variant<Ts...>, Ts...>(var, j)) {
|
||||
return var;
|
||||
}
|
||||
|
||||
throw std::runtime_error("The given json object doesn't contain any type, the std::variant is aware of");
|
||||
}
|
||||
|
||||
template <typename T> nlohmann::json variant_to_json(T variant) {
|
||||
return std::visit(
|
||||
[](auto&& arg) -> nlohmann::json {
|
||||
using U = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<std::monostate, U>) { // FIXME: do we really want this?
|
||||
return nlohmann::json(nullptr);
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
},
|
||||
variant);
|
||||
}
|
||||
|
||||
namespace conversions {
|
||||
/// The json tag for the error type.
|
||||
constexpr auto ERROR_TYPE = "__everest__error_type";
|
||||
|
||||
/// The json tag for the error message.
|
||||
constexpr auto ERROR_MSG = "__everest__error_msg";
|
||||
|
||||
std::string cmd_error_type_to_string(CmdErrorType cmd_error_type);
|
||||
CmdErrorType string_to_cmd_error_type(const std::string& cmd_error_string);
|
||||
} // namespace conversions
|
||||
|
||||
void to_json(nlohmann::json& j, const CmdResultError& e);
|
||||
void from_json(const nlohmann::json& j, CmdResultError& e);
|
||||
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_CONVERSIONS_HPP
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_DATE_HPP
|
||||
#define UTILS_DATE_HPP
|
||||
|
||||
#include <date/date.h>
|
||||
#include <date/tz.h>
|
||||
|
||||
namespace Everest {
|
||||
namespace Date {
|
||||
|
||||
std::string to_rfc3339(const std::chrono::time_point<date::utc_clock>& t);
|
||||
|
||||
std::chrono::time_point<date::utc_clock> from_rfc3339_slow(const std::string& t);
|
||||
|
||||
std::chrono::time_point<date::utc_clock> from_rfc3339(const std::string& t);
|
||||
|
||||
} // namespace Date
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_CONFIG_HPP
|
||||
@@ -0,0 +1,92 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_ERROR_HPP
|
||||
#define UTILS_ERROR_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <utils/date.hpp>
|
||||
#include <utils/types.hpp>
|
||||
|
||||
#define UTILS_ERROR_DEFAULTS_TYPE "NotValidType"
|
||||
#define UTILS_ERROR_DEFAULTS_SUB_TYPE ""
|
||||
#define UTILS_ERROR_DEFAULTS_DESCRIPTION "no description provided"
|
||||
#define UTILS_ERROR_DEFAULTS_MESSAGE "no message provided"
|
||||
#define UTILS_ERROR_DEFAULTS_SEVERITY Everest::error::Severity::Low
|
||||
#define UTILS_ERROR_DEFAULTS_ORIGIN \
|
||||
ImplementationIdentifier("no-module-id-provided", "no-implementation-id-provided", std::nullopt)
|
||||
#define UTILS_ERROR_DEFAULTS_TIMESTAMP date::utc_clock::now()
|
||||
#define UTILS_ERROR_DEFAULTS_UUID UUID()
|
||||
#define UTILS_ERROR_DEFAULTS_STATE Everest::error::State::Active
|
||||
#define UTILS_ERROR_DEFAULTS_VENDOR_ID "everest"
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
enum class Severity {
|
||||
Low,
|
||||
Medium,
|
||||
High
|
||||
};
|
||||
std::string severity_to_string(const Severity& s);
|
||||
Severity string_to_severity(const std::string& s);
|
||||
|
||||
struct UUID {
|
||||
UUID();
|
||||
explicit UUID(const std::string& uuid);
|
||||
bool operator<(const UUID& other) const;
|
||||
bool operator==(const UUID& other) const;
|
||||
bool operator!=(const UUID& other) const;
|
||||
std::string to_string() const;
|
||||
|
||||
std::string uuid;
|
||||
};
|
||||
|
||||
using ErrorType = std::string;
|
||||
using ErrorSubType = std::string;
|
||||
|
||||
enum class State {
|
||||
Active,
|
||||
ClearedByModule,
|
||||
ClearedByReboot
|
||||
};
|
||||
std::string state_to_string(const State& s);
|
||||
State string_to_state(const std::string& s);
|
||||
|
||||
///
|
||||
/// \brief The Error struct represents an error object
|
||||
///
|
||||
struct Error {
|
||||
using time_point = date::utc_clock::time_point;
|
||||
Error(const ErrorType& type, const ErrorSubType& sub_type, const std::string& message,
|
||||
const std::string& description, const ImplementationIdentifier& origin, const std::string& vendor_id,
|
||||
const Severity& severity, const time_point& timestamp, const UUID& uuid,
|
||||
const State& state = UTILS_ERROR_DEFAULTS_STATE);
|
||||
Error(const ErrorType& type, const ErrorSubType& sub_type, const std::string& message,
|
||||
const std::string& description, const ImplementationIdentifier& origin,
|
||||
const Severity& severity = UTILS_ERROR_DEFAULTS_SEVERITY);
|
||||
Error(const ErrorType& type, const ErrorSubType& sub_type, const std::string& message,
|
||||
const std::string& description, const std::string& origin_module, const std::string& origin_implementation,
|
||||
const Severity& severity = UTILS_ERROR_DEFAULTS_SEVERITY);
|
||||
Error();
|
||||
ErrorType type;
|
||||
ErrorSubType sub_type;
|
||||
std::string message;
|
||||
std::string description;
|
||||
ImplementationIdentifier origin;
|
||||
std::string vendor_id;
|
||||
Severity severity;
|
||||
time_point timestamp;
|
||||
UUID uuid;
|
||||
State state;
|
||||
};
|
||||
|
||||
using ErrorHandle = UUID;
|
||||
using ErrorPtr = std::shared_ptr<Error>;
|
||||
using ErrorCallback = std::function<void(Error)>;
|
||||
using ErrorTypes = std::map<ErrorType, std::string>;
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_HPP
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_DATABASE_HPP
|
||||
#define UTILS_ERROR_DATABASE_HPP
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include <utils/error/error_filter.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
class ErrorDatabase {
|
||||
public:
|
||||
using EditErrorFunc = std::function<void(ErrorPtr)>;
|
||||
|
||||
ErrorDatabase() = default;
|
||||
virtual ~ErrorDatabase() = default;
|
||||
|
||||
virtual void add_error(ErrorPtr error) = 0;
|
||||
virtual std::list<ErrorPtr> get_errors(const std::list<ErrorFilter>& filters) const = 0;
|
||||
virtual std::list<ErrorPtr> edit_errors(const std::list<ErrorFilter>& filters, EditErrorFunc edit_func) = 0;
|
||||
virtual std::list<ErrorPtr> remove_errors(const std::list<ErrorFilter>& filters) = 0;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_DATABASE_HPP
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef ERROR_DATABASE_MAP_HPP
|
||||
#define ERROR_DATABASE_MAP_HPP
|
||||
|
||||
#include <list>
|
||||
#include <utils/error.hpp>
|
||||
#include <utils/error/error_database.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
class ErrorDatabaseMap : public ErrorDatabase {
|
||||
public:
|
||||
ErrorDatabaseMap() = default;
|
||||
|
||||
void add_error(ErrorPtr error) override;
|
||||
std::list<ErrorPtr> get_errors(const std::list<ErrorFilter>& filters) const override;
|
||||
std::list<ErrorPtr> edit_errors(const std::list<ErrorFilter>& filters, EditErrorFunc edit_func) override;
|
||||
std::list<ErrorPtr> remove_errors(const std::list<ErrorFilter>& filters) override;
|
||||
|
||||
private:
|
||||
std::list<ErrorPtr> get_errors_no_mutex(const std::list<ErrorFilter>& filters) const;
|
||||
std::map<ErrorHandle, ErrorPtr> errors;
|
||||
mutable std::mutex errors_mutex;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // ERROR_DATABASE_MAP_HPP
|
||||
@@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_FACTORY_HPP
|
||||
#define UTILS_ERROR_FACTORY_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <utils/error.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
struct ErrorTypeMap;
|
||||
|
||||
class ErrorFactory {
|
||||
public:
|
||||
explicit ErrorFactory(std::shared_ptr<ErrorTypeMap> error_type_map);
|
||||
ErrorFactory(std::shared_ptr<ErrorTypeMap> error_type_map, ImplementationIdentifier default_origin);
|
||||
ErrorFactory(std::shared_ptr<ErrorTypeMap> error_type_map, ImplementationIdentifier default_origin,
|
||||
Severity default_severity);
|
||||
ErrorFactory(std::shared_ptr<ErrorTypeMap> error_type_map, std::optional<ImplementationIdentifier> default_origin,
|
||||
std::optional<Severity> default_severity, std::optional<State> default_state,
|
||||
std::optional<ErrorType> default_type, std::optional<ErrorSubType> default_sub_type,
|
||||
std::optional<std::string> default_message, std::optional<std::string> default_vendor_id);
|
||||
|
||||
Error create_error() const;
|
||||
Error create_error(const ErrorType& type, const ErrorSubType& sub_type, const std::string& message) const;
|
||||
Error create_error(const ErrorType& type, const ErrorSubType& sub_type, const std::string& message,
|
||||
const Severity severity) const;
|
||||
Error create_error(const ErrorType& type, const ErrorSubType& sub_type, const std::string& message,
|
||||
const State state) const;
|
||||
Error create_error(const ErrorType& type, const ErrorSubType& sub_type, const std::string& message,
|
||||
const Severity severity, const State state) const;
|
||||
|
||||
void set_default_origin(const ImplementationIdentifier& origin);
|
||||
void set_default_severity(Severity severity);
|
||||
void set_default_state(State state);
|
||||
void set_default_type(const ErrorType& type);
|
||||
void set_default_sub_type(const ErrorSubType& sub_type);
|
||||
void set_default_message(const std::string& message);
|
||||
void set_default_vendor_id(const std::string& vendor_id);
|
||||
|
||||
private:
|
||||
const std::shared_ptr<ErrorTypeMap> error_type_map;
|
||||
|
||||
std::optional<ImplementationIdentifier> default_origin;
|
||||
std::optional<Severity> default_severity;
|
||||
std::optional<State> default_state;
|
||||
std::optional<ErrorType> default_type;
|
||||
std::optional<ErrorSubType> default_sub_type;
|
||||
std::optional<std::string> default_message;
|
||||
std::optional<std::string> default_vendor_id;
|
||||
|
||||
void set_description(Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_FACTORY_HPP
|
||||
@@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_FILTER_HPP
|
||||
#define UTILS_ERROR_FILTER_HPP
|
||||
|
||||
#include <utils/error.hpp>
|
||||
#include <utils/types.hpp>
|
||||
|
||||
#include <variant>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
///
|
||||
/// \brief This filter is used to filter errors by their state.
|
||||
///
|
||||
using StateFilter = State;
|
||||
std::string state_filter_to_string(const StateFilter& s);
|
||||
StateFilter string_to_state_filter(const std::string& s);
|
||||
|
||||
///
|
||||
/// \brief This filter is used to filter errors by their origin.
|
||||
///
|
||||
using OriginFilter = ImplementationIdentifier;
|
||||
|
||||
///
|
||||
/// \brief This filter is used to filter errors by their type.
|
||||
///
|
||||
|
||||
struct TypeFilter {
|
||||
explicit TypeFilter(const ErrorType& value);
|
||||
ErrorType value;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief This filter is used to filter errors by their severity.
|
||||
///
|
||||
enum class SeverityFilter {
|
||||
LOW_GE, ///< greater or equal LOW
|
||||
MEDIUM_GE, ///< greater or equal MEDIUM
|
||||
HIGH_GE ///< greater or equal HIGHS
|
||||
};
|
||||
std::string severity_filter_to_string(const SeverityFilter& s);
|
||||
SeverityFilter string_to_severity_filter(const std::string& s);
|
||||
|
||||
///
|
||||
/// \brief This filter is used to filter errors by their time of occurrence.
|
||||
///
|
||||
struct TimePeriodFilter {
|
||||
Error::time_point from; // time point from which the filter should be applied
|
||||
Error::time_point to; // time point to which the filter should be applied
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief This filter is used to filter errors by their handle.
|
||||
/// The handle is the error code.
|
||||
using HandleFilter = ErrorHandle;
|
||||
|
||||
///
|
||||
/// \brief This filter is used to filter errors by their sub type.
|
||||
///
|
||||
struct SubTypeFilter {
|
||||
explicit SubTypeFilter(const ErrorSubType& value);
|
||||
ErrorSubType value;
|
||||
};
|
||||
|
||||
struct VendorIdFilter {
|
||||
explicit VendorIdFilter(const std::string& value);
|
||||
std::string value;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief This enum is used to identify the different filter types.
|
||||
///
|
||||
enum class FilterType {
|
||||
State = 1,
|
||||
Origin = 2,
|
||||
Type = 3,
|
||||
Severity = 4,
|
||||
TimePeriod = 5,
|
||||
Handle = 6,
|
||||
SubType = 7,
|
||||
VendorId = 8
|
||||
};
|
||||
std::string filter_type_to_string(const FilterType& f);
|
||||
FilterType string_to_filter_type(const std::string& s);
|
||||
|
||||
class ErrorFilter {
|
||||
public:
|
||||
using FilterVariant = std::variant<std::monostate, StateFilter, OriginFilter, TypeFilter, SeverityFilter,
|
||||
TimePeriodFilter, HandleFilter, SubTypeFilter, VendorIdFilter>;
|
||||
ErrorFilter();
|
||||
explicit ErrorFilter(const FilterVariant& filter_);
|
||||
|
||||
FilterType get_filter_type() const;
|
||||
|
||||
StateFilter get_state_filter() const;
|
||||
OriginFilter get_origin_filter() const;
|
||||
TypeFilter get_type_filter() const;
|
||||
SeverityFilter get_severity_filter() const;
|
||||
TimePeriodFilter get_time_period_filter() const;
|
||||
HandleFilter get_handle_filter() const;
|
||||
SubTypeFilter get_sub_type_filter() const;
|
||||
VendorIdFilter get_vendor_id_filter() const;
|
||||
|
||||
private:
|
||||
FilterVariant filter;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_FILTER_HPP
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_ERROR_JSON_HPP
|
||||
#define UTILS_ERROR_JSON_HPP
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <utils/error.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
template <> struct adl_serializer<Everest::error::Error> {
|
||||
static void to_json(json& j, const Everest::error::Error& e) {
|
||||
j = {{"type", e.type},
|
||||
{"description", e.description},
|
||||
{"message", e.message},
|
||||
{"origin", {{"module_id", e.origin.module_id}, {"implementation_id", e.origin.implementation_id}}},
|
||||
{"severity", Everest::error::severity_to_string(e.severity)},
|
||||
{"timestamp", Everest::Date::to_rfc3339(e.timestamp)},
|
||||
{"uuid", e.uuid.uuid},
|
||||
{"state", Everest::error::state_to_string(e.state)},
|
||||
{"sub_type", e.sub_type},
|
||||
{"vendor_id", e.vendor_id}};
|
||||
if (e.origin.mapping.has_value()) {
|
||||
j["origin"]["mapping"] = e.origin.mapping.value();
|
||||
}
|
||||
}
|
||||
static Everest::error::Error from_json(const json& j) {
|
||||
const Everest::error::ErrorType type = j.at("type");
|
||||
const std::string message = j.at("message");
|
||||
const std::string description = j.at("description");
|
||||
std::optional<Mapping> mapping;
|
||||
if (j.at("origin").contains("mapping")) {
|
||||
mapping = j.at("origin").at("mapping");
|
||||
}
|
||||
const ImplementationIdentifier origin =
|
||||
ImplementationIdentifier(j.at("origin").at("module_id"), j.at("origin").at("implementation_id"), mapping);
|
||||
const Everest::error::Severity severity = Everest::error::string_to_severity(j.at("severity"));
|
||||
const Everest::error::Error::time_point timestamp = Everest::Date::from_rfc3339(j.at("timestamp"));
|
||||
const Everest::error::UUID uuid(j.at("uuid"));
|
||||
const Everest::error::State state = Everest::error::string_to_state(j.at("state"));
|
||||
const Everest::error::ErrorSubType sub_type(j.at("sub_type"));
|
||||
const std::string vendor_id = j.at("vendor_id");
|
||||
|
||||
return {type, sub_type, message, description, origin, vendor_id, severity, timestamp, uuid, state};
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
#endif // UTILS_ERROR_JSON_HPP
|
||||
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_MANAGER_IMPL_HPP
|
||||
#define UTILS_ERROR_MANAGER_IMPL_HPP
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <utils/error.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
struct ErrorDatabase;
|
||||
struct ErrorTypeMap;
|
||||
|
||||
class ErrorManagerImpl {
|
||||
public:
|
||||
using PublishErrorFunc = std::function<void(const error::Error&)>;
|
||||
|
||||
ErrorManagerImpl(std::shared_ptr<ErrorTypeMap> error_type_map, std::shared_ptr<ErrorDatabase> error_database,
|
||||
std::list<ErrorType> allowed_error_types, PublishErrorFunc publish_raised_error,
|
||||
PublishErrorFunc publish_cleared_error, const bool validate_error_types = true);
|
||||
|
||||
///
|
||||
/// \brief raise_error raises an error
|
||||
/// \param error The error to raise
|
||||
/// \details This function raises an error. The error is added to the error database if it has a valid error type
|
||||
/// and is allowed to be raised.
|
||||
///
|
||||
void raise_error(const Error& error);
|
||||
|
||||
///
|
||||
/// \brief clear_error clears an error
|
||||
/// \param type The error type to clear
|
||||
/// \details This function clears an error if there are not multiples sub types active.
|
||||
///
|
||||
std::list<ErrorPtr> clear_error(const ErrorType& type);
|
||||
|
||||
///
|
||||
/// \brief clear_error clears an error
|
||||
/// \param type The error type to clear
|
||||
/// \param sub_type The error sub type to clear
|
||||
/// \details This function clears a specific subtype of an error type.
|
||||
///
|
||||
std::list<ErrorPtr> clear_error(const ErrorType& type, const ErrorSubType& sub_type);
|
||||
|
||||
///
|
||||
/// \brief clear_all_errors clears all errors
|
||||
/// \details This function clears all errors that are currently active.
|
||||
///
|
||||
std::list<ErrorPtr> clear_all_errors();
|
||||
|
||||
///
|
||||
/// \brief clear_all_errors clears all errors of a specific ErrorType
|
||||
/// \param error_type The error type to clear
|
||||
/// \details This function clears all errors of a specific ErrorType that are currently active.
|
||||
///
|
||||
std::list<ErrorPtr> clear_all_errors(const ErrorType& error_type);
|
||||
|
||||
private:
|
||||
bool can_be_raised(const ErrorType& type, const ErrorSubType& sub_type) const;
|
||||
bool can_be_cleared(const ErrorType& type, const ErrorSubType& sub_type) const;
|
||||
bool can_be_cleared(const ErrorType& type) const;
|
||||
|
||||
std::shared_ptr<ErrorTypeMap> error_type_map;
|
||||
std::shared_ptr<ErrorDatabase> database;
|
||||
std::list<ErrorType> allowed_error_types;
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
PublishErrorFunc publish_raised_error;
|
||||
PublishErrorFunc publish_cleared_error;
|
||||
|
||||
const bool validate_error_types;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_MANAGER_IMPL_HPP
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_MANAGER_REQ_HPP
|
||||
#define UTILS_ERROR_MANAGER_REQ_HPP
|
||||
|
||||
#include <utils/error.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
struct ErrorDatabase;
|
||||
struct ErrorTypeMap;
|
||||
|
||||
class ErrorManagerReq {
|
||||
public:
|
||||
using SubscribeErrorFunc = std::function<void(const ErrorType&, const ErrorCallback&, const ErrorCallback&)>;
|
||||
|
||||
ErrorManagerReq(std::shared_ptr<ErrorTypeMap> error_type_map, std::shared_ptr<ErrorDatabase> error_database,
|
||||
std::list<ErrorType> allowed_error_types, SubscribeErrorFunc subscribe_error_func);
|
||||
|
||||
void subscribe_error(const ErrorType& type, const ErrorCallback& callback, const ErrorCallback& clear_callback);
|
||||
void subscribe_all_errors(const ErrorCallback& callback, const ErrorCallback& clear_callback);
|
||||
|
||||
private:
|
||||
struct Subscription {
|
||||
Subscription(const ErrorType& type, const ErrorCallback& callback, const ErrorCallback& clear_callback);
|
||||
ErrorType type;
|
||||
ErrorCallback callback;
|
||||
ErrorCallback clear_callback;
|
||||
};
|
||||
|
||||
std::map<ErrorType, std::list<Subscription>> error_subscriptions;
|
||||
|
||||
void on_error_raised(const Error& error);
|
||||
void on_error_cleared(const Error& error);
|
||||
void on_error(const Error& error, const bool raised) const;
|
||||
|
||||
std::shared_ptr<ErrorTypeMap> error_type_map;
|
||||
std::shared_ptr<ErrorDatabase> database;
|
||||
std::list<ErrorType> allowed_error_types;
|
||||
|
||||
SubscribeErrorFunc subscribe_error_func;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_MANAGER_REQ_HPP
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_MANAGER_REQ_GLOBAL_HPP
|
||||
#define UTILS_ERROR_MANAGER_REQ_GLOBAL_HPP
|
||||
|
||||
#include <utils/error.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
struct ErrorDatabase;
|
||||
struct ErrorTypeMap;
|
||||
|
||||
class ErrorManagerReqGlobal {
|
||||
public:
|
||||
using SubscribeGlobalAllErrorsFunc = std::function<void(const ErrorCallback&, const ErrorCallback&)>;
|
||||
|
||||
ErrorManagerReqGlobal(std::shared_ptr<ErrorTypeMap> error_type_map, std::shared_ptr<ErrorDatabase> error_database,
|
||||
SubscribeGlobalAllErrorsFunc subscribe_global_all_errors_func);
|
||||
|
||||
void subscribe_global_all_errors(const ErrorCallback& callback, const ErrorCallback& clear_callback);
|
||||
|
||||
private:
|
||||
struct Subscription {
|
||||
Subscription(const ErrorCallback& callback, const ErrorCallback& clear_callback);
|
||||
ErrorCallback callback;
|
||||
ErrorCallback clear_callback;
|
||||
};
|
||||
|
||||
void on_error_raised(const Error& error);
|
||||
void on_error_cleared(const Error& error);
|
||||
|
||||
std::shared_ptr<ErrorTypeMap> error_type_map;
|
||||
std::shared_ptr<ErrorDatabase> database;
|
||||
|
||||
SubscribeGlobalAllErrorsFunc subscribe_global_all_errors_func;
|
||||
|
||||
std::list<Subscription> subscriptions;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_MANAGER_REQ_GLOBAL_HPP
|
||||
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_STATE_MONITOR_HPP
|
||||
#define UTILS_ERROR_STATE_MONITOR_HPP
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <utils/error.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
struct ErrorDatabase;
|
||||
|
||||
///
|
||||
/// \brief The StateMonitor class is used to monitor the state of multiple error types
|
||||
/// \details The StateMonitor class is used to monitor the state of multiple error types. It can be used to check if a
|
||||
/// certain error type is active or if a certain condition is satisfied.
|
||||
///
|
||||
class ErrorStateMonitor {
|
||||
public:
|
||||
///
|
||||
/// \brief The StateCondition struct represents a single condition that can be checked by the StateMonitor
|
||||
///
|
||||
struct StateCondition {
|
||||
StateCondition(ErrorType type, ErrorSubType sub_type, bool active);
|
||||
ErrorType type;
|
||||
ErrorSubType sub_type;
|
||||
bool active;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief StateMonitor constructor
|
||||
/// \param error_database The error database to monitor
|
||||
///
|
||||
explicit ErrorStateMonitor(std::shared_ptr<ErrorDatabase> error_database);
|
||||
|
||||
///
|
||||
/// \brief is_error_active checks if a certain combination of error type and sub_type is active
|
||||
/// \param type The error type to check
|
||||
/// \param sub_type The error sub type to check
|
||||
/// \return True if the error type is active, false otherwise
|
||||
///
|
||||
bool is_error_active(const ErrorType& type, const ErrorSubType& sub_type) const;
|
||||
|
||||
///
|
||||
/// \brief get_active_errors returns the list of active errors for this error state monitor
|
||||
/// \return List of active errors
|
||||
///
|
||||
std::list<ErrorPtr> get_active_errors() const;
|
||||
|
||||
///
|
||||
/// \brief is_condition_satisfied checks if a certain condition is satisfied
|
||||
/// \param condition The condition to check
|
||||
/// \return True if the condition is satisfied, false otherwise
|
||||
///
|
||||
bool is_condition_satisfied(const StateCondition& condition) const;
|
||||
|
||||
///
|
||||
/// \brief is_condition_satisfied checks if a certain list of conditions is satisfied
|
||||
/// \param condition The list of conditions to check
|
||||
/// \return True if all conditions are satisfied, false otherwise
|
||||
///
|
||||
bool is_condition_satisfied(const std::list<StateCondition>& condition) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ErrorDatabase> database;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_STATE_MONITOR_HPP
|
||||
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef UTILS_ERROR_TYPE_MAP_HPP
|
||||
#define UTILS_ERROR_TYPE_MAP_HPP
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <utils/error.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace error {
|
||||
|
||||
///
|
||||
/// \brief A map of error types to their descriptions.
|
||||
/// This class is used to load error types from a directory
|
||||
/// and to get the description of an error type.
|
||||
///
|
||||
class ErrorTypeMap {
|
||||
public:
|
||||
///
|
||||
/// \brief Default constructor.
|
||||
/// Creates an empty map.
|
||||
///
|
||||
ErrorTypeMap() = default;
|
||||
|
||||
///
|
||||
/// \brief Constructor that loads error types from a directory.
|
||||
/// \param error_types_dir The directory to load the error types from.
|
||||
///
|
||||
explicit ErrorTypeMap(const std::filesystem::path& error_types_dir);
|
||||
|
||||
///
|
||||
/// \brief Loads error types from a directory.
|
||||
/// \param error_types_dir The directory to load the error types from.
|
||||
///
|
||||
void load_error_types(const std::filesystem::path& error_types_dir);
|
||||
|
||||
///
|
||||
/// \brief Loads error types from a given map
|
||||
/// \param error_types_dir The map to load the error types from.
|
||||
///
|
||||
void load_error_types_map(std::map<ErrorType, std::string> error_types_map);
|
||||
|
||||
///
|
||||
/// \brief Gets the description of an error type.
|
||||
/// \param error_type The error type to get the description of.
|
||||
/// \return The description of the error type.
|
||||
///
|
||||
std::string get_description(const ErrorType& error_type) const;
|
||||
|
||||
///
|
||||
/// \brief Checks if an error type exists.
|
||||
/// \param error_type The error type to check.
|
||||
/// \return True if the error type exists, false otherwise.
|
||||
///
|
||||
bool has(const ErrorType& error_type) const;
|
||||
|
||||
///
|
||||
/// \brief Returns the contained ErrorType map
|
||||
/// \return The error types map
|
||||
///
|
||||
const ErrorTypes& get_error_types() const;
|
||||
|
||||
private:
|
||||
ErrorTypes error_types;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_ERROR_TYPE_MAP_HPP
|
||||
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Everest {
|
||||
struct BootException : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class CmdError : public std::runtime_error {
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class MessageParsingError : public CmdError {
|
||||
public:
|
||||
using CmdError::CmdError;
|
||||
};
|
||||
|
||||
class SchemaValidationError : public CmdError {
|
||||
public:
|
||||
using CmdError::CmdError;
|
||||
};
|
||||
|
||||
class HandlerException : public CmdError {
|
||||
public:
|
||||
using CmdError::CmdError;
|
||||
};
|
||||
|
||||
class CmdTimeout : public CmdError {
|
||||
public:
|
||||
using CmdError::CmdError;
|
||||
};
|
||||
|
||||
class Shutdown : public CmdError {
|
||||
public:
|
||||
using CmdError::CmdError;
|
||||
};
|
||||
|
||||
class NotReady : public CmdError {
|
||||
public:
|
||||
using CmdError::CmdError;
|
||||
};
|
||||
} // namespace Everest
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_FILESYSTEM_HPP
|
||||
#define UTILS_FILESYSTEM_HPP
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
/// \brief Check if the provided \p path is a directory
|
||||
/// \returns The canonical version of the provided path
|
||||
/// \throws BootException if the path doesn't exist or isn't a directory
|
||||
fs::path assert_dir(const std::string& path, const std::string& path_alias = "The");
|
||||
|
||||
/// \brief Check if the provided \p path is a file
|
||||
/// \returns The canonical version of the provided path
|
||||
/// \throws BootException if the path doesn't exist or isn't a regular file
|
||||
fs::path assert_file(const std::string& path, const std::string& file_alias = "The");
|
||||
|
||||
/// \returns true if the file at the provided \p path has an extensions \p ext
|
||||
bool has_extension(const std::string& path, const std::string& ext);
|
||||
|
||||
/// \returns a path that has been prefixed by \p prefix from the provided json \p value
|
||||
std::string get_prefixed_path_from_json(const nlohmann::json& value, const fs::path& prefix);
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_FILESYSTEM_HPP
|
||||
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_FORMATTER_HPP
|
||||
#define UTILS_FORMATTER_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <fmt/core.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace everest::formatting {
|
||||
// NOTE (aw): this function is only here to hide the implementation of
|
||||
// fmt::format_error in <fmt/format.h>
|
||||
// in fmt version < 10, the function throw_format_error is not exposed
|
||||
// in the public api
|
||||
// NOTE (aw): this is only for backward compatibility, once finally
|
||||
// switching to version 10, this function could be removed again
|
||||
void throw_format_error(const char* message);
|
||||
} // namespace everest::formatting
|
||||
|
||||
template <> struct fmt::formatter<nlohmann::json> {
|
||||
constexpr auto parse(fmt::format_parse_context& ctx) -> fmt::format_parse_context::iterator {
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
|
||||
if (it != end && *it != '}') {
|
||||
everest::formatting::throw_format_error("Invalid format");
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
auto format(const nlohmann::json& j, fmt::format_context& ctx) const -> fmt::format_context::iterator {
|
||||
return fmt::format_to(ctx.out(), "{}", j.dump());
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct fmt::formatter<std::atomic_bool> {
|
||||
constexpr auto parse(fmt::format_parse_context& ctx) -> fmt::format_parse_context::iterator {
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
|
||||
if (it != end && *it != '}') {
|
||||
everest::formatting::throw_format_error("Invalid format");
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
auto format(const std::atomic_bool& a, fmt::format_context& ctx) const -> fmt::format_context::iterator {
|
||||
if (a) {
|
||||
return fmt::format_to(ctx.out(), "true");
|
||||
}
|
||||
return fmt::format_to(ctx.out(), "false");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct fmt::formatter<std::atomic<T>> {
|
||||
constexpr auto parse(fmt::format_parse_context& ctx) -> fmt::format_parse_context::iterator {
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
|
||||
if (it != end && *it != '}') {
|
||||
everest::formatting::throw_format_error("Invalid format");
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
auto format(const std::atomic<T>& a, fmt::format_context& ctx) const -> fmt::format_context::iterator {
|
||||
return fmt::format_to(ctx.out(), "{}", a.load());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // UTILS_FORMATTER_HPP
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest::helpers {
|
||||
template <typename T, typename U> T constexpr clamp_to(U len) {
|
||||
return (len <= std::numeric_limits<T>::max()) ? static_cast<T>(len) : std::numeric_limits<T>::max();
|
||||
}
|
||||
} // namespace Everest::helpers
|
||||
@@ -0,0 +1,129 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <everest/util/async/monitor.hpp>
|
||||
#include <everest/util/async/thread_pool_scaling.hpp>
|
||||
#include <everest/util/queue/thread_safe_queue.hpp>
|
||||
|
||||
#include <utils/message_handler_scaling_policy.hpp>
|
||||
#include <utils/message_queue.hpp>
|
||||
#include <utils/types.hpp>
|
||||
|
||||
using MqttTopic = std::string;
|
||||
using CmdId = std::string;
|
||||
|
||||
namespace Everest {
|
||||
|
||||
constexpr std::size_t THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS =
|
||||
detail::MESSAGE_HANDLER_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS;
|
||||
constexpr std::chrono::seconds THREAD_POOL_SCALING_IDLE_TIMEOUT{2};
|
||||
constexpr std::size_t THREAD_POOL_SCALING_MIN_THREAD_COUNT = 1;
|
||||
constexpr std::size_t MAX_PENDING_MESSAGES_PER_TOPIC = 100;
|
||||
|
||||
/// \brief Handles message dispatching and thread-safe queuing of different message types.
|
||||
///
|
||||
/// Messages are routed to one of four channels based on their type:
|
||||
/// - operation_message_queue → operation_dispatcher_thread → operation_thread_pool
|
||||
/// (vars, cmds, errors, GetConfig, ModuleReady — parallel across topics, serial per topic)
|
||||
/// - result_message_queue → result_worker_thread (cmd results, GetConfig responses — serial)
|
||||
/// - external_mqtt_message_queue → external_mqtt_worker_thread (external MQTT — serial)
|
||||
/// - GlobalReady → ready_thread (one-shot, spawned per message)
|
||||
class MessageHandler {
|
||||
public:
|
||||
MessageHandler();
|
||||
~MessageHandler();
|
||||
|
||||
/// \brief Adds given \p message to the message queue for processing
|
||||
void add(const ParsedMessage& message);
|
||||
|
||||
/// \brief Stops all threads started by this handler
|
||||
void stop();
|
||||
|
||||
/// \brief Registers a \p handler for a specific \p topic
|
||||
void register_handler(const std::string& topic, std::shared_ptr<TypedHandler> handler);
|
||||
|
||||
using SharedTypedHandler = std::shared_ptr<TypedHandler>;
|
||||
using SingleHandlerMap = std::map<MqttTopic, SharedTypedHandler>;
|
||||
using MultiHandlerMap = std::map<MqttTopic, std::vector<SharedTypedHandler>>;
|
||||
|
||||
private:
|
||||
struct OperationTopics {
|
||||
std::unordered_set<std::string> in_flight;
|
||||
std::unordered_map<std::string, everest::lib::util::simple_queue<ParsedMessage>> pending_messages;
|
||||
};
|
||||
|
||||
struct ResponseHandlers {
|
||||
std::map<CmdId, std::shared_ptr<TypedHandler>> cmd; // cmd result handlers of module
|
||||
std::shared_ptr<TypedHandler> config; // get module config response handler of module
|
||||
};
|
||||
|
||||
struct GenericHandlers {
|
||||
MultiHandlerMap var; // var handlers of module
|
||||
SingleHandlerMap cmd; // cmd handlers of module
|
||||
MultiHandlerMap error; // error handlers with wildcard support
|
||||
SingleHandlerMap get_module_config; // get module config handler of manager
|
||||
SharedTypedHandler global_ready; // global ready handler of module
|
||||
SingleHandlerMap module_ready; // module ready handlers of manager
|
||||
MultiHandlerMap external_var; // external MQTT handlers of module
|
||||
};
|
||||
|
||||
void run_operation_dispatcher();
|
||||
void run_result_message_worker();
|
||||
void run_external_mqtt_worker();
|
||||
|
||||
void dispatch_operation_message(ParsedMessage&& message);
|
||||
void schedule_operation_message(ParsedMessage&& message);
|
||||
void on_operation_message_done(const std::string& topic);
|
||||
|
||||
void handle_operation_message(const std::string& topic, const json& payload);
|
||||
void handle_result_message(const std::string& topic, const json& payload);
|
||||
|
||||
// Individual message handler methods
|
||||
void handle_var_message(const std::string& topic, const json& data);
|
||||
void handle_cmd_message(const std::string& topic, const json& data);
|
||||
void handle_external_mqtt_message(const std::string& topic, const json& data);
|
||||
void handle_error_message(const std::string& topic, const json& data);
|
||||
void handle_get_config_message(const std::string& topic, const json& data);
|
||||
void handle_module_ready_message(const std::string& topic, const json& data);
|
||||
void handle_cmd_result(const std::string& topic, const json& payload);
|
||||
void handle_get_config_response(const std::string& topic, const json& payload);
|
||||
|
||||
// Threads
|
||||
std::thread operation_dispatcher_thread; // processes vars, commands, external MQTT, errors, GetConfig and
|
||||
// ModuleReady messages
|
||||
std::thread result_worker_thread; // processes cmd results and GetConfig responses
|
||||
std::thread external_mqtt_worker_thread; // processes external MQTT messages
|
||||
|
||||
// Wrapped in a monitor so that concurrent GlobalReady arrivals in add() are safe:
|
||||
// the steal-then-join pattern moves the previous thread out under the lock and joins
|
||||
// outside the lock, preventing concurrent join/assignment races on the raw std::thread.
|
||||
everest::lib::util::monitor<std::thread> ready;
|
||||
|
||||
using ThreadPool = everest::lib::util::thread_pool_scaling<detail::MessageHandlerScalingPolicy,
|
||||
everest::lib::util::RethrowExceptions>;
|
||||
std::unique_ptr<ThreadPool> operation_thread_pool;
|
||||
|
||||
using MessageQueue = everest::lib::util::thread_safe_queue<ParsedMessage>;
|
||||
MessageQueue operation_message_queue;
|
||||
MessageQueue result_message_queue;
|
||||
MessageQueue external_mqtt_message_queue;
|
||||
|
||||
everest::lib::util::monitor<OperationTopics> operations;
|
||||
everest::lib::util::monitor<ResponseHandlers> responses;
|
||||
everest::lib::util::monitor<GenericHandlers> handlers;
|
||||
|
||||
std::atomic<bool> running = true;
|
||||
};
|
||||
|
||||
} // namespace Everest
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <everest/compile_time_settings.hpp>
|
||||
#include <everest/util/async/thread_pool_scaling.hpp>
|
||||
|
||||
namespace Everest::detail {
|
||||
|
||||
constexpr std::size_t MESSAGE_HANDLER_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS =
|
||||
EVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS;
|
||||
constexpr std::size_t MESSAGE_HANDLER_THREAD_POOL_SCALING_LATENCY_TICK_MS =
|
||||
EVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_TICK_MS;
|
||||
constexpr std::size_t MESSAGE_HANDLER_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD =
|
||||
EVEREST_FRAMEWORK_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD;
|
||||
|
||||
using MessageHandlerLatencyScaling =
|
||||
everest::lib::util::LatencyScaling<MESSAGE_HANDLER_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS,
|
||||
MESSAGE_HANDLER_THREAD_POOL_SCALING_LATENCY_TICK_MS>;
|
||||
using MessageHandlerFixedSizeScaling =
|
||||
everest::lib::util::FixedSizeScaling<MESSAGE_HANDLER_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD>;
|
||||
|
||||
#if EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY == EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM
|
||||
using MessageHandlerScalingPolicy = EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_TYPE;
|
||||
#elif EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY == EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_GREEDY
|
||||
using MessageHandlerScalingPolicy = everest::lib::util::GreedyScaling;
|
||||
#elif EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY == EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CONSERVATIVE
|
||||
using MessageHandlerScalingPolicy = everest::lib::util::ConservativeScaling;
|
||||
#elif EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY == EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_FIXED_SIZE
|
||||
using MessageHandlerScalingPolicy = MessageHandlerFixedSizeScaling;
|
||||
#else
|
||||
using MessageHandlerScalingPolicy = MessageHandlerLatencyScaling;
|
||||
#endif
|
||||
|
||||
} // namespace Everest::detail
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_MESSAGE_QUEUE_HPP
|
||||
#define UTILS_MESSAGE_QUEUE_HPP
|
||||
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
/// \brief Contains a payload and the topic it was received on
|
||||
struct Message {
|
||||
std::string topic; ///< The MQTT topic where this message originated from
|
||||
std::string payload; ///< The message payload
|
||||
|
||||
Message(std::string_view topic_param, std::string_view payload_param) : topic(topic_param), payload(payload_param) {
|
||||
}
|
||||
};
|
||||
|
||||
struct ParsedMessage {
|
||||
std::string topic;
|
||||
nlohmann::json data;
|
||||
};
|
||||
|
||||
using MessageCallback = std::function<void(const Message&)>;
|
||||
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_MESSAGE_QUEUE_HPP
|
||||
@@ -0,0 +1,551 @@
|
||||
/**
|
||||
* Macros for metaprogramming
|
||||
* ExtendedC
|
||||
*
|
||||
* Copyright (C) 2012 Justin Spahr-Summers
|
||||
* Released under the MIT license
|
||||
*/
|
||||
|
||||
#ifndef EXTC_METAMACROS_H
|
||||
#define EXTC_METAMACROS_H
|
||||
|
||||
/**
|
||||
* Executes one or more expressions (which may have a void type, such as a call
|
||||
* to a function that returns no value) and always returns true.
|
||||
*/
|
||||
#define metamacro_exprify(...) ((__VA_ARGS__), true)
|
||||
|
||||
/**
|
||||
* Returns a string representation of VALUE after full macro expansion.
|
||||
*/
|
||||
#define metamacro_stringify(VALUE) metamacro_stringify_(VALUE)
|
||||
|
||||
/**
|
||||
* Returns A and B concatenated after full macro expansion.
|
||||
*/
|
||||
#define metamacro_concat(A, B) metamacro_concat_(A, B)
|
||||
|
||||
/**
|
||||
* Returns the Nth variadic argument (starting from zero). At least
|
||||
* N + 1 variadic arguments must be given. N must be between zero and twenty,
|
||||
* inclusive.
|
||||
*/
|
||||
#define metamacro_at(N, ...) metamacro_concat(metamacro_at, N)(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Returns the number of arguments (up to twenty) provided to the macro. At
|
||||
* least one argument must be provided.
|
||||
*
|
||||
* Inspired by P99: http://p99.gforge.inria.fr
|
||||
*/
|
||||
#define metamacro_argcount(...) \
|
||||
metamacro_at(20, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
|
||||
/**
|
||||
* Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is
|
||||
* given. Only the index and current argument will thus be passed to MACRO.
|
||||
*/
|
||||
#define metamacro_foreach(MACRO, SEP, ...) metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* For each consecutive variadic argument (up to twenty), MACRO is passed the
|
||||
* zero-based index of the current argument, CONTEXT, and then the argument
|
||||
* itself. The results of adjoining invocations of MACRO are then separated by
|
||||
* SEP.
|
||||
*
|
||||
* Inspired by P99: http://p99.gforge.inria.fr
|
||||
*/
|
||||
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
|
||||
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Identical to #metamacro_foreach_cxt. This can be used when the former would
|
||||
* fail due to recursive macro expansion.
|
||||
*/
|
||||
#define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \
|
||||
metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* In consecutive order, appends each variadic argument (up to twenty) onto
|
||||
* BASE. The resulting concatenations are then separated by SEP.
|
||||
*
|
||||
* This is primarily useful to manipulate a list of macro invocations into instead
|
||||
* invoking a different, possibly related macro.
|
||||
*/
|
||||
#define metamacro_foreach_concat(BASE, SEP, ...) \
|
||||
metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Iterates COUNT times, each time invoking MACRO with the current index
|
||||
* (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO
|
||||
* are then separated by SEP.
|
||||
*
|
||||
* COUNT must be an integer between zero and twenty, inclusive.
|
||||
*/
|
||||
#define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT)
|
||||
|
||||
/**
|
||||
* Returns the first argument given. At least one argument must be provided.
|
||||
*
|
||||
* This is useful when implementing a variadic macro, where you may have only
|
||||
* one variadic argument, but no way to retrieve it (for example, because \c ...
|
||||
* always needs to match at least one argument).
|
||||
*
|
||||
* @code
|
||||
|
||||
#define varmacro(...) \
|
||||
metamacro_head(__VA_ARGS__)
|
||||
|
||||
* @endcode
|
||||
*/
|
||||
#define metamacro_head(...) metamacro_head_(__VA_ARGS__, 0)
|
||||
|
||||
/**
|
||||
* Returns every argument except the first. At least two arguments must be
|
||||
* provided.
|
||||
*/
|
||||
#define metamacro_tail(...) metamacro_tail_(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Returns the first N (up to twenty) variadic arguments as a new argument list.
|
||||
* At least N variadic arguments must be provided.
|
||||
*/
|
||||
#define metamacro_take(N, ...) metamacro_concat(metamacro_take, N)(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Removes the first N (up to twenty) variadic arguments from the given argument
|
||||
* list. At least N variadic arguments must be provided.
|
||||
*/
|
||||
#define metamacro_drop(N, ...) metamacro_concat(metamacro_drop, N)(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Decrements VAL, which must be a number between zero and twenty, inclusive.
|
||||
*
|
||||
* This is primarily useful when dealing with indexes and counts in
|
||||
* metaprogramming.
|
||||
*/
|
||||
#define metamacro_dec(VAL) metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
|
||||
|
||||
/**
|
||||
* Increments VAL, which must be a number between zero and twenty, inclusive.
|
||||
*
|
||||
* This is primarily useful when dealing with indexes and counts in
|
||||
* metaprogramming.
|
||||
*/
|
||||
#define metamacro_inc(VAL) metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)
|
||||
|
||||
/**
|
||||
* If A is equal to B, the next argument list is expanded; otherwise, the
|
||||
* argument list after that is expanded. A and B must be numbers between zero
|
||||
* and twenty, inclusive. Additionally, B must be greater than or equal to A.
|
||||
*
|
||||
* @code
|
||||
|
||||
// expands to true
|
||||
metamacro_if_eq(0, 0)(true)(false)
|
||||
|
||||
// expands to false
|
||||
metamacro_if_eq(0, 1)(true)(false)
|
||||
|
||||
* @endcode
|
||||
*
|
||||
* This is primarily useful when dealing with indexes and counts in
|
||||
* metaprogramming.
|
||||
*/
|
||||
#define metamacro_if_eq(A, B) metamacro_concat(metamacro_if_eq, A)(B)
|
||||
|
||||
/**
|
||||
* Identical to #metamacro_if_eq. This can be used when the former would fail
|
||||
* due to recursive macro expansion.
|
||||
*/
|
||||
#define metamacro_if_eq_recursive(A, B) metamacro_concat(metamacro_if_eq_recursive, A)(B)
|
||||
|
||||
/**
|
||||
* Returns 1 if N is an even number, or 0 otherwise. N must be between zero and
|
||||
* twenty, inclusive.
|
||||
*
|
||||
* For the purposes of this test, zero is considered even.
|
||||
*/
|
||||
#define metamacro_is_even(N) metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
|
||||
|
||||
/**
|
||||
* Returns the logical NOT of B, which must be the number zero or one.
|
||||
*/
|
||||
#define metamacro_not(B) metamacro_at(B, 1, 0)
|
||||
|
||||
// IMPLEMENTATION DETAILS FOLLOW!
|
||||
// Do not write code that depends on anything below this line.
|
||||
#define metamacro_stringify_(VALUE) #VALUE
|
||||
#define metamacro_concat_(A, B) A##B
|
||||
#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG)
|
||||
#define metamacro_head_(FIRST, ...) FIRST
|
||||
#define metamacro_tail_(FIRST, ...) __VA_ARGS__
|
||||
#define metamacro_consume_(...)
|
||||
#define metamacro_expand_(...) __VA_ARGS__
|
||||
|
||||
// implemented from scratch so that metamacro_concat() doesn't end up nesting
|
||||
#define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG)
|
||||
#define metamacro_foreach_concat_iter_(BASE, ARG) BASE##ARG
|
||||
|
||||
// metamacro_at expansions
|
||||
#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) \
|
||||
metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) \
|
||||
metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) \
|
||||
metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) \
|
||||
metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) \
|
||||
metamacro_head(__VA_ARGS__)
|
||||
|
||||
// metamacro_foreach_cxt expansions
|
||||
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
|
||||
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
|
||||
|
||||
#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
|
||||
metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) SEP MACRO(1, CONTEXT, _1)
|
||||
|
||||
#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
|
||||
metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) SEP MACRO(2, CONTEXT, _2)
|
||||
|
||||
#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
|
||||
metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) SEP MACRO(3, CONTEXT, _3)
|
||||
|
||||
#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
|
||||
metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) SEP MACRO(4, CONTEXT, _4)
|
||||
|
||||
#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
|
||||
metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) SEP MACRO(5, CONTEXT, _5)
|
||||
|
||||
#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
|
||||
metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) SEP MACRO(6, CONTEXT, _6)
|
||||
|
||||
#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
|
||||
metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) SEP MACRO(7, CONTEXT, _7)
|
||||
|
||||
#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
|
||||
metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) SEP MACRO(8, CONTEXT, _8)
|
||||
|
||||
#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
|
||||
metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) SEP MACRO(9, CONTEXT, _9)
|
||||
|
||||
#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
|
||||
metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) SEP MACRO(10, CONTEXT, _10)
|
||||
|
||||
#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
|
||||
metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
|
||||
SEP MACRO(11, CONTEXT, _11)
|
||||
|
||||
#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
|
||||
metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
|
||||
SEP MACRO(12, CONTEXT, _12)
|
||||
|
||||
#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
|
||||
metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
|
||||
SEP MACRO(13, CONTEXT, _13)
|
||||
|
||||
#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
|
||||
metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
|
||||
SEP MACRO(14, CONTEXT, _14)
|
||||
|
||||
#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \
|
||||
_15) \
|
||||
metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
|
||||
SEP MACRO(15, CONTEXT, _15)
|
||||
|
||||
#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \
|
||||
_15, _16) \
|
||||
metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
|
||||
SEP MACRO(16, CONTEXT, _16)
|
||||
|
||||
#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \
|
||||
_15, _16, _17) \
|
||||
metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \
|
||||
_16) SEP MACRO(17, CONTEXT, _17)
|
||||
|
||||
#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \
|
||||
_15, _16, _17, _18) \
|
||||
metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \
|
||||
_16, _17) SEP MACRO(18, CONTEXT, _18)
|
||||
|
||||
#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \
|
||||
_15, _16, _17, _18, _19) \
|
||||
metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \
|
||||
_16, _17, _18) SEP MACRO(19, CONTEXT, _19)
|
||||
|
||||
// metamacro_foreach_cxt_recursive expansions
|
||||
#define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT)
|
||||
#define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \
|
||||
metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) SEP MACRO(1, CONTEXT, _1)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \
|
||||
metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) SEP MACRO(2, CONTEXT, _2)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
|
||||
metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) SEP MACRO(3, CONTEXT, _3)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
|
||||
metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) SEP MACRO(4, CONTEXT, _4)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
|
||||
metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) SEP MACRO(5, CONTEXT, _5)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
|
||||
metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) SEP MACRO(6, CONTEXT, _6)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
|
||||
metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) SEP MACRO(7, CONTEXT, _7)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
|
||||
metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) SEP MACRO(8, CONTEXT, _8)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
|
||||
metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) SEP MACRO(9, CONTEXT, _9)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
|
||||
metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
|
||||
SEP MACRO(10, CONTEXT, _10)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
|
||||
metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
|
||||
SEP MACRO(11, CONTEXT, _11)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
|
||||
metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
|
||||
SEP MACRO(12, CONTEXT, _12)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13) \
|
||||
metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
|
||||
SEP MACRO(13, CONTEXT, _13)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13, _14) \
|
||||
metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
|
||||
SEP MACRO(14, CONTEXT, _14)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13, _14, _15) \
|
||||
metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
|
||||
_14) SEP MACRO(15, CONTEXT, _15)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13, _14, _15, _16) \
|
||||
metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
|
||||
_14, _15) SEP MACRO(16, CONTEXT, _16)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13, _14, _15, _16, _17) \
|
||||
metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
|
||||
_14, _15, _16) SEP MACRO(17, CONTEXT, _17)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13, _14, _15, _16, _17, _18) \
|
||||
metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
|
||||
_14, _15, _16, _17) SEP MACRO(18, CONTEXT, _18)
|
||||
|
||||
#define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13, _14, _15, _16, _17, _18, _19) \
|
||||
metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
|
||||
_14, _15, _16, _17, _18) SEP MACRO(19, CONTEXT, _19)
|
||||
|
||||
// metamacro_for_cxt expansions
|
||||
#define metamacro_for_cxt0(MACRO, SEP, CONTEXT)
|
||||
#define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt2(MACRO, SEP, CONTEXT) metamacro_for_cxt1(MACRO, SEP, CONTEXT) SEP MACRO(1, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt3(MACRO, SEP, CONTEXT) metamacro_for_cxt2(MACRO, SEP, CONTEXT) SEP MACRO(2, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt4(MACRO, SEP, CONTEXT) metamacro_for_cxt3(MACRO, SEP, CONTEXT) SEP MACRO(3, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt5(MACRO, SEP, CONTEXT) metamacro_for_cxt4(MACRO, SEP, CONTEXT) SEP MACRO(4, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt6(MACRO, SEP, CONTEXT) metamacro_for_cxt5(MACRO, SEP, CONTEXT) SEP MACRO(5, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt7(MACRO, SEP, CONTEXT) metamacro_for_cxt6(MACRO, SEP, CONTEXT) SEP MACRO(6, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt8(MACRO, SEP, CONTEXT) metamacro_for_cxt7(MACRO, SEP, CONTEXT) SEP MACRO(7, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt9(MACRO, SEP, CONTEXT) metamacro_for_cxt8(MACRO, SEP, CONTEXT) SEP MACRO(8, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt10(MACRO, SEP, CONTEXT) metamacro_for_cxt9(MACRO, SEP, CONTEXT) SEP MACRO(9, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt11(MACRO, SEP, CONTEXT) metamacro_for_cxt10(MACRO, SEP, CONTEXT) SEP MACRO(10, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt12(MACRO, SEP, CONTEXT) metamacro_for_cxt11(MACRO, SEP, CONTEXT) SEP MACRO(11, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt13(MACRO, SEP, CONTEXT) metamacro_for_cxt12(MACRO, SEP, CONTEXT) SEP MACRO(12, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt14(MACRO, SEP, CONTEXT) metamacro_for_cxt13(MACRO, SEP, CONTEXT) SEP MACRO(13, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt15(MACRO, SEP, CONTEXT) metamacro_for_cxt14(MACRO, SEP, CONTEXT) SEP MACRO(14, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt16(MACRO, SEP, CONTEXT) metamacro_for_cxt15(MACRO, SEP, CONTEXT) SEP MACRO(15, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt17(MACRO, SEP, CONTEXT) metamacro_for_cxt16(MACRO, SEP, CONTEXT) SEP MACRO(16, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt18(MACRO, SEP, CONTEXT) metamacro_for_cxt17(MACRO, SEP, CONTEXT) SEP MACRO(17, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt19(MACRO, SEP, CONTEXT) metamacro_for_cxt18(MACRO, SEP, CONTEXT) SEP MACRO(18, CONTEXT)
|
||||
|
||||
#define metamacro_for_cxt20(MACRO, SEP, CONTEXT) metamacro_for_cxt19(MACRO, SEP, CONTEXT) SEP MACRO(19, CONTEXT)
|
||||
|
||||
// metamacro_if_eq expansions
|
||||
#define metamacro_if_eq0(VALUE) metamacro_concat(metamacro_if_eq0_, VALUE)
|
||||
|
||||
#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_
|
||||
#define metamacro_if_eq0_1(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_2(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_3(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_4(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_5(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_6(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_7(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_8(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_9(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_10(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_11(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_12(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_13(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_14(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_15(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_16(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_17(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_18(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_19(...) metamacro_expand_
|
||||
#define metamacro_if_eq0_20(...) metamacro_expand_
|
||||
|
||||
#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE))
|
||||
|
||||
// metamacro_if_eq_recursive expansions
|
||||
#define metamacro_if_eq_recursive0(VALUE) metamacro_concat(metamacro_if_eq_recursive0_, VALUE)
|
||||
|
||||
#define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_
|
||||
#define metamacro_if_eq_recursive0_1(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_2(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_3(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_4(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_5(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_6(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_7(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_8(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_9(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_10(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_11(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_12(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_13(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_14(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_15(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_16(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_17(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_18(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_19(...) metamacro_expand_
|
||||
#define metamacro_if_eq_recursive0_20(...) metamacro_expand_
|
||||
|
||||
#define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE))
|
||||
#define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE))
|
||||
|
||||
// metamacro_take expansions
|
||||
#define metamacro_take0(...)
|
||||
#define metamacro_take1(...) metamacro_head(__VA_ARGS__)
|
||||
#define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__))
|
||||
|
||||
// metamacro_drop expansions
|
||||
#define metamacro_drop0(...) __VA_ARGS__
|
||||
#define metamacro_drop1(...) metamacro_tail(__VA_ARGS__)
|
||||
#define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__))
|
||||
#define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_MODULE_CONFIG_HPP
|
||||
#define UTILS_MODULE_CONFIG_HPP
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/mqtt_abstraction.hpp>
|
||||
|
||||
namespace Everest {
|
||||
/// \brief get config from manager via mqtt
|
||||
nlohmann::json get_module_config(std::shared_ptr<MQTTAbstraction> mqtt, const std::string& module_id);
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_MODULE_CONFIG_HPP
|
||||
@@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_MQTT_ABSTRACTION_HPP
|
||||
#define UTILS_MQTT_ABSTRACTION_HPP
|
||||
|
||||
#include <future>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/config/mqtt_settings.hpp>
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
|
||||
///
|
||||
/// \brief Pure virtual interface for MQTT communication in EVerest modules.
|
||||
///
|
||||
/// Use MQTTAbstractionImpl for the real MQTT connection.
|
||||
///
|
||||
class MQTTAbstraction {
|
||||
public:
|
||||
// forbid copy assignment and copy construction
|
||||
MQTTAbstraction(MQTTAbstraction const&) = delete;
|
||||
void operator=(MQTTAbstraction const&) = delete;
|
||||
|
||||
virtual ~MQTTAbstraction() = default;
|
||||
|
||||
/// \brief connects to the mqtt broker
|
||||
virtual bool connect() = 0;
|
||||
|
||||
/// \brief disconnects from the mqtt broker
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
/// \brief publishes the given \p json on the given \p topic with QOS level 0
|
||||
virtual void publish(const std::string& topic, const nlohmann::json& json) = 0;
|
||||
|
||||
/// \brief publishes the given \p json on the given \p topic with the given \p qos
|
||||
virtual void publish(const std::string& topic, const nlohmann::json& json, QOS qos, bool retain = false) = 0;
|
||||
|
||||
/// \brief publishes the given \p data on the given \p topic with QOS level 0
|
||||
virtual void publish(const std::string& topic, const std::string& data) = 0;
|
||||
|
||||
/// \brief publishes the given \p data on the given \p topic with the given \p qos
|
||||
virtual void publish(const std::string& topic, const std::string& data, QOS qos, bool retain = false) = 0;
|
||||
|
||||
/// \brief subscribes to the given \p topic with QOS level 0
|
||||
virtual void subscribe(const std::string& topic) = 0;
|
||||
|
||||
/// \brief subscribes to the given \p topic with the given \p qos
|
||||
virtual void subscribe(const std::string& topic, QOS qos) = 0;
|
||||
|
||||
/// \brief unsubscribes from the given \p topic
|
||||
virtual void unsubscribe(const std::string& topic) = 0;
|
||||
|
||||
/// \brief clears any previously published topics that had the retain flag set
|
||||
virtual void clear_retained_topics() = 0;
|
||||
|
||||
/// \brief Sends a get request on \p topic and waits for a JSON response, with the given \p qos and \p retries
|
||||
virtual nlohmann::json get(const std::string& topic, QOS qos, std::size_t retries = 0) = 0;
|
||||
|
||||
/// \brief Sends an MQTT request and waits for a JSON response.
|
||||
///
|
||||
/// Registers a temporary handler for the response topic in \p request, publishes the request
|
||||
/// message, and waits for the corresponding response. Throws EverestTimeoutError on timeout.
|
||||
///
|
||||
/// \param request The MQTT request containing the response topic, request topic, payload, QoS, and timeout.
|
||||
/// \param retries How often the get should be retried on timeout, defaults to 0.
|
||||
/// \return The JSON response received.
|
||||
virtual nlohmann::json get(const MQTTRequest& request, std::size_t retries = 0) = 0;
|
||||
|
||||
/// \brief Get MQTT topic prefix for the "everest" topic
|
||||
virtual const std::string& get_everest_prefix() const = 0;
|
||||
|
||||
/// \brief Get MQTT topic prefix for external topics
|
||||
virtual const std::string& get_external_prefix() const = 0;
|
||||
|
||||
/// \brief Spawn a thread running the mqtt main loop
|
||||
/// \returns a future, which will be fulfilled on thread termination
|
||||
virtual std::shared_future<void> spawn_main_loop_thread() = 0;
|
||||
|
||||
/// \returns the main loop future, which will be fulfilled on thread termination
|
||||
virtual std::shared_future<void> get_main_loop_future() = 0;
|
||||
|
||||
/// \brief subscribes to \p topic and registers a \p handler called when a message arrives, with the given \p qos
|
||||
virtual void register_handler(const std::string& topic, std::shared_ptr<TypedHandler> handler, QOS qos) = 0;
|
||||
|
||||
/// \brief unsubscribes a handler identified by its \p token from the given \p topic
|
||||
virtual void unregister_handler(const std::string& topic, const Token& token) = 0;
|
||||
|
||||
protected:
|
||||
MQTTAbstraction() = default;
|
||||
};
|
||||
|
||||
/// \brief Create a real MQTTAbstraction backed by the given \p mqtt_settings.
|
||||
/// Use this instead of constructing MQTTAbstractionImpl directly.
|
||||
std::unique_ptr<MQTTAbstraction> make_mqtt_abstraction(const MQTTSettings& mqtt_settings);
|
||||
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_MQTT_ABSTRACTION_HPP
|
||||
@@ -0,0 +1,117 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_MQTT_ABSTRACTION_IMPL_HPP
|
||||
#define UTILS_MQTT_ABSTRACTION_IMPL_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <everest/io/event/fd_event_handler.hpp>
|
||||
#include <everest/io/event/timer_fd.hpp>
|
||||
#include <everest/io/mqtt/mqtt_client.hpp>
|
||||
#include <everest/util/async/monitor.hpp>
|
||||
#include <everest/util/queue/simple_queue.hpp>
|
||||
#include <utils/config/mqtt_settings.hpp>
|
||||
#include <utils/message_handler.hpp>
|
||||
#include <utils/message_queue.hpp>
|
||||
#include <utils/mqtt_abstraction.hpp>
|
||||
#include <utils/thread.hpp>
|
||||
#include <utils/types.hpp>
|
||||
|
||||
namespace Everest {
|
||||
/// \brief Contains a payload and the topic it was received on with additional QOS
|
||||
struct MessageWithQOS : Message {
|
||||
QOS qos; ///< The Quality of Service level
|
||||
bool retain; ///< If the retain flag should be set on publishing this message
|
||||
|
||||
MessageWithQOS(const std::string& topic, const std::string& payload, QOS qos, bool retain);
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Contains a C++ abstraction of MQTT-C and some convenience functionality for using MQTT in EVerest modules
|
||||
///
|
||||
class MQTTAbstractionImpl : public MQTTAbstraction {
|
||||
public:
|
||||
MQTTAbstractionImpl(const MQTTSettings& mqtt_settings);
|
||||
|
||||
~MQTTAbstractionImpl() override;
|
||||
|
||||
MQTTAbstractionImpl(MQTTAbstractionImpl const&) = delete;
|
||||
void operator=(MQTTAbstractionImpl const&) = delete;
|
||||
|
||||
bool connect() override;
|
||||
void disconnect() override;
|
||||
void publish(const std::string& topic, const nlohmann::json& json) override;
|
||||
void publish(const std::string& topic, const nlohmann::json& json, QOS qos, bool retain = false) override;
|
||||
void publish(const std::string& topic, const std::string& data) override;
|
||||
void publish(const std::string& topic, const std::string& data, QOS qos, bool retain = false) override;
|
||||
void subscribe(const std::string& topic) override;
|
||||
void subscribe(const std::string& topic, QOS qos) override;
|
||||
void unsubscribe(const std::string& topic) override;
|
||||
void clear_retained_topics() override;
|
||||
nlohmann::json get(const MQTTRequest& request, std::size_t retries = 0) override;
|
||||
nlohmann::json get(const std::string& topic, QOS qos, std::size_t retries = 0) override;
|
||||
const std::string& get_everest_prefix() const override;
|
||||
const std::string& get_external_prefix() const override;
|
||||
std::shared_future<void> spawn_main_loop_thread() override;
|
||||
std::shared_future<void> get_main_loop_future() override;
|
||||
void register_handler(const std::string& topic, std::shared_ptr<TypedHandler> handler, QOS qos) override;
|
||||
void unregister_handler(const std::string& topic, const Token& token) override;
|
||||
|
||||
///
|
||||
/// \brief checks if the given \p full_topic matches the given \p wildcard_topic that can contain "+" and "#"
|
||||
/// wildcards
|
||||
///
|
||||
/// \returns true if the topic matches, false otherwise
|
||||
static bool check_topic_matches(const std::string& full_topic, const std::string& wildcard_topic);
|
||||
|
||||
private:
|
||||
template <class T> using monitor = everest::lib::util::monitor<T>;
|
||||
using shared_messages = std::vector<std::shared_ptr<MessageWithQOS>>;
|
||||
struct Topics {
|
||||
std::vector<std::string> retained_topics;
|
||||
std::unordered_set<std::string> subscribed_topics;
|
||||
};
|
||||
|
||||
std::atomic_bool mqtt_is_connected;
|
||||
std::atomic_bool running;
|
||||
MessageHandler message_handler;
|
||||
everest::lib::util::simple_queue<Message> message_queue;
|
||||
|
||||
monitor<shared_messages> messages_before_connected;
|
||||
monitor<Topics> managed_topics;
|
||||
|
||||
std::shared_future<void> main_loop_future;
|
||||
|
||||
std::string mqtt_server_socket_path;
|
||||
std::string mqtt_server_address;
|
||||
std::uint16_t mqtt_server_port = 0;
|
||||
std::string mqtt_everest_prefix;
|
||||
std::string mqtt_external_prefix;
|
||||
|
||||
std::unique_ptr<everest::lib::io::mqtt::mqtt_client> mqtt_client;
|
||||
everest::lib::io::event::event_fd disconnect_event;
|
||||
everest::lib::io::event::event_fd new_message_event;
|
||||
everest::lib::io::event::fd_event_handler ev_handler;
|
||||
|
||||
// This must be destroyed first.
|
||||
Thread mqtt_mainloop_thread;
|
||||
|
||||
void on_mqtt_message();
|
||||
void on_mqtt_connect();
|
||||
void handle_mqtt_message(const Message& message);
|
||||
|
||||
static void on_mqtt_disconnect();
|
||||
nlohmann::json get_internal(const MQTTRequest& request);
|
||||
};
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_MQTT_ABSTRACTION_IMPL_HPP
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_SERIAL_HPP
|
||||
#define UTILS_SERIAL_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <termios.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Everest {
|
||||
class Serial {
|
||||
|
||||
public:
|
||||
Serial();
|
||||
virtual ~Serial();
|
||||
|
||||
bool openDevice(const char* device, int baud);
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
virtual std::string hexdump(const std::uint8_t* const msg, int msg_len) const;
|
||||
virtual std::string hexdump(const std::vector<std::uint8_t>& msg) const;
|
||||
|
||||
protected:
|
||||
int fd{-1};
|
||||
int baud{0};
|
||||
|
||||
private:
|
||||
// Serial interface
|
||||
bool setSerialAttributes();
|
||||
|
||||
// COBS de-/encoder
|
||||
void cobsDecodeReset();
|
||||
void handlePacket(uint8_t* buf, int len);
|
||||
void cobsDecode(uint8_t* buf, int len);
|
||||
void cobsDecodeByte(uint8_t byte);
|
||||
std::size_t cobsEncode(const void* data, std::size_t length, uint8_t* buffer);
|
||||
uint8_t msg[2048];
|
||||
uint8_t code;
|
||||
uint8_t block;
|
||||
uint8_t* decode;
|
||||
uint32_t crc32(uint8_t* buf, int len);
|
||||
};
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_CONFIG_HPP
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef STATUS_FIFO_HPP
|
||||
#define STATUS_FIFO_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Everest {
|
||||
|
||||
class StatusFifo {
|
||||
public:
|
||||
// defined messages
|
||||
static constexpr auto ALL_MODULES_STARTED = "ALL_MODULES_STARTED\n";
|
||||
static constexpr auto WAITING_FOR_STANDALONE_MODULES = "WAITING_FOR_STANDALONE_MODULES\n";
|
||||
|
||||
static StatusFifo create_from_path(const std::string&);
|
||||
void update(const std::string&);
|
||||
|
||||
StatusFifo(StatusFifo const&) = delete;
|
||||
StatusFifo& operator=(StatusFifo const&) = delete;
|
||||
// NOTE (aw): the move constructor could be implementented, but we don't need it for now
|
||||
StatusFifo(StatusFifo&&) = delete;
|
||||
StatusFifo& operator=(StatusFifo&&) = delete;
|
||||
~StatusFifo();
|
||||
|
||||
private:
|
||||
StatusFifo() = default;
|
||||
explicit StatusFifo(int fd_) : fd(fd_), disabled(false), opened(true){};
|
||||
|
||||
int fd{-1};
|
||||
bool disabled{true};
|
||||
bool opened{false};
|
||||
};
|
||||
|
||||
} // namespace Everest
|
||||
|
||||
#endif // STATUS_FIFO_HPP
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_THREAD_HPP
|
||||
#define UTILS_THREAD_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
namespace Everest {
|
||||
class Thread {
|
||||
public:
|
||||
~Thread();
|
||||
|
||||
void stop();
|
||||
|
||||
bool shouldExit();
|
||||
Thread& operator=(std::thread&&);
|
||||
|
||||
private:
|
||||
std::thread handle;
|
||||
std::atomic_bool exit_signal{false};
|
||||
};
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_THREAD_HPP
|
||||
204
tools/EVerest-main/lib/everest/framework/include/utils/types.hpp
Normal file
204
tools/EVerest-main/lib/everest/framework/include/utils/types.hpp
Normal file
@@ -0,0 +1,204 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UTILS_TYPES_HPP
|
||||
#define UTILS_TYPES_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fmt/core.h>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <utils/config/types.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using Value = json;
|
||||
using Parameters = json;
|
||||
using Result = std::optional<json>;
|
||||
using JsonCommand = std::function<json(json)>;
|
||||
using Command = std::function<Result(const Parameters&)>;
|
||||
using ArgumentType = std::vector<std::string>;
|
||||
using Arguments = std::map<std::string, ArgumentType>;
|
||||
using ReturnType = std::vector<std::string>;
|
||||
using JsonCallback = std::function<void(json)>;
|
||||
using ValueCallback = std::function<void(Value)>;
|
||||
using ConfigMap = std::map<std::string, everest::config::ConfigEntry>;
|
||||
using ModuleConfigs = std::map<std::string, ConfigMap>;
|
||||
using Array = json::array_t;
|
||||
using Object = json::object_t;
|
||||
// TODO (aw): can we pass the handler arguments by const ref?
|
||||
using Handler = std::function<void(const std::string&, json)>;
|
||||
using StringHandler = std::function<void(std::string)>;
|
||||
using StringPairHandler = std::function<void(const std::string& topic, const std::string& data)>;
|
||||
|
||||
enum class HandlerType {
|
||||
Call,
|
||||
Result,
|
||||
SubscribeVar,
|
||||
SubscribeError,
|
||||
GetConfig,
|
||||
GetConfigResponse,
|
||||
ConfigRequest,
|
||||
ModuleReady,
|
||||
GlobalReady,
|
||||
ExternalMQTT,
|
||||
Unknown
|
||||
};
|
||||
|
||||
struct TypedHandler {
|
||||
std::string name;
|
||||
std::string id;
|
||||
HandlerType type;
|
||||
std::shared_ptr<Handler> handler;
|
||||
|
||||
TypedHandler(const std::string& name_, const std::string& id_, HandlerType type_,
|
||||
std::shared_ptr<Handler> handler_);
|
||||
TypedHandler(const std::string& name_, HandlerType type_, std::shared_ptr<Handler> handler_);
|
||||
TypedHandler(HandlerType type_, std::shared_ptr<Handler> handler_);
|
||||
};
|
||||
|
||||
using Token = std::shared_ptr<TypedHandler>;
|
||||
|
||||
struct ModuleInfo {
|
||||
struct Paths {
|
||||
std::filesystem::path etc;
|
||||
std::filesystem::path libexec;
|
||||
std::filesystem::path share;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::vector<std::string> authors;
|
||||
std::string license;
|
||||
std::string id;
|
||||
Paths paths;
|
||||
bool telemetry_enabled = false;
|
||||
bool global_errors_enabled = false;
|
||||
std::optional<Mapping> mapping;
|
||||
};
|
||||
|
||||
enum class MqttMessageType {
|
||||
Var, ///< Variable message
|
||||
Cmd, ///< Command message
|
||||
CmdResult, ///< Command result message
|
||||
ExternalMQTT, ///< External MQTT message
|
||||
RaiseError, ///< Raise error message
|
||||
ClearError, ///< Clear error message
|
||||
GetConfig, ///< Get config request
|
||||
GetConfigResponse, ///< Get config response
|
||||
Telemetry, ///< Telemetry message
|
||||
Heartbeat, ///< Heartbeat message
|
||||
ModuleReady, ///< Module ready message
|
||||
GlobalReady ///< Global ready message
|
||||
};
|
||||
|
||||
std::string mqtt_message_type_to_string(MqttMessageType type);
|
||||
MqttMessageType string_to_mqtt_message_type(std::string_view str);
|
||||
|
||||
struct MqttMessagePayload {
|
||||
MqttMessageType type; ///< The type of the MQTT message
|
||||
json data; ///< The data of the MQTT message
|
||||
MqttMessagePayload() = delete;
|
||||
template <typename T, typename = typename std::enable_if<std::is_convertible<T, json>::value>::type>
|
||||
MqttMessagePayload(MqttMessageType type_, T&& data_) : type(type_), data(std::forward<T>(data_)) {
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Contains everything that's needed to initialize a requirement in user code
|
||||
struct RequirementInitializer {
|
||||
Requirement requirement;
|
||||
Fulfillment fulfillment;
|
||||
std::optional<Mapping> mapping;
|
||||
};
|
||||
|
||||
using RequirementInitialization = std::map<std::string, std::vector<RequirementInitializer>>;
|
||||
|
||||
struct ImplementationIdentifier {
|
||||
ImplementationIdentifier(const std::string& module_id_, const std::string& implementation_id_,
|
||||
std::optional<Mapping> mapping_ = std::nullopt);
|
||||
std::string to_string() const;
|
||||
std::string module_id;
|
||||
std::string implementation_id;
|
||||
std::optional<Mapping> mapping;
|
||||
};
|
||||
bool operator==(const ImplementationIdentifier& lhs, const ImplementationIdentifier& rhs);
|
||||
bool operator!=(const ImplementationIdentifier& lhs, const ImplementationIdentifier& rhs);
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
template <> struct adl_serializer<Mapping> {
|
||||
static void to_json(json& j, const Mapping& m);
|
||||
static Mapping from_json(const json& j);
|
||||
};
|
||||
template <> struct adl_serializer<TelemetryConfig> {
|
||||
static void to_json(json& j, const TelemetryConfig& t);
|
||||
static TelemetryConfig from_json(const json& j);
|
||||
};
|
||||
template <> struct adl_serializer<ModuleTierMappings> {
|
||||
static void to_json(json& j, const ModuleTierMappings& m);
|
||||
static ModuleTierMappings from_json(const json& j);
|
||||
};
|
||||
template <> struct adl_serializer<Requirement> {
|
||||
static void to_json(json& j, const Requirement& r);
|
||||
static Requirement from_json(const json& j);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<Fulfillment> {
|
||||
static void to_json(json& j, const Fulfillment& f);
|
||||
static Fulfillment from_json(const json& j);
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<MqttMessagePayload> {
|
||||
static void to_json(json& j, const MqttMessagePayload& m);
|
||||
static MqttMessagePayload from_json(const json& j);
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#define EVCALLBACK(function) [](auto&& PH1) { function(std::forward<decltype(PH1)>(PH1)); }
|
||||
|
||||
namespace Everest {
|
||||
inline constexpr int mqtt_get_config_timeout_ms = 5000;
|
||||
inline constexpr std::size_t mqtt_get_config_retries = 1;
|
||||
|
||||
/// \brief Errors than can happen related to commands
|
||||
enum class CmdErrorType {
|
||||
MessageParsingError, ///< Parsing of the message to the command handler has failed
|
||||
SchemaValidationError, ///< Schema validation of arguments or result has failed
|
||||
HandlerException, ///< An exception was thrown during handling of the command in user code
|
||||
CmdTimeout, ///< A timeout happened when calling the command (eg. when the callee doesn't respond to the caller)
|
||||
Shutdown, ///< EVerest is shutting down during a command call
|
||||
NotReady ///< EVerest / the callee is not yet ready but received a command call already from another module
|
||||
};
|
||||
|
||||
struct CmdResultError {
|
||||
CmdErrorType event;
|
||||
std::string msg;
|
||||
std::exception_ptr ex;
|
||||
};
|
||||
|
||||
struct CmdResult {
|
||||
std::optional<json> result;
|
||||
std::optional<CmdResultError> error;
|
||||
};
|
||||
|
||||
/// \brief MQTT Quality of service
|
||||
enum class QOS {
|
||||
QOS0, ///< At most once delivery
|
||||
QOS1, ///< At least once delivery
|
||||
QOS2 ///< Exactly once delivery
|
||||
};
|
||||
|
||||
struct MQTTRequest {
|
||||
std::string response_topic;
|
||||
QOS qos = QOS::QOS2;
|
||||
std::chrono::milliseconds timeout = std::chrono::milliseconds(mqtt_get_config_timeout_ms);
|
||||
std::optional<std::string> request_topic;
|
||||
std::optional<std::string> request_data;
|
||||
};
|
||||
} // namespace Everest
|
||||
|
||||
#endif // UTILS_TYPES_HPP
|
||||
Reference in New Issue
Block a user