Skip to content

Commit 4e9afb1

Browse files
committed
Add basic entrypoint_API module
Signed-off-by: Christoph Burandt <christoph.burandt@pionix.de>
1 parent d9bf8ae commit 4e9afb1

File tree

12 files changed

+633
-0
lines changed

12 files changed

+633
-0
lines changed

config/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
generate_config_run_script(CONFIG EVerestAPI-entrypoint)
12
generate_config_run_script(CONFIG sil)
23
generate_config_run_script(CONFIG sil-two-evse)
34
generate_config_run_script(CONFIG sil-ocpp)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
settings:
2+
telemetry_enabled: true
3+
active_modules:
4+
api_entrypoint:
5+
module: EVerest_API
6+
mapping:
7+
module:
8+
evse: 1
9+
connector: 2
10+
access:
11+
config:
12+
allow_global_read: true
13+
allow_global_write: false
14+
allow_set_read_only: false
15+
modules:
16+
auth:
17+
allow_read: true
18+
allow_write: true
19+
allow_set_read_only: true
20+
dm_1:
21+
module: display_message_API
22+
config_module:
23+
cfg_communication_check_to_s: 5
24+
mapping:
25+
module:
26+
evse: 1
27+
connector: 1
28+
implementations:
29+
main:
30+
evse: 1
31+
connector: 1
32+
ps_dc_1:
33+
module: power_supply_DC_API
34+
config_module:
35+
cfg_communication_check_to_s: 0
36+
cfg_heartbeat_interval_ms: 10000
37+
mapping:
38+
module:
39+
evse: 1
40+
connector: 1
41+
ps_dc_2:
42+
module: power_supply_DC_API
43+
config_module:
44+
cfg_communication_check_to_s: 0
45+
cfg_heartbeat_interval_ms: 10000
46+
mapping:
47+
module:
48+
evse: 2
49+
connector: 1
50+
51+
# send "{"headers": { "replyTo": "my/reply/topic" } }" to "everest_api/1/entrypoint/api_entrypoint/m2e/discover"
52+
# and receive an array of (module_id, type, version[optional]) tuples on the reply topic

