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:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,19 @@
{% from "helper_macros.j2" import insert_block, print_template_info %}
{{ print_template_info('3', 'marked regions will be kept', '#') }}
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
{{ insert_block(info.blocks.add_general) }}
{% if provides %}
target_sources(${MODULE_NAME}
PRIVATE
{% for impl in provides %}
"{{ impl.cpp_file_rel_path }}"
{% endfor %}
)
{% endif %}
{{ insert_block(info.blocks.add_other) }}

View File

@@ -0,0 +1,127 @@
{% macro cpp_type(type, interface=none, call=false) -%}
{% if type.is_variant -%}
std::variant<{{ type.cpp_type|join(', ') }}>
{%- else -%}
{% if interface is not none and 'object_type' in type %}
{{ interface + '::' + type.object_type }}
{% else %}
{% if call -%}{{'const '}}{%- endif %}
{% if 'object_type' in type -%}
{{ type.object_type }}
{%- elif 'enum_type' in type -%}
{{ type.enum_type }}
{%- elif 'array_type' in type -%}
std::vector<{{ type.array_type }}>
{%- else -%}
{{ type.cpp_type }}
{%- endif %}
{% if call -%}&{%- endif %}
{%- endif %}
{%- endif %}
{%- endmacro %}
{% macro result_type(type, interface=none) -%}
{% if type -%}
{{ cpp_type(type) }}
{%- else -%}
void
{%- endif %}
{%- endmacro %}
{% macro var_to_any(var, name) -%}
{% if var.is_variant -%}
Everest::variant_to_json({{ name }})
{%- else -%}
{{ name }}
{%- endif %}
{%- endmacro %}
{% macro var_to_cpp(arg) -%}
{% if arg.is_variant -%}
Everest::json_to_variant<{{ arg.cpp_type|join(', ')}}>
{%- else -%}
static_cast<{{ arg.cpp_type }}>
{%- endif %}
{%- endmacro %}
{% macro call_cmd_signature(cmd, interface=none) -%}
{{ result_type(cmd.result, interface) }} call_{{ cmd.name }}(
{%- for arg in cmd.args -%}
{{ cpp_type(arg, none, true) }} {{ arg.name }}{{ ', ' if not loop.last }}
{%- endfor -%}
)
{%- endmacro %}
{% macro handle_cmd_signature(cmd, class_name=None, interface=none) -%}
{% if not class_name %}virtual {% endif -%}
{{ result_type(cmd.result, interface) }} {% if class_name %}{{ class_name }}::{% endif -%}
handle_{{ cmd.name }}(
{%- for arg in cmd.args -%}
{{ cpp_type(arg) }}& {{ arg.name }}{{ ', ' if not loop.last }}
{%- endfor -%}
)
{%- endmacro %}
{% macro publish_var_signature(var) %}
void publish_{{ var.name }}({% if var.json_type != 'null' %}{{ cpp_type(var) }} value{% endif %}) {
_ev->publish(_name, "{{ var.name }}", {% if var.json_type != 'null' %}{{ var_to_any(var, 'value')}}{% else %}nullptr{% endif %});
}
{% endmacro %}
{% macro list_json_types(json_type) -%}
{% if json_type is iterable and json_type is not string -%}
{
{%- for type in json_type -%}
"{{ type }}"{{ ', ' if not loop.last}}
{%- endfor -%}
}
{%- else -%}
{"{{ json_type }}"}
{%- endif %}
{%- endmacro %}
{% macro insert_block(block, indent=0) %}
{{ block.tag }}
{{ " "*indent if block.first_use }}{{ block.content }}
{{ " "*indent }}{{ block.tag }}
{%- endmacro %}
{% macro print_spdx_line(license, year_tag=None) %}
// SPDX-License-Identifier: {{ license }}
// Copyright{% if year_tag %} {{ year_tag }}{% endif %} Pionix GmbH and Contributors to EVerest
{%- endmacro %}
{% macro print_license_header(license) %}
{{ license }}
{%- endmacro %}
{% macro print_template_info(version, title='DO NOT EDIT!', comment_sep='//') %}
{{ comment_sep }}
{{ comment_sep }} AUTO GENERATED - {{ title|upper }}
{{ comment_sep }} template version {{ version }}
{{ comment_sep }}
{%- endmacro %}
{% macro string_to_enum(enum_type) %}
{% if '::' not in enum_type %}
string_to_{{ enum_type | snake_case -}}
{% else %}
{{ enum_type[:enum_type.rfind('::')] }}::string_to_{{ enum_type[(enum_type.rfind('::')+2):] | snake_case -}}
{% endif %}
{%- endmacro %}
{% macro enum_to_string(enum_type) %}
{% if '::' not in enum_type %}
{{ enum_type | snake_case }}_to_string
{%- else %}
{{ enum_type[:enum_type.rfind('::')] }}::{{ enum_type[(enum_type.rfind('::')+2):] | snake_case }}_to_string
{%- endif %}
{%- endmacro %}
{% macro enum_to_string_view(enum_type) %}
{% if '::' not in enum_type %}
{{ enum_type | snake_case }}_to_string_view
{%- else %}
{{ enum_type[:enum_type.rfind('::')] }}::{{ enum_type[(enum_type.rfind('::')+2):] | snake_case }}_to_string_view
{%- endif %}
{%- endmacro %}

