RTC Toolkit 6.0.0
Loading...
Searching...
No Matches
rtcComponentMain.hpp
Go to the documentation of this file.
1
11
12#ifndef RTCTK_COMPONENTFRAMEWORK_RTCCOMPONENTMAIN_HPP
13#define RTCTK_COMPONENTFRAMEWORK_RTCCOMPONENTMAIN_HPP
14
15#include <exception>
31
32#include <mal/Cii.hpp>
33
34#include <fmt/format.h>
35
36#include <boost/core/demangle.hpp>
37
38#include <boost/stacktrace.hpp>
39#include <iostream>
40#include <memory>
41#include <string>
42
53
55
72template <class BL, class BLF>
73void RunAsRtcComponent(const Args& args, BLF factory) {
74 try {
75 auto& logger = GetLogger("rtctk");
76
77 auto component_name = args.GetComponentName();
78 LOG4CPLUS_INFO(logger, "RtcComponent '" << component_name << "' started.");
79 using boost::core::demangle;
80 LOG4CPLUS_DEBUG(logger, "BusinessLogic '" << demangle(typeid(BL).name()) << "'");
81
82 {
83 ServiceContainer services;
84
85 // Service Discovery
86 auto svc_disc_endpoint = args.GetServiceDiscEndpoint();
87 services.Add<ServiceDiscovery>(
88 std::make_unique<ServiceDiscovery>(svc_disc_endpoint, component_name));
89 ServiceDiscovery& svc_disc = services.Get<ServiceDiscovery>();
90
91 // Runtime repository
92 auto rtr_endpoint = svc_disc.GetRuntimeRepoEndpoint();
93 auto rtr_adapter = std::shared_ptr(RuntimeRepoIf::CreateAdapter(rtr_endpoint));
94 services.Add<RuntimeRepoIf>(rtr_adapter);
95
96 // OLDB
97 auto oldb_endpoint = svc_disc.GetOldbEndpoint();
98 auto oldb_adapter = std::shared_ptr(OldbIf::CreateAdapter(oldb_endpoint));
99 services.Add<OldbIf>(oldb_adapter);
100
101 // Telegraf
102 auto telegraf_endpoint = svc_disc.GetTelegrafEndpoint();
103 auto telegraf_adapter =
104 std::make_shared<TelegrafAdapter>(telegraf_endpoint, component_name);
105 services.Add<TelegrafAdapter>(telegraf_adapter);
106
107 // Telemetry service
108 auto component_metrics = std::make_shared<ComponentMetrics>(
109 *oldb_adapter,
110 DataPointPath(fmt::format("/{}/metrics", component_name)),
111 *rtr_adapter,
112 DataPointPath(fmt::format("/{}/dynamic/metrics", component_name)),
113 GetLogger("rtctk.metrics"),
114 telegraf_adapter.get());
115 services.Add<ComponentMetricsIf>(component_metrics);
116
117 // Event Service
118 auto event_service = std::make_shared<MalDdsEventService>();
119 services.Add<EventServiceIf>(event_service);
120
121 // Alert service and standard observers
122 auto alert_service = std::make_shared<AlertService>();
123 alert_service->AddObserver(std::make_unique<AlertLogger>());
124 alert_service->AddObserver(
125 std::make_unique<AlertOldbPublisher>(component_name, *oldb_adapter));
126 alert_service->AddObserver(
127 std::make_unique<AlertEventPublisher>(component_name, *event_service));
128 alert_service->AddObserver(
129 std::make_unique<AlertTelegrafPublisher>(component_name, *telegraf_adapter));
130 services.Add<AlertServiceIf>(alert_service);
131 // for RR endpoint we do not want to be checked if the process is there or not (as we
132 // are just starting it up)
133 auto rr_endpoint =
134 svc_disc.GetReqRepEndpoint("", ServiceRegistryIf::COMPONENT_TYPE, false);
135 CommandReplier replier(rr_endpoint);
136
137 // ... and sam for PubSub endpoint we do not want to be checked if the process is there
138 // or not (as we are just starting it up)
139 auto ps_endpoint =
140 svc_disc.GetPubSubEndpoint("", ServiceRegistryIf::COMPONENT_TYPE, false);
141 StatePublisher publisher(ps_endpoint, component_name);
142
143 StateMachineEngine engine;
144
145 engine.RegisterStateChangeHandler([&](const std::string& state) {
146 try {
147 LOG4CPLUS_DEBUG(logger, "Entering State: " << state);
148 publisher.PublishState(state);
149
150 // save to OLDB
151 OldbIf& oldb = services.Get<OldbIf>();
152 DataPointPath dp_name = DataPointPath("/" + component_name + "/state");
153 if (not oldb.DataPointExists(dp_name)) {
154 oldb.CreateDataPoint<std::string>(dp_name);
155 }
156 oldb.SetDataPoint(dp_name, state);
157
158 // save to InfluxDB
159 TelegrafAdapter& telegraf = services.Get<TelegrafAdapter>();
160 auto points =
161 ParseStateToInfluxPoints(component_name, state, telegraf.BaseTags());
162 telegraf.SendPoints(points);
163 } catch (...) {
164 LOG4CPLUS_ERROR(logger,
166 RtctkException("StateChangeHandler failed"))));
167 }
168 });
169
170 static_assert(std::is_base_of_v<typename BL::ComponentType::BizLogicIf, BL>,
171 "BusinessLogic must implement BusinessLogicInterface");
172
173 static_assert(
174 std::is_invocable_v<BLF, const std::string&, ServiceContainer&>,
175 "Factory must be invocable with 'const std::string&' and 'ServiceContainer&'");
176
177 static_assert(
178 std::is_same_v<std::invoke_result_t<BLF, const std::string&, ServiceContainer&>,
179 std::unique_ptr<BL>>,
180 "Factory must return type 'std::unique_ptr<BusinessLogic>'");
181
182 // Invoking user factory here -> catch and rethrow RtctkException
183 std::unique_ptr<BL> biz_logic;
184 try {
185 biz_logic = invoke(factory, component_name, services);
186 if (!biz_logic) {
187 CII_THROW(RtctkException,
188 "BusinessLogic factory did not return a valid object");
189 }
190 } catch (const std::exception& ex) {
191 CII_THROW_WITH_NESTED(RtctkException, ex, "BusinessLogic factory failed");
192 }
193
194 typename BL::ComponentType::InputStage input_stage(replier, engine);
195
196 typename BL::ComponentType::OutputStage output_stage(engine, *biz_logic);
197
198 typename BL::ComponentType::ModelBuilder model_builder(engine);
199
200 if (auto emf = args.GetModelExportFile(); emf) {
201 model_builder.ExportModel(emf->path().to_string());
202 }
203
204 model_builder.RegisterModel();
205 input_stage.Start();
206 component_metrics->StartMonitoring();
207 engine.Work();
208 }
209
210 LOG4CPLUS_INFO(logger, "RtcComponent '" << component_name << "' terminated.");
211
212 } catch (const std::exception& ex) {
213 CII_THROW_WITH_NESTED(RtctkException, ex, "RunAsRtcComponent() failed");
214 }
215}
216
228template <class BL>
229void RunAsRtcComponent(const Args& args) {
230 static_assert(
231 std::is_constructible_v<BL, const std::string&, ServiceContainer&>,
232 "BusinessLogic must be constructible with 'const std::string&' and 'ServiceContainer&'");
233
234 RunAsRtcComponent<BL>(args, std::make_unique<BL, const std::string&, ServiceContainer&>);
235}
236
237} // namespace rtctk::componentFramework
238
239#ifndef RTCTK_NOMAIN
241
248int Main(int argc, char* argv[]) {
249 LogInitializer initializer;
250 Args args{argc, argv};
251
252 try {
253 args.Parse();
254 } catch (const CLI::ParseError& e) {
255 return args.PrintHelpOrErrMsg(e);
256 } catch (const std::exception& e) {
257 fmt::print(stderr, "RtcComponentMain failed during argument parsing: {}\n", e.what());
258 return EXIT_FAILURE;
259 } catch (...) {
260 fmt::print(stderr, "{}", NestedExceptionPrinter(std::current_exception()).Str());
261 return EXIT_FAILURE;
262 }
263
264 try {
265 auto name = args.GetComponentName();
266 auto log_props_file = args.GetLogPropsFile();
267
268 if (log_props_file.has_value()) {
269 LogConfigure(name, *log_props_file);
270 } else {
271 auto debug_mode = args.GetDebugMode();
272 auto level = debug_mode ? log4cplus::DEBUG_LOG_LEVEL : log4cplus::INFO_LOG_LEVEL;
273 LogConfigure(name, level);
274 }
275 } catch (const std::exception& e) {
276 fmt::print(stderr, "Failed to initialise log system: {}\n", e.what());
277 return EXIT_FAILURE;
278 }
279
280 try {
281 // Call user implementation of RtcComponentMain
282 ::RtcComponentMain(args);
283
284 return EXIT_SUCCESS;
285
286 } catch (const std::exception& e) {
287 LOG4CPLUS_FATAL(GetLogger("rtctk"), NestedExceptionPrinter(e));
288 } catch (...) {
289 LOG4CPLUS_FATAL(GetLogger("rtctk"), NestedExceptionPrinter(std::current_exception()));
290 }
291 return EXIT_FAILURE;
292}
293
294} // namespace rtctk::componentFramework
295
299int main(int argc, char* argv[]) {
300 std::set_terminate([]() {
301 using namespace rtctk::componentFramework;
302
303 std::exception_ptr eptr{std::current_exception()};
304 if (eptr) {
305 LOG4CPLUS_FATAL(GetLogger("rtctk"),
306 "Unhandled Exception caught: "
307 << NestedExceptionPrinter(eptr) << "\n\nBacktrace:\n"
308 << boost::to_string(boost::stacktrace::stacktrace()));
309 } else {
310 LOG4CPLUS_FATAL(GetLogger("rtctk"),
311 "Exiting without exception.\nBacktrace:\n"
312 << boost::to_string(boost::stacktrace::stacktrace()));
313 }
314 std::abort();
315 }); // std::set_terminate
316
317 try {
318 return rtctk::componentFramework::Main(argc, argv);
319 } catch (...) {
320 // Note: Use minimal formatting to avoid further exception leaks.
321 // This handler is only here to avoid std::terminate due to exception-leaks.
322 constexpr std::string_view msg =
323 "Caught exception leaked from rtctk::componentFramework::Main\n";
324 std::cerr.write(msg.data(), msg.size());
325 }
326}
327#endif // RTCTK_NOMAIN
328
329#endif // RTCTK_COMPONENTFRAMEWORK_RTCCOMPONENTMAIN_HPP
Declares AlertService.
Alert Service interface.
Definition alertServiceIf.hpp:138
Class used to parse default command line arguments.
Definition rtcComponentArgs.hpp:32
std::optional< elt::mal::Uri > GetModelExportFile() const
Get name of optionally provided state machine model export file.
Definition rtcComponentArgs.cpp:39
const std::string & GetComponentName() const
Get component name or identifier.
Definition rtcComponentArgs.cpp:19
elt::mal::Uri GetServiceDiscEndpoint() const
Get service discovery endpoint.
Definition rtcComponentArgs.cpp:23
Class that handles reception of commands using MAL.
Definition commandReplier.hpp:29
Component metrics interface.
Definition componentMetricsIf.hpp:163
This class provides a wrapper for a data point path.
Definition dataPointPath.hpp:76
Interface class for providing pub/sub facilities for JSON events.
Definition eventServiceIf.hpp:28
RAII class to clean-up logging without leaking memory.
Definition logger.hpp:27
Adapter object intended to be used in contexts without direct access to the output-stream object.
Definition exceptions.hpp:168
Base interface for all OLDB adapters.
Definition oldbIf.hpp:24
static std::unique_ptr< OldbIf > CreateAdapter(const elt::mal::Uri &uri)
Factory method used to create the appropriate OLDB adapter depending on the URI scheme.
Definition oldbIf.cpp:17
The RtctkException class is the base class for all Rtctk exceptions.
Definition exceptions.hpp:220
Base interface for all Runtime Configuration Repository adapters.
Definition runtimeRepoIf.hpp:26
static std::unique_ptr< RuntimeRepoIf > CreateAdapter(const elt::mal::Uri &uri)
Factory method used to create the appropriate Runtime Configuration Repository adapter depending on t...
Definition runtimeRepoIf.cpp:18
Container class that holds services of any type.
Definition serviceContainer.hpp:38
Class that implements a very basic service discovery mechanism.
Definition serviceDiscovery.hpp:32
ServiceEndpoint GetRuntimeRepoEndpoint()
Get the Runtime Repository Endpoint from the ServiceDiscovery.
Definition serviceDiscovery.cpp:58
static const std::string COMPONENT_TYPE
the string for component type (default)
Definition serviceRegistryIf.hpp:59
Definition stateMachineEngine.hpp:34
void RegisterStateChangeHandler(StateMethod on_statechange)
Register state changed handler.
Definition stateMachineEngine.cpp:141
void Work()
Runs the event loop of the state machine.
Definition stateMachineEngine.cpp:154
Class used to publish state-changed-topic using MAL.
Definition statePublisher.hpp:35
void PublishState(const std::string &state)
Definition statePublisher.cpp:40
Implementation of the Telegraf Adapter.
Definition telegrafAdapter.hpp:93
std::map< std::string, std::string > BaseTags()
Constructs and returns a map of base tags for InfluxDB.
Definition telegrafAdapter.cpp:77
void SendPoints(const std::vector< InfluxPoint > &points)
Sends vector of points to Telegraf using UDP and Influx line protocol.
Definition telegrafAdapter.cpp:81
Receive commands via MAL.
Declares ComponentMetrics.
log4cplus::Logger & GetLogger(const std::string &name="app")
Get handle to a specific logger.
Definition logger.cpp:191
Provides macros and utilities for exception handling.
void RtcComponentMain(const rtctk::componentFramework::Args &args)
Main entry point for user code, this method must be implemented by component developers.
Definition main.cpp:42
void RunAsRtcComponent(const Args &args, BLF factory)
RTC Component runner function, needed to run custom BusinessLogic as RTC Component.
Definition rtcComponentMain.hpp:73
auto WrapWithNested(E &&exception) noexcept(std::is_nothrow_constructible_v< detail::UnspecifiedNested< typename std::decay_t< E > >, E && >)
Constructs an unspecified exception that derives from both the provided object and std::nested_except...
Definition exceptions.hpp:96
log4cplus::Logger & GetLogger(const std::string &name="app")
Get handle to a specific logger.
Definition logger.cpp:191
void LogConfigure(const std::string &app_name, const std::string &props_file_name)
Performs custom logging configuration for any application.
Definition logger.cpp:69
Logging Support Library based on log4cplus.
Implementation of the event service.
Definition commandReplier.cpp:21
std::vector< InfluxPoint > ParseStateToInfluxPoints(const std::string &component_name, const std::string &states_str, const std::map< std::string, std::string > &base_tags)
Definition telegrafAdapter.cpp:212
int Main(int argc, char *argv[])
Main function implementation.
Definition rtcComponentMain.hpp:248
Header file for OldbIf, which defines the API for OldbAdapters.
Provides argument parsing functionality of an RTC Component.
int main(int argc, char *argv[])
Main function, this is private to the RTC Tk and shall NOT be used by component developers.
Definition rtcComponentMain.hpp:299
Header file for RuntimeRepoIf, which defines the API for RuntimeRepoAdapters.
A container that can hold any type of service.
Class that implements a very basic service discover mechanism.
Class that implements the service registry interface.
Wrapper around the SCXML State Machine Engine.
Publishes the stdif state topic via MAL.
Header file needed to instantiate an TelegrafAdapter.