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