View File

@@ -0,0 +1,24 @@
.. _everest_modules_handwritten_{{ info.name }}:
.. This file is a placeholder for optional multiple files
handwritten documentation for the {{ info.name }} module.
.. This handwritten documentation is optional. In case
you do not want to write it, you can delete the doc/ directory.
.. The documentation can be written in reStructuredText,
and will be converted to HTML and PDF by Sphinx.
This index.rst file is the entry point for the module documentation.
.. Use underlined-only headlines inside this document (highest-level
sub-section headline should use "=" characters)
.. The content of this file will be included in the auto-generated HTML
page for the module. You can link to it using the following
reference: everest_modules_{{ info.name }}.
.. *******************************************
.. {{ info.name }}
.. *******************************************
{{ info.desc }}

View File

@@ -0,0 +1,194 @@
{% from "helper_macros.j2" import handle_cmd_signature, publish_var_signature, list_json_types, var_to_cpp, var_to_any, print_template_info, print_spdx_line, string_to_enum, enum_to_string, enum_to_string_view %}
{{ print_spdx_line('Apache-2.0') }}
#ifndef {{ info.hpp_guard }}
#define {{ info.hpp_guard }}
{{ print_template_info('6') }}
#include <framework/ModuleAdapter.hpp>
#include <utils/types.hpp>
#include <utils/error.hpp>
#include <utils/error/error_state_monitor.hpp>
#include <utils/error/error_manager_impl.hpp>
#include <utils/error/error_factory.hpp>
#include "Types.hpp"
class {{ info.class_name }} : public Everest::ImplementationBase {
public:
{{ info.class_name }}(Everest::ModuleAdapter* ev, const std::string& name)
: Everest::ImplementationBase(),
_ev(ev),
_name(name) {
if (ev == nullptr) {
EVLOG_error << "ev is nullptr, please check the initialization of the module";
error_manager = nullptr;
error_state_monitor = nullptr;
error_factory = nullptr;
EVLOG_error << "error_manager, error_state_monitor and error_factory are nullptr";
} else {
error_manager = ev->get_error_manager_impl(name);
if (error_manager == nullptr) {
EVLOG_error << "error_manager is nullptr";
}
error_state_monitor = ev->get_error_state_monitor_impl(name);
if (error_state_monitor == nullptr) {
EVLOG_error << "error_state_monitor is nullptr";
}
error_factory = ev->get_error_factory(name);
if (error_factory == nullptr) {
EVLOG_error << "error_factory is nullptr";
}
impl_mapping = Everest::get_impl_mapping(ev->get_mapping(), name);
}
}
{% if not vars %}
// no variables defined for this interface
{% else %}
// publish functions for variables
{% for var in vars %}
void publish_{{ var.name }}(
{%- if 'array_type' in var %}
const std::vector<{{ var.array_type }}>&
{%- elif 'object_type' in var %}
{{- 'const '}} {{- var.object_type }}&
{%- elif 'enum_type' in var %}
{{- 'const '}} {{- var.enum_type }}&
{%- else %}
{{- 'const '}} {{- var.cpp_type }}&
{%- endif -%}
{{ ' value) {' }}
{% if 'object_type' in var %}
_ev->publish(_name, "{{ var.name }}", value);
{% elif 'enum_type' in var %}
_ev->publish(_name, "{{ var.name }}", {{ enum_to_string_view(var.enum_type) }}(value));
{% elif 'array_type' in var %}
{% if 'array_type_contains_enum' in var %}
std::vector<std::string> string_array;
string_array.reserve(value.size());
for (const auto& entry : value) {
string_array.push_back({{ enum_to_string(var.array_type) }}(entry));
}
_ev->publish(_name, "{{ var.name }}", string_array);
{% else %}
_ev->publish(_name, "{{ var.name }}", value);
{% endif %}
{% else %}
_ev->publish(_name, "{{ var.name }}", value);
{% endif %}
}
{% endfor %}
{% endif %}
void raise_error(const Everest::error::Error& error) {
error_manager->raise_error(error);
}
void clear_error(const Everest::error::ErrorType& type) {
error_manager->clear_error(type);
}
void clear_error(const Everest::error::ErrorType& type, const Everest::error::ErrorSubType& sub_type) {
error_manager->clear_error(type, sub_type);
}
void clear_all_errors_of_impl() {
error_manager->clear_all_errors();
}
void clear_all_errors_of_impl(const Everest::error::ErrorType& type) {
error_manager->clear_all_errors(type);
}
std::shared_ptr<Everest::error::ErrorStateMonitor> error_state_monitor;
std::shared_ptr<Everest::error::ErrorFactory> error_factory;
std::shared_ptr<Everest::error::ErrorManagerImpl> error_manager;
std::optional<Mapping> get_mapping() {
return impl_mapping;
}
protected:
{% if not cmds %}
// no commands defined for this interface
{% else %}
// command handler functions (virtual)
{% for cmd in cmds %}
{{ handle_cmd_signature(cmd, none, info.interface_name) }} = 0;
{% endfor %}
{% endif %}
private:
Everest::ModuleAdapter* const _ev;
const std::string _name;
std::optional<Mapping> impl_mapping;
// helper function for getting all commands
void _gather_cmds([[maybe_unused]] std::vector<Everest::cmd>& cmds) override {
{% if not cmds %}
// this interface does not offer any commands
{% else %}
{% for cmd in cmds %}
// {{ cmd.name }} command
Everest::cmd {{ cmd.name }}_cmd;
{{ cmd.name }}_cmd.impl_id = _name;
{{ cmd.name }}_cmd.cmd_name = "{{ cmd.name }}";
{% if not cmd.args %}
// cmd {{ cmd.name }} has no arguments
{% else %}
{{ cmd.name }}_cmd.arg_types = {
{% for arg in cmd.args %}
{"{{ arg.name }}", {{ list_json_types(arg.json_type) + '}' }}{{ ',' if not loop.last }}
{% endfor %}
};
{% endif %}
{{ cmd.name }}_cmd.cmd = [this](const Parameters& args) -> Result {
{% for arg in cmd.args %}
{% if 'object_type' in arg %}
auto {{ arg.name }} = args.at("{{ arg.name }}").get<{{ arg.object_type }}>();
{% elif 'enum_type' in arg %}
auto {{ arg.name }} = {{ string_to_enum(arg.enum_type) }}(args.at("{{ arg.name }}").get<std::string>());
{% elif 'array_type' in arg %}
json {{ arg.name }}_json_array = args["{{ arg.name }}"];
{% if 'array_type_contains_enum' in arg %}
std::vector<{{ arg.array_type }}> {{ arg.name }};
for (auto entry : {{ arg.name }}_json_array) {
{{ arg.name }}.push_back({{ string_to_enum(arg.array_type) }}(entry));
}
{% else %}
auto {{ arg.name }} = args.at("{{ arg.name }}").get<std::vector<{{ arg.array_type }}>>();
{% endif %}
{% else %}
auto {{ arg.name }} = {{ var_to_cpp(arg) }}(args.at("{{ arg.name }}"));
{% endif %}
{% else %}
(void) args; // no arguments used for this callback
{% endfor %}
{{ 'auto result = ' if cmd.result }}this->handle_{{ cmd.name }}(
{%- for arg in cmd.args -%}
{{ arg.name }}{{ ', ' if not loop.last }}
{%- endfor -%}
);
{% if cmd.result and 'object_type' in cmd.result %}
return result;
{% elif cmd.result and 'enum_type' in cmd.result %}
return {{ enum_to_string(cmd.result.enum_type) }}(result);
{% else %}
return {{ var_to_any(cmd.result, 'result') if cmd.result else 'nullptr'}};
{% endif %}
};
{% if cmd.result %}
{{ cmd.name }}_cmd.return_type = {{ list_json_types(cmd.result.json_type) }};
{% endif %}
cmds.emplace_back(std::move({{ cmd.name }}_cmd));
{% if not loop.last %}
{% endif %}
{% endfor %}
{% endif %}
};
};
#endif // {{ info.hpp_guard }}

View File

@@ -0,0 +1,163 @@
{% from "helper_macros.j2" import call_cmd_signature, var_to_any, var_to_cpp, print_template_info, cpp_type, result_type, print_spdx_line, string_to_enum, enum_to_string %}
{{ print_spdx_line('Apache-2.0') }}
#ifndef {{ info.hpp_guard }}
#define {{ info.hpp_guard }}
{{ print_template_info('5') }}
#include <framework/ModuleAdapter.hpp>
#include <utils/types.hpp>
#include <utils/error.hpp>
#include <utils/error/error_state_monitor.hpp>
#include <utils/error/error_manager_req.hpp>
#include "Types.hpp"
class {{ info.class_name }} {
public:
{{ info.class_name }}(Everest::ModuleAdapter* adapter, Requirement req, const std::string& module_id, std::optional<Mapping> mapping)
: module_id(module_id),
_adapter(adapter),
_req(req),
_mapping(mapping) {
if (adapter == nullptr) {
EVLOG_error << "adapter is nullptr, please check the initialization of the module";
error_manager = nullptr;
error_state_monitor = nullptr;
EVLOG_error << "error_manager and error_state_monitor are nullptr";
} else {
error_manager = adapter->get_error_manager_req(req);
if (error_manager == nullptr) {
EVLOG_error << "error_manager is nullptr";
}
error_state_monitor = adapter->get_error_state_monitor_req(req);
if (error_state_monitor == nullptr) {
EVLOG_error << "error_state_monitor is nullptr";
}
}
}
const std::string module_id;
{% if not vars %}
// this interface does not export any variables to subscribe to
{% else %}
// variables available for subscription
{% for var in vars %}
void subscribe_{{ var.name }}(const std::function<void({% if var.json_type != 'null' %}const {{ cpp_type(var) }}&{% endif %})>& listener) {
_adapter->subscribe(_req, "{{ var.name }}", [func = std::move(listener)](const Value& value) {
{% if 'object_type' in var %}
func(value);
{% elif 'enum_type' in var %}
func({{ string_to_enum(var.enum_type) }}({{ var_to_cpp(var) }}(value)));
{% elif 'array_type' in var %}
{% if 'array_type_contains_enum' in var %}
std::vector<{{ var.array_type }}> typed_value;
for (auto& entry : value) {
typed_value.push_back({{ string_to_enum(var.array_type) }}(entry));
}
func(typed_value);
{% else %}
func(value);
{% endif %}
{% elif var.json_type != 'null' %}
func({{ var_to_cpp(var) }}(value));
{% else %}
if (not Everest::detail::is_type_compatible<{{ cpp_type(var) }}>(value.type())) {
EVLOG_error << "Callback for variable '{{ var.name }}' in interface '{{ info.interface }}' has wrong type!";
}
func();
{% endif %}
});
}
{% if not loop.last %}
{% endif %}
{% endfor %}
{% endif %}
void subscribe_error(
const Everest::error::ErrorType& type,
const Everest::error::ErrorCallback& callback,
const Everest::error::ErrorCallback& clear_callback
) {
error_manager->subscribe_error(type, callback, clear_callback);
}
void subscribe_all_errors(
const Everest::error::ErrorCallback& callback,
const Everest::error::ErrorCallback& clear_callback
) {
error_manager->subscribe_all_errors(callback, clear_callback);
}
{% if not cmds %}
// this interface does not export any commands to call
{% else %}
// commands available to call
{% for cmd in cmds %}
{{ call_cmd_signature(cmd, info.interface_name) }} {
{% for arg in cmd.args %}
{% if 'array_type' in arg %}
{% if 'array_type_contains_enum' in arg %}
Array {{ arg.name }}_array;
for (const auto& {{ arg.name }}_entry : {{ arg.name }}) {
{{ arg.name }}_array.push_back({{ enum_to_string(arg.array_type) }}({{ arg.name }}_entry));
}
{% endif %}
{% endif %}
{% endfor %}
{{ '' }}{% if cmd.result %}Result result = {% endif %}_adapter->call(_req, "{{ cmd.name }}",
{{ 'Parameters{' }}
{% for arg in cmd.args %}
{% if 'enum_type' in arg %}
{"{{ arg.name }}", {{ enum_to_string(arg.enum_type) }}({{ arg.name }})}
{% elif 'object_type' in arg %}
{"{{ arg.name }}", {{ arg.name }}}
{% elif 'array_type' in arg %}
{% if 'array_type_contains_enum' in arg %}
{"{{ arg.name }}", {{ var_to_any(arg, arg.name + '_array') }}}
{% else %}
{"{{ arg.name }}", {{ var_to_any(arg, arg.name) }}}
{% endif %}
{% else %}
{"{{ arg.name }}", {{ var_to_any(arg, arg.name) }}}
{% endif%}
{% if not loop.last %},{% endif %}
{% endfor %}
{{ '}' }}
);
{% if cmd.result %}
{% if 'enum_type' in cmd.result %}
auto retval = {{ string_to_enum(cmd.result.enum_type) }}({{ var_to_cpp(cmd.result) }}(result.value()));
{% elif 'object_type' in cmd.result %}
json retval_json = result.value();
{{ result_type(cmd.result) }} retval = retval_json;
{% elif 'array_type' in cmd.result %}
{{ result_type(cmd.result) }} retval (result.value().begin(), result.value().end());
{% else %}
auto retval = {{ var_to_cpp(cmd.result) }}(result.value());
{% endif %}
return retval;
{% endif %}
}
{% if not loop.last %}
{% endif %}
{% endfor %}
{% endif %}
std::shared_ptr<Everest::error::ErrorStateMonitor> error_state_monitor;
std::optional<Mapping> get_mapping() {
return _mapping;
}
private:
std::shared_ptr<Everest::error::ErrorManagerReq> error_manager;
Everest::ModuleAdapter* const _adapter;
Requirement _req;
std::optional<Mapping> _mapping;
};
#endif // {{ info.hpp_guard }}

View File

@@ -0,0 +1,27 @@
{% from "helper_macros.j2" import handle_cmd_signature, print_license_header %}
{{ print_license_header(info.license_header) }}
#include "{{ info.class_name}}.hpp"
namespace module {
namespace {{ info.interface_implementation_id }} {
void {{ info.class_name }}::init() {
}
void {{ info.class_name }}::ready() {
}
{% for cmd in cmds %}
{{ handle_cmd_signature(cmd, info.class_name, '::'+info.interface) }}{
// your code for cmd {{ cmd.name }} goes here
{% if cmd.result %}
return {{ cmd.result.json_type|create_dummy_result }};
{% endif %}
}
{% endfor %}
} // namespace {{ info.interface_implementation_id }}
} // namespace module

View File

@@ -0,0 +1,61 @@
{% from "helper_macros.j2" import handle_cmd_signature, print_template_info, insert_block, cpp_type, print_license_header %}
{{ print_license_header(info.license_header) }}
#ifndef {{ info.hpp_guard }}
#define {{ info.hpp_guard }}
{{ print_template_info('3', 'marked regions will be kept') }}
#include <{{ info.base_class_header }}>
#include "{{ info.module_header }}"
{{ insert_block(info.blocks.add_headers) }}
namespace module {
namespace {{ info.interface_implementation_id }} {
struct Conf {
{% for item in info.config %}
{{ cpp_type(item) }} {{ item.name }};
{% endfor %}
};
class {{ info.class_name }} : public {{ info.class_parent}} {
public:
{{ info.class_name }}() = delete;
{{ info.class_name }}(Everest::ModuleAdapter* ev, const Everest::PtrContainer<{{ info.module_class }}> &mod, Conf& config) :
{{ info.class_parent }}(ev, "{{ info.interface_implementation_id }}"),
mod(mod),
config(config)
{};
{{ insert_block(info.blocks.public_defs, indent=4) }}
protected:
{% if not cmds %}
// no commands defined for this interface
{% else %}
// command handler functions (virtual)
{% for cmd in cmds %}
{{ handle_cmd_signature(cmd, none, '::'+info.interface) }} override;
{% endfor %}
{% endif %}
{{ insert_block(info.blocks.protected_defs, indent=4) }}
private:
const Everest::PtrContainer<{{ info.module_class }}>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
{{ insert_block(info.blocks.private_defs, indent=4) }}
};
{{ insert_block(info.blocks.after_class) }}
} // namespace {{ info.interface_implementation_id }}
} // namespace module
#endif // {{ info.hpp_guard }}

View File

@@ -0,0 +1,172 @@
{% from "helper_macros.j2" import print_template_info, var_to_cpp, print_spdx_line %}
{{ print_spdx_line('Apache-2.0') }}
{{ print_template_info('5') }}
#include "{{ info.ld_ev_header }}"
#ifdef EVEREST_COVERAGE_ENABLED
#include <everest/helpers/coverage.hpp>
#endif
#include "{{ info.module_header }}"
{% for impl in provides %}
#include "{{ impl.class_header }}"
{% endfor %}
#include <framework/runtime.hpp>
#include <utils/types.hpp>
#include <generated/version_information.hpp>
#ifndef PROJECT_NAME
#define PROJECT_NAME "undefined project"
#endif
#ifndef PROJECT_VERSION
#define PROJECT_VERSION "undefined version"
#endif
#ifndef GIT_VERSION
#define GIT_VERSION "undefined git version"
#endif
{% if info.enable_global_errors %}
#include <utils/error/error_manager_req_global.hpp>
{% endif %}
namespace module {
// FIXME (aw): could this way of keeping static variables be changed somehow?
static Everest::ModuleAdapter adapter {};
static Everest::PtrContainer<{{info.class_name }}> mod_ptr {};
// per module configs
{# FIXME (aw): instead of being static, this could also be inside a map #}
{% for impl in provides %}
static {{ impl.id }}::Conf {{ impl.id }}_config;
{% endfor %}
static Conf module_conf;
static ModuleInfo module_info;
{% if info.enable_global_errors %}
void subscribe_global_all_errors(
const Everest::error::ErrorCallback& callback,
const Everest::error::ErrorCallback& clear_callback
) {
adapter.get_global_error_manager()->subscribe_global_all_errors(callback, clear_callback);
}
std::shared_ptr<Everest::error::ErrorStateMonitor> get_global_error_state_monitor() {
return adapter.get_global_error_state_monitor();
}
{% endif %}
std::shared_ptr<Everest::config::ConfigServiceClient> get_config_service_client() {
return adapter.get_config_service_client();
}
void LdEverest::init(ModuleConfigs module_configs, const ModuleInfo& mod_info) {
EVLOG_debug << "init() called on module {{ info.name }}";
// populate config for provided implementations
{% for impl in provides %}
auto {{ impl.id }}_config_input = std::move(module_configs["{{ impl.id }}"]);
{% for item in impl.config %}
{{ impl.id }}_config.{{ item.name }} = std::get<{{ item.cpp_type }}>({{ impl.id }}_config_input["{{ item.name }}"]);
{% endfor %}
{% endfor %}
{% if info.module_config|length %}
{% for item in info.module_config %}
module_conf.{{ item.name }} = std::get<{{ item.cpp_type }}>(module_configs["!module"]["{{ item.name }}"]);
{% endfor %}
{% endif %}
module_info = mod_info;
mod_ptr->init();
}
void LdEverest::ready() {
EVLOG_debug << "ready() called on module {{ info.name }}";
mod_ptr->ready();
}
void register_module_adapter(Everest::ModuleAdapter module_adapter) {
adapter = std::move(module_adapter);
}
std::vector<Everest::cmd> everest_register(const RequirementInitialization& requirement_init) {
EVLOG_debug << "everest_register() called on module {{ info.name }}";
adapter.check_complete();
{% for impl in provides %}
auto p_{{ impl.id }} = std::make_unique<{{ impl.id }}::{{ impl.class_name }}>(&adapter, mod_ptr, {{ impl.id }}_config);
adapter.gather_cmds(*p_{{ impl.id }});
{% endfor %}
{% for requirement in requires %}
{# FIXME: needs refactoring #}
{% if requirement.is_vector %}
auto r_{{ requirement.id }} = std::vector<std::unique_ptr<{{ requirement.class_name }}>>();
if (auto it = requirement_init.find("{{ requirement.id }}"); it != requirement_init.end()) {
for (const auto& requirement_initializer : (*it).second) {
auto requirement_module_id = requirement_initializer.fulfillment.module_id;
auto requirement = requirement_initializer.requirement;
auto mapping = requirement_initializer.mapping;
r_{{ requirement.id }}.emplace_back(std::make_unique<{{ requirement.class_name }}>(&adapter, requirement, requirement_module_id, mapping));
}
}
{% else %}
std::string r_{{ requirement.id }}_requirement_module_id;
Requirement r_{{ requirement.id }}_requirement;
std::optional<Mapping> r_{{ requirement.id }}_mapping;
if (auto it = requirement_init.find("{{ requirement.id }}"); it != requirement_init.end()) {
auto requirement_initializer = (*it).second;
if (requirement_initializer.size() > 0) {
r_{{ requirement.id }}_requirement_module_id = requirement_initializer.at(0).fulfillment.module_id;
r_{{ requirement.id }}_requirement = requirement_initializer.at(0).requirement;
r_{{ requirement.id }}_mapping = requirement_initializer.at(0).mapping;
}
}
auto r_{{ requirement.id }} = std::make_unique<{{ requirement.class_name }}>(&adapter, r_{{ requirement.id }}_requirement, r_{{ requirement.id }}_requirement_module_id, r_{{ requirement.id }}_mapping);
{% endif %}
{% else %}
(void) requirement_init; // no requirements -> unused requirement initialization
{% endfor %}
{% if info.enable_external_mqtt %}
static Everest::MqttProvider mqtt_provider(adapter);
{% endif %}
static Everest::TelemetryProvider telemetry_provider(adapter);
static {{ info.class_name }} module(
module_info,
{%- if info.enable_external_mqtt %}mqtt_provider, {% endif -%}
{%- if info.enable_telemetry %}telemetry_provider, {% endif -%}
{%- for impl in provides -%}
std::move(p_{{ impl.id }}){{ ', ' }}
{%- endfor -%}
{%- for requirement in requires -%}
std::move(r_{{ requirement.id }}){{ ', ' }}
{%- endfor -%}
module_conf);
mod_ptr.set(&module);
return adapter.registered_commands;
}
} // namespace module
int main(int argc, char* argv[]) {
#ifdef EVEREST_COVERAGE_ENABLED
everest::helpers::install_signal_handlers_for_gcov();
#endif
auto module_loader = Everest::ModuleLoader(argc, argv, Everest::ModuleCallbacks(
module::register_module_adapter, module::everest_register,
module::LdEverest::init, module::LdEverest::ready),
{PROJECT_NAME, PROJECT_VERSION, GIT_VERSION});
return module_loader.initialize();
}

View File

@@ -0,0 +1,37 @@
{% from "helper_macros.j2" import print_template_info, print_spdx_line %}
{{ print_spdx_line('Apache-2.0') }}
#ifndef {{ info.hpp_guard }}
#define {{ info.hpp_guard }}
{{ print_template_info('3') }}
#include <framework/ModuleAdapter.hpp>
#include <framework/everest.hpp>
#include <everest/logging.hpp>
namespace module {
// helper class for invoking private functions on module
struct LdEverest {
static void init(ModuleConfigs module_configs, const ModuleInfo& info);
static void ready();
};
void register_module_adapter(Everest::ModuleAdapter module_adapter);
std::vector<Everest::cmd> everest_register(const RequirementInitialization& requirement_init);
{% if info.enable_global_errors %}
void subscribe_global_all_errors(
const Everest::error::ErrorCallback& callback,
const Everest::error::ErrorCallback& clear_callback
);
std::shared_ptr<Everest::error::ErrorStateMonitor> get_global_error_state_monitor();
{% endif %}
std::shared_ptr<Everest::config::ConfigServiceClient> get_config_service_client();
} // namespace module
#endif // {{ info.hpp_guard }}

View File

@@ -0,0 +1,19 @@
{% from "helper_macros.j2" import print_license_header %}
{{ print_license_header(info.license_header) }}
#include "{{ info.module_header }}"
namespace module {
void {{ info.class_name }}::init() {
{% for impl in provides %}
invoke_init(*p_{{ impl.id }});
{% endfor %}
}
void {{ info.class_name }}::ready() {
{% for impl in provides %}
invoke_ready(*p_{{ impl.id }});
{% endfor %}
}
} // namespace module

View File

@@ -0,0 +1,109 @@
{% from "helper_macros.j2" import print_template_info, insert_block, cpp_type, print_license_header %}
{{ print_license_header(info.license_header) }}
#ifndef {{ info.hpp_guard }}
#define {{ info.hpp_guard }}
{{ print_template_info('2', 'marked regions will be kept') }}
#include "{{ info.ld_ev_header }}"
{% for impl in provides %}
{% if loop.first %}
// headers for provided interface implementations
{% endif %}
#include <{{ impl.base_class_header }}>
{% endfor %}
{% for interface in requires %}
{% if loop.first %}
// headers for required interface implementations
{% endif %}
#include <{{ interface.exports_header }}>
{% endfor %}
{{ insert_block(info.blocks.add_headers) }}
namespace module {
struct Conf {
{% for item in info.module_config %}
{{ cpp_type(item) }} {{ item.name }};
{% endfor %}
};
class {{ info.class_name }} : public Everest::ModuleBase {
public:
{{ info.class_name }}() = delete;
{{ info.class_name }}(
const ModuleInfo& info,
{% if info.enable_external_mqtt %}
Everest::MqttProvider& mqtt_provider,
{% endif %}
{% if info.enable_telemetry %}
Everest::TelemetryProvider& telemetry,
{% endif %}
{% for impl in provides %}
std::unique_ptr<{{ impl.base_class }}> p_{{ impl.id }},
{% endfor %}
{% for requirement in requires %}
{% if requirement.is_vector %}
std::vector<std::unique_ptr<{{ requirement.class_name }}>> r_{{ requirement.id }},
{% else %}
std::unique_ptr<{{ requirement.class_name }}> r_{{ requirement.id }},
{% endif %}
{% endfor %}
Conf& config
) :
ModuleBase(info),
{% if info.enable_external_mqtt %}
mqtt(mqtt_provider),
{% endif %}
{% if info.enable_telemetry %}
telemetry(telemetry),
{% endif %}
{% for impl in provides %}
p_{{ impl.id }}(std::move(p_{{ impl.id }})),
{% endfor %}
{% for requirement in requires %}
r_{{ requirement.id }}(std::move(r_{{ requirement.id }})),
{% endfor %}
config(config)
{};
{% if info.enable_external_mqtt %}
Everest::MqttProvider& mqtt;
{% endif %}
{% if info.enable_telemetry %}
Everest::TelemetryProvider& telemetry;
{% endif %}
{% for impl in provides %}
const std::unique_ptr<{{ impl.base_class }}> p_{{ impl.id }};
{% endfor %}
{% for requirement in requires %}
{% if requirement.is_vector %}
const std::vector<std::unique_ptr<{{ requirement.class_name }}>> r_{{ requirement.id }};
{% else %}
const std::unique_ptr<{{ requirement.class_name }}> r_{{ requirement.id }};
{% endif %}
{% endfor %}
const Conf& config;
{{ insert_block(info.blocks.public_defs, indent=4) }}
protected:
{{ insert_block(info.blocks.protected_defs, indent=4) }}
private:
friend class LdEverest;
void init();
void ready();
{{ insert_block(info.blocks.private_defs, indent=4) }}
};
{{ insert_block(info.blocks.after_class) }}
} // namespace module
#endif // {{ info.hpp_guard }}

View File

@@ -0,0 +1,243 @@
{% from "helper_macros.j2" import print_template_info, print_spdx_line, string_to_enum, enum_to_string, enum_to_string_view %}
{{ print_spdx_line('Apache-2.0') }}
#ifndef {{ info.hpp_guard }}
#define {{ info.hpp_guard }}
{{ print_template_info('5') }}
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
#include <nlohmann/json.hpp>
{% if 'type_headers' in info %}
{% for type_header in info.type_headers %}
#include <{{type_header}}>
{% endfor %}
{% endif %}
using nlohmann::json;
{% if not enums %}
// no enums defined for this interface
{% else %}
// enums of {{ info.interface_name }}
{% for enum in enums %}
{% for namespace in info.namespace %}
namespace {{ namespace }} {
{% endfor %}
enum class {{ enum.enum_type}}
{
{% for e in enum.enum %}
{{e}},
{% endfor %}
};
/// \brief Converts the given {{ enum.enum_type }} \p e to human readable string
/// \returns a string representation of the {{ enum.enum_type }}
inline std::string {{ enum.enum_type | snake_case }}_to_string(const {{ enum.enum_type }}& e) {
switch (e) {
{% for e in enum.enum %}
case {{ enum.enum_type }}::{{ e }}: return "{{e}}";
{% endfor %}
}
throw std::out_of_range("No known string conversion for provided enum of type {{ enum.enum_type }}");
}
/// \brief Converts the given {{ enum.enum_type }} \p e to human readable string
/// \returns a string_view representation of the {{ enum.enum_type }}
inline constexpr std::string_view {{ enum.enum_type | snake_case }}_to_string_view(const {{ enum.enum_type }}& e) {
switch (e) {
{% for e in enum.enum %}
case {{ enum.enum_type }}::{{ e }}: return "{{e}}";
{% endfor %}
}
throw std::out_of_range("No known string conversion for provided enum of type {{ enum.enum_type }}");
}
/// \brief Converts the given std::string \p s to {{ enum.enum_type }}
/// \returns a {{ enum.enum_type }} from a string representation
inline {{ enum.enum_type }} string_to_{{ enum.enum_type | snake_case }}(const std::string& s) {
{% for e in enum.enum %}
if (s == "{{e}}") {
return {{ enum.enum_type }}::{{ e }};
}
{% endfor %}
throw std::out_of_range("Provided string " + s + " could not be converted to enum of type {{ enum.enum_type }}");
}
/// \brief Writes the string representation of the given {{ enum.enum_type }} \p {{ enum.enum_type | snake_case }} to the given output stream \p os
/// \returns an output stream with the {{ enum.enum_type }} written to
inline std::ostream& operator<<(std::ostream& os, const types::{{ info.interface_name }}::{{ enum.enum_type }}& {{ enum.enum_type | snake_case }}) {
os << types::{{info.interface_name}}::{{ enum.enum_type | snake_case }}_to_string({{ enum.enum_type | snake_case }});
return os;
}
{% for namespace in info.namespace|reverse %}
} // namespace {{namespace}}
{% endfor %}
{% endfor %}
{% endif%}
{% if not types %}
// no types defined for this interface
{% else %}
// types of {{ info.interface_name }}
{% for namespace in info.namespace %}
namespace {{ namespace }} {
{% endfor %}
{% for parsed_type in types %}
{% if parsed_type.properties|length > 0 %}
struct {{ parsed_type.name }} {
{% for property in parsed_type.properties %}
{# Constraints will be checked by the framework #}
{{ 'std::optional<' if not property.required -}}
{{ property.type -}}
{{ '>' if not property.required -}}
{{ ' ' + property.name + ';' }} ///< {{ property.info.description }}
{% endfor %}
/// \brief Conversion from a given {{ parsed_type.name }} \p k to a given json object \p j
friend void to_json(json& j, const {{ parsed_type.name }}& k) {
// the required parts of the type
{% if parsed_type.properties|selectattr('required')|list|length %}
j = json{
{%- endif %}
{%- for property in parsed_type.properties %}
{%- if property.required +%}
{"{{property.name}}",
{%- if property.enum %} {{ enum_to_string_view(property.type) }}(k.{{ property.name }})
{%- else %}
{%- if property.type == 'DateTime' %} k.{{property.name}}.to_rfc3339()
{%- else %} k.{{property.name}}
{%- endif %}
{%- endif %}},
{%- endif %}
{%- endfor %}
{% if not parsed_type.properties|selectattr('required')|list|length %}
j = json ({});
{%- else +%}
};
{%- endif %}
// the optional parts of the type
{% for property in parsed_type.properties %}
{% if not property.required %}
if (k.{{property.name}}) {
{% if property.type.startswith('std::vector<') %}
{%- if parsed_type.properties|selectattr('required')|list|length %}
j.emplace("{{property.name}}", *k.{{property.name}});
{%- else %}
{#only optional keys in json#}
{#TODO: add key to json when there are no required keys but multiple optional keys#}
{# FIXME: this is never generated?#}
if (j.size() == 0) {
j = json{{'{{"'+property.name+'", json::array()}};'}}
} else {
j.emplace("{{property.name}}", *k.{{property.name}});
}
{% endif %}
{% else %}
{%- if property.enum %}
j.emplace("{{property.name}}", {{ enum_to_string_view(property.type) }}(*k.{{ property.name }}));
{%- else %}
{%- if property.type == 'DateTime' %}
j.emplace("{{property.name}}", k.{{property.name}}->to_rfc3339());
{%- else %}
j.emplace("{{property.name}}", *k.{{property.name}});
{%- endif %}
{%- endif %}
{% endif %}
}
{% endif %}
{% endfor %}
}
/// \brief Conversion from a given json object \p j to a given {{ parsed_type.name }} \p k
friend void from_json(const json& j, {{ parsed_type.name }}& k) {
// the required parts of the type
{% for property in parsed_type.properties %}
{% if property.required %}
{% if property.type.startswith('std::vector<') %}
k.{{property.name}} = std::move(j.at("{{property.name}}").get<{{property.type}}>());
{% else %}
k.{{property.name}} =
{%- if property.enum %} {{ string_to_enum(property.type) }}(j.at("{{property.name}}"))
{%- else %}
{%- if property.type == 'DateTime' %} DateTime(std::string(j.at("{{property.name}}")));
{%- else %} j.at("{{property.name}}")
{%- endif %}
{%- endif %};
{% endif %}
{% endif %}
{%- endfor %}
// the optional parts of the type
auto it = j.end();
{% for property in parsed_type.properties %}
{% if not property.required %}
it = it = j.find("{{property.name}}");
if (it != j.end()) {
{% if property.type.startswith('std::vector<') %}
k.{{property.name}} = j.at("{{property.name}}").get<{{property.type}}>();
{% else %}
{%- if property.enum %}
k.{{property.name}} = {{ string_to_enum(property.type) }}(it->get<std::string>());
{%- else %}
k.{{property.name}} = it->get<{{property.type}}>();
{% endif %}
{% endif %}
}
{% endif %}
{% endfor %}
}
/// \brief Compares objects of type {{ parsed_type.name }} for equality
friend constexpr bool operator==(const {{ parsed_type.name }}& k, const {{ parsed_type.name }}& l) {
{%- for var, tuple in [('k.', 'lhs'), ('l.', 'rhs')] +%}
const auto& {{ tuple }}_tuple = std::tie(
{%- for property in parsed_type.properties +%}
{{ var }}{{ property.name }}
{%- if not loop.last %},
{%- endif %}
{%- endfor +%}
);
{%- endfor +%}
return lhs_tuple == rhs_tuple;
}
/// \brief Compares objects of type {{ parsed_type.name }} for inequality
friend constexpr bool operator!=(const {{ parsed_type.name }}& k, const {{ parsed_type.name }}& l) {
return not operator==(k, l);
}
/// \brief Writes the string representation of the given {{ parsed_type.name }} \p k to the given output stream \p os
/// \returns an output stream with the {{ parsed_type.name }} written to
friend std::ostream& operator<<(std::ostream& os, const {{ parsed_type.name }}& k) {
os << json(k).dump(4);
return os;
}
};
{% endif %}
{% endfor %}
{% for namespace in info.namespace|reverse %}
} // namespace {{namespace}}
{% endfor %}
{% endif %}
#endif // {{ info.hpp_guard }}