modules/API/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
ev_add_module(API)
22
ev_add_module(EvAPI)
3+
ev_add_module(EVerest_API)
34
ev_add_module(auth_consumer_API)
45
ev_add_module(auth_token_provider_API)
56
ev_add_module(auth_token_validator_API)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
3+
# template version 3
4+
#
5+
6+
# module setup:
7+
# - ${MODULE_NAME}: module name
8+
ev_setup_cpp_module()
9+
10+
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
11+
# insert your custom targets and additional config variables here
12+
13+
target_compile_options(${MODULE_NAME}
14+
PUBLIC -Wall -Wextra -pedantic -Werror=switch)
15+
16+
target_link_libraries(${MODULE_NAME}
17+
PRIVATE
18+
atomic
19+
everest::everest_api_types
20+
)
21+
22+
install(
23+
FILES ${CMAKE_CURRENT_SOURCE_DIR}/apis.yaml
24+
DESTINATION "${CMAKE_INSTALL_DATADIR}/everest/modules/${MODULE_NAME}/"
25+
)
26+
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
27+
28+
target_sources(${MODULE_NAME}
29+
PRIVATE
30+
"main/generic_errorImpl.cpp"
31+
)
32+
33+
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
34+
# insert other things like install cmds etc here
35+
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Pionix GmbH and Contributors to EVerest
3+
#include "EVerest_API.hpp"
4+
5+
#include <everest_api_types/entrypoint/codec.hpp>
6+
#include <everest_api_types/entrypoint/json_codec.hpp>
7+
#include <everest_api_types/generic/codec.hpp>
8+
#include <everest_api_types/utilities/codec.hpp>
9+
#include <utils/yaml_loader.hpp>
10+
11+
#include <map>
12+
#include <string>
13+
#include <utility>
14+
15+
namespace {
16+
17+
struct ApiModuleParameter {
18+
std::string api_type{};
19+
std::optional<int> api_version{};
20+
};
21+
22+
std::map<std::string, ApiModuleParameter>
23+
get_all_ApiModuleParameters(Everest::config::ConfigServiceClient* config_service_client,
24+
const std::filesystem::path& api_details_filepath) {
25+
std::map<std::string, ApiModuleParameter> api_modules;
26+
27+
json schema_data;
28+
try {
29+
schema_data = Everest::load_yaml(api_details_filepath);
30+
} catch (const std::exception& err) {
31+
EVLOG_error << "Error parsing YAML file at " << api_details_filepath << ": " << err.what();
32+
return {};
33+
}
34+
35+
const auto& module_configs = config_service_client->get_module_configs();
36+
37+
for (const auto& [config, _] : module_configs) {
38+
if (schema_data.contains(config.module_type)) {
39+
ApiModuleParameter module_parameters;
40+
module_parameters.api_type = config.module_type;
41+
42+
// No mechanism to retreive the version yet
43+
44+
api_modules.emplace(config.module_id, module_parameters);
45+
}
46+
}
47+
48+
return api_modules;
49+
}
50+
51+
std::string strip_suffix(const std::string& input, const std::string& suffix) {
52+
if (input.size() >= suffix.size() && input.substr(input.size() - suffix.size()) == suffix) {
53+
return input.substr(0, input.size() - suffix.size());
54+
}
55+
}
56+
57+
API_types_ext::ApiDiscoverResponse
58+
module_config_to_ApiDiscoverResponse(const std::map<std::string, ApiModuleParameter>& api_modules) {
59+
API_types_ext::ApiDiscoverResponse response;
60+
61+
for (const auto& [name, parameter] : api_modules) {
62+
API_types_ext::ApiTypeEnum type_as_enum;
63+
json api_type_j = strip_suffix(parameter.api_type, "_API");
64+
if (not ev_API::deserialize(api_type_j.dump(), type_as_enum)) {
65+
EVLOG_error << "Could not deserialize API type for '" << parameter.api_type << "'";
66+
}
67+
API_types_ext::ApiParameter api_params;
68+
api_params.type = type_as_enum;
69+
api_params.module_id = name;
70+
api_params.version = parameter.api_version;
71+
response.apis.emplace_back(api_params);
72+
}
73+
74+
return response;
75+
}
76+
} // namespace
77+
78+
namespace module {
79+
80+
namespace API_generic = API_types::generic;
81+
using ev_API::deserialize;
82+
83+
void EVerest_API::init() {
84+
invoke_init(*p_main);
85+
86+
topics.setup(info.id, "entrypoint", 1);
87+
}
88+
89+
void EVerest_API::ready() {
90+
invoke_ready(*p_main);
91+
92+
auto config_service_client = get_config_service_client();
93+
94+
const auto apis_details_path = info.paths.share / "apis.yaml";
95+
96+
auto api_modules = get_all_ApiModuleParameters(config_service_client.get(), apis_details_path);
97+
98+
api_discovery_response = module_config_to_ApiDiscoverResponse(api_modules);
99+
100+
generate_api_cmd_discover();
101+
102+
// Not calling start on comm_check - we only use the heartbeat functionality here
103+
setup_heartbeat_generator();
104+
}
105+
106+
void EVerest_API::generate_api_cmd_discover() {
107+
subscribe_api_topic("discover", [this](std::string const& data) {
108+
API_generic::RequestReply msg;
109+
if (deserialize(data, msg)) {
110+
mqtt.publish(msg.replyTo, API_types_ext::serialize(api_discovery_response));
111+
return true;
112+
}
113+
return false;
114+
});
115+
}
116+
117+
void EVerest_API::subscribe_api_topic(std::string const& var, ParseAndPublishFtor const& parse_and_publish) {
118+
auto topic = topics.extern_to_everest(var);
119+
mqtt.subscribe(topic, [=](std::string const& data) {
120+
try {
121+
if (not parse_and_publish(data)) {
122+
EVLOG_warning << "Invalid data: Deserialization failed.\n" << topic << "\n" << data;
123+
}
124+
} catch (const std::exception& e) {
125+
EVLOG_warning << "Topic: '" << topic << "' failed with -> " << e.what() << "\n => " << data;
126+
} catch (...) {
127+
EVLOG_warning << "Invalid data: Failed to parse JSON or to get data from it.\n" << topic;
128+
}
129+
});
130+
}
131+
132+
void EVerest_API::setup_heartbeat_generator() {
133+
auto topic = topics.everest_to_extern("heartbeat");
134+
auto action = [this, topic]() {
135+
mqtt.publish(topic, API_generic::serialize(hb_id++));
136+
return true;
137+
};
138+
comm_check.heartbeat(config.cfg_heartbeat_interval_ms, action);
139+
}
140+
141+
} // namespace module
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Pionix GmbH and Contributors to EVerest
3+
#ifndef EVEREST_API_HPP
4+
#define EVEREST_API_HPP
5+
6+
//
7+
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
8+
// template version 2
9+
//
10+
11+
#include "ld-ev.hpp"
12+
13+
// headers for provided interface implementations
14+
#include <generated/interfaces/generic_error/Implementation.hpp>
15+
16+
// headers for required interface implementations
17+
#pragma GCC diagnostic push
18+
#pragma GCC diagnostic ignored "-Wunused-parameter"
19+
#include <generated/interfaces/generic_error/Implementation.hpp> // TODO(CB): Required?
20+
#pragma GCC diagnostic pop
21+
22+
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
23+
// insert your custom include headers here
24+
#include <everest_api_types/entrypoint/API.hpp>
25+
#include <everest_api_types/utilities/CommCheckHandler.hpp>
26+
#include <everest_api_types/utilities/Topics.hpp>
27+
28+
namespace ev_API = everest::lib::API;
29+
namespace API_types = ev_API::V1_0::types;
30+
namespace API_types_ext = API_types::entrypoint;
31+
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
32+
33+
namespace module {
34+
35+
struct Conf {
36+
int cfg_communication_check_to_s;
37+
int cfg_heartbeat_interval_ms;
38+
};
39+
40+
class EVerest_API : public Everest::ModuleBase {
41+
public:
42+
EVerest_API() = delete;
43+
EVerest_API(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider,
44+
std::unique_ptr<generic_errorImplBase> p_main, Conf& config) :
45+
ModuleBase(info),
46+
mqtt(mqtt_provider),
47+
p_main(std::move(p_main)),
48+
config(config),
49+
comm_check("generic/CommunicationFault", "Bridge to implementation connection lost", this->p_main){};
50+
51+
Everest::MqttProvider& mqtt;
52+
const std::shared_ptr<generic_errorImplBase> p_main;
53+
const Conf& config;
54+
55+
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
56+
// insert your public definitions here
57+
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
58+
59+
protected:
60+
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
61+
// insert your protected definitions here
62+
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
63+
64+
private:
65+
friend class LdEverest;
66+
void init();
67+
void ready();
68+
69+
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
70+
// insert your private definitions here
71+
using ParseAndPublishFtor = std::function<bool(std::string const&)>;
72+
void subscribe_api_topic(std::string const& var, ParseAndPublishFtor const& parse_and_publish);
73+
74+
void generate_api_cmd_discover();
75+
76+
void setup_heartbeat_generator();
77+
78+
ev_API::Topics topics;
79+
ev_API::CommCheckHandler<generic_errorImplBase> comm_check;
80+
size_t hb_id{0};
81+
82+
API_types_ext::ApiDiscoverResponse api_discovery_response;
83+
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
84+
};
85+
86+
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
87+
// insert other definitions here
88+
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
89+
90+
} // namespace module
91+
92+
#endif // EVEREST_API_HPP

0 commit comments

Comments
 (0)