RTC Toolkit 6.0.0
Loading...
Searching...
No Matches
ipcqDdtForwarder.hpp
Go to the documentation of this file.
1
11
12#ifndef IPCQ_DDT_FORWARDER_HPP
13#define IPCQ_DDT_FORWARDER_HPP
14
16
25
26#include <ipcq/adapter.hpp>
27#include <ipcq/reader.hpp>
28
29#include <numapp/numapolicies.hpp>
30#include <numapp/thread.hpp>
31
32#include <fmt/format.h>
33#include <gsl/span>
34
35#include <atomic>
36#include <memory>
37#include <shared_mutex>
38#include <string_view>
39
40namespace rtctk::ddtServer {
41
42namespace {
43
44// Helper type used to construct the tuple of DDT Publishers
45
46template <typename Tuple>
47struct Wrapper;
48
49template <typename... PublisherType>
50struct Wrapper<std::tuple<PublisherType...>> {
51 using ServiceContainer = rtctk::componentFramework::ServiceContainer;
52
53 static std::tuple<PublisherType...>
54 MakePublishers(const std::string& db_prefix, ServiceContainer& services) {
55 return std::make_tuple(PublisherType(db_prefix, services)...);
56 }
57};
58
59} // namespace
60
68template <typename FwdInfo, typename ReaderType = ipcq::Reader<typename FwdInfo::Topic>>
70public:
71 using Topic = typename FwdInfo::Topic;
79
80 static_assert(FwdInfo::ID.find_first_not_of("abcdefghijklmnopqrstuvwxyz_0123456789") ==
81 std::string_view::npos,
82 "DDT Forwarder ID contains illegal characters!");
83
84 IpcqDdtForwarder(const std::string& comp_id, ServiceContainer& services)
85 : DdtForwarder(comp_id, "IpcqDdtForwarder", std::string(FwdInfo::ID), services)
86 , m_rtr(services.Get<RuntimeRepoIf>())
87 , m_oldb(services.Get<OldbIf>())
88 , m_metrics(services.Get<ComponentMetricsIf>())
89 , m_comp_id(comp_id)
90 , m_oldb_prefix(fmt::format("/forwarders/{}", GetId())) // counters prepend comp name
91 , m_rtr_prefix_static(fmt::format("/{}/static/forwarders/{}", comp_id, GetId()))
92 , m_rtr_prefix_dynamic(fmt::format("/{}/dynamic/forwarders/{}", comp_id, GetId()))
93 , m_publishers(Wrapper<typename FwdInfo::DdtPublisherTupleType>::MakePublishers(
94 fmt::format("{}/forwarders/{}/publishers", comp_id, GetId()), services))
95 , m_requested_state(State::STOPPED) {
96 using namespace rtctk::componentFramework;
97
98 // reader monitoring
99 const InfluxTagMap base_tags = {
100 {"forwarder_id", GetId()},
101 };
102
103 m_samples_forwarded_reg =
104 m_metrics.AddCounter(&m_samples_forwarded,
105 CounterMetricInfo(m_oldb_prefix + "/samples_forwarded",
106 "Number of samples forwarded",
107 base_tags,
108 "samples_forwarded"));
109
110 m_last_sample_id_forwarded_reg =
111 m_metrics.AddCounter(&m_last_sample_id_forwarded,
112 CounterMetricInfo(m_oldb_prefix + "/last_sample_id_forwarded",
113 "Last sample id forwarded",
114 base_tags,
115 "last_sample_id_forwarded"));
116
117 m_freq_estimator = std::make_unique<FrequencyEstimator>(
118 m_metrics, "Estimated frequency of the data forwarder", m_oldb_prefix);
119
120 m_dur_monitor = std::make_unique<DurationMonitor>(
121 m_metrics, "Duration of publishing data to DDT", m_oldb_prefix);
122
123 m_buffer_monitor =
124 std::make_unique<BufferMonitor>(m_metrics, "SHM read buffer occupancy", m_oldb_prefix);
125
126 // reader configuration
127
128 auto queue_name_path = DataPointPath{m_rtr_prefix_static + "/shm_queue_name"};
129 m_queue_name = m_rtr.GetDataPoint<std::string>(queue_name_path);
130
131 auto thread_policies_path = DataPointPath{m_rtr_prefix_static + "/thread_policies"};
132 m_thread_policies = GetNumaPolicies(m_rtr, thread_policies_path);
133
134 auto subsample_factor_path = DataPointPath{m_rtr_prefix_dynamic + "/subsample_factor"};
135 m_subsample_factor = m_rtr.GetDataPoint<int64_t>(subsample_factor_path);
136 }
137
138 ~IpcqDdtForwarder() override {
139 m_requested_state = State::STOPPED;
140 if (m_processing_thread.joinable()) {
141 m_processing_thread.join();
142 }
143 }
144
145 void Start() override {
147 m_requested_state = State::STARTING;
149
150 // we trim the thread name to not cause an exception from numapp
151 m_processing_thread = numapp::MakeThread(GetId().substr(0, 15),
152 m_thread_policies.value_or(numapp::NumaPolicies()),
153 [&]() { Process(); });
154
155 while (true) {
156 using namespace std::chrono_literals;
157 std::this_thread::sleep_for(10ms);
158
159 CheckErrors();
160
161 if (GetState() != State::STARTING) {
162 break;
163 }
164 }
165 }
166
167 void Run() override {
169 m_requested_state = State::RUNNING;
170 }
171
172 void Idle() override {
174 m_requested_state = State::IDLE;
175 }
176
177 void Stop() override {
178 m_requested_state = State::STOPPED;
179 if (m_processing_thread.joinable()) {
180 m_processing_thread.join();
181 }
182 }
183
184 void Recover() override {
185 Stop();
186
187 // clear stored exception
188 {
189 std::scoped_lock lock(m_exception_mutex);
190 m_exception = std::current_exception();
191 }
192
193 // make sure that we get out of error state
195 }
196
197 void Update() override {
198 using namespace rtctk::componentFramework;
199
200 LOG4CPLUS_INFO(m_logger, fmt::format("Updating IpcqDdtForwarder '{}'", GetId()));
201
202 auto subsample_factor_path = DataPointPath{m_rtr_prefix_dynamic + "/subsample_factor"};
203 m_subsample_factor = m_rtr.GetDataPoint<int64_t>(subsample_factor_path);
204
205 std::apply([&](auto&&... pub) { ((pub.Update()), ...); }, m_publishers);
206 }
207
208 void CheckErrors() override {
209 // cppcheck-suppress unreadVariable
210 auto lock = std::shared_lock{m_exception_mutex};
211 if (m_exception) {
212 std::rethrow_exception(m_exception);
213 }
214 }
215
216private:
217 void Process() {
218 using namespace std::chrono_literals;
219 using namespace rtctk::componentFramework;
220
221 try {
222 const std::error_code ok{};
223 std::pair<std::error_code, size_t> result;
224
225 std::vector<Topic> read_buffer;
226 read_buffer.reserve(MAX_SAMPLES_READ);
227
228 m_samples_forwarded.Store(0);
229 m_last_sample_id_forwarded.Store(0);
230
231 ReaderType reader(m_queue_name.c_str());
232
233 size_t reader_capacity = reader.Capacity();
234 size_t to_skip = 0;
235 size_t to_read = 0;
236
238
239 while (m_requested_state != State::STOPPED) {
240 switch (GetState()) {
241 case State::IDLE:
242 if (m_requested_state == State::RUNNING) {
243 if (auto ret = reader.Reset(); ret == ok) {
245 } else {
246 CII_THROW(IpcqError, "Error resetting ipcq Reader");
247 }
248 break;
249 }
250 std::this_thread::sleep_for(1ms);
251 break;
252
253 case State::RUNNING:
254 if (m_requested_state == State::IDLE) {
256 break;
257 }
258 to_skip = 0;
259 to_read = reader.NumAvailable();
260 // This appears to be a false positive. NumAvailable can return 0.
261 // cppcheck-suppress knownConditionTrueFalse
262 if (to_read == 0) { // try to always read at least one
263 to_read = 1;
264 }
265 to_read = std::min(to_read, MAX_SAMPLES_READ);
266
267 if (m_subsample_factor > 1) {
268 m_sampling_counter = m_sampling_counter % m_subsample_factor;
269 if (m_sampling_counter == 0) {
270 to_read = 1;
271 } else {
272 to_skip = std::min(to_read, m_subsample_factor - m_sampling_counter);
273 }
274 }
275
276 if (to_skip == 0) {
277 m_buffer_monitor->Tick(reader.NumAvailable(), reader_capacity);
278 result = reader.Read(ipcq::BackInserter(read_buffer), to_read, 100ms);
279 for (const auto& sample : read_buffer) {
280 m_last_sample_id_forwarded.Store(sample.sample_id);
281 auto t1 = std::chrono::steady_clock::now();
282 ExtractAndPublish(sample);
283 auto t2 = std::chrono::steady_clock::now();
284 m_dur_monitor->Tick(t2 - t1);
285 m_freq_estimator->Tick();
286 m_samples_forwarded++;
287 }
288 read_buffer.clear();
289 } else {
290 result = reader.Skip(to_skip, 100ms);
291 }
292 m_sampling_counter += result.second;
293 // check for error
294 if (not(result.first == ok or result.first == ipcq::Error::Timeout)) {
295 std::string error = "Error reading from ipcq: " + result.first.message();
296 CII_THROW(IpcqError, error);
297 }
298 break;
299
300 default:
301 std::this_thread::sleep_for(1ms);
302 }
303 }
304
306
307 } catch (...) {
308 {
309 std::scoped_lock lock(m_exception_mutex);
310 m_exception = std::current_exception();
311 }
313 }
314 }
315
316 void ExtractAndPublish(const Topic& sample) {
317 auto func = [&](auto& pub) {
318 if (pub.IsEnabled()) {
319 // invoke user function that extracts data from SHM Topic sample
320 auto e = std::remove_reference_t<decltype(pub)>::StreamInfo::Extract(sample);
321 // publish extracted data to DDT
322 pub.Publish(std::get<0>(e),
323 reinterpret_cast<const uint8_t*>(std::get<1>(e).data()),
324 std::get<1>(e).size());
325 }
326 };
327
328 std::apply([&](auto&&... pub) { (func(pub), ...); }, m_publishers);
329 }
330
331 RuntimeRepoIf& m_rtr;
332 OldbIf& m_oldb;
333 ComponentMetricsIf& m_metrics;
334
335 std::string m_comp_id;
336 std::string m_oldb_prefix;
337 std::string m_rtr_prefix_static;
338 std::string m_rtr_prefix_dynamic;
339
340 typename FwdInfo::DdtPublisherTupleType m_publishers;
341
342 std::atomic<State> m_requested_state;
343 std::exception_ptr m_exception = nullptr;
344 std::shared_mutex m_exception_mutex;
345
346 std::thread m_processing_thread;
347
351 std::string m_queue_name;
352
356 std::optional<numapp::NumaPolicies> m_thread_policies;
357
361 int64_t m_subsample_factor = -1;
362
366 uint64_t m_sampling_counter = 0;
367
371 perfc::CounterI64 m_samples_forwarded;
372 perfc::ScopedRegistration m_samples_forwarded_reg;
373
377 perfc::CounterI64 m_last_sample_id_forwarded;
378 perfc::ScopedRegistration m_last_sample_id_forwarded_reg;
379
380 std::unique_ptr<FrequencyEstimator> m_freq_estimator;
381 std::unique_ptr<DurationMonitor> m_dur_monitor;
382 std::unique_ptr<BufferMonitor> m_buffer_monitor;
383
387 inline static constexpr size_t MAX_SAMPLES_READ = 16;
388};
389
390} // namespace rtctk::ddtServer
391
392#endif // IPCQ_DDT_FORWARDER
Header file for Buffer Monitor.
Monitors min, mean and max occupation of a buffer and publishes them to OLDB.
Definition bufferMonitor.hpp:36
Component metrics interface.
Definition componentMetricsIf.hpp:163
Defines auxiliary information associated with each counter registered with ComponentMetricsIf.
Definition componentMetricsIf.hpp:48
This class provides a wrapper for a data point path.
Definition dataPointPath.hpp:76
Monitors min, mean and max duration and publishes them to OLDB.
Definition durationMonitor.hpp:36
Estimates the frequency in which Tick is called and publishes result to OLDB.
Definition frequencyEstimator.hpp:30
Helper class for passing tags in Telegraf.
Definition influxTagMap.hpp:26
This Exception is raised when the ipc queue returns an error that cannot be handled by the Telemetry ...
Definition exceptions.hpp:391
Base interface for all OLDB adapters.
Definition oldbIf.hpp:24
Base interface for all Runtime Configuration Repository adapters.
Definition runtimeRepoIf.hpp:26
Container class that holds services of any type.
Definition serviceContainer.hpp:38
DdtForwarder(const std::string &comp_id, const std::string &fwd_type, const std::string &fwd_id, ServiceContainer &services)
Definition ddtForwarder.hpp:55
void AssertState(const std::set< State > &states)
Definition ddtForwarder.hpp:143
virtual void SetState(State state)
Definition ddtForwarder.hpp:130
State
States a forwarder unit can be in.
Definition ddtForwarder.hpp:53
@ STARTING
Definition ddtForwarder.hpp:53
@ STOPPED
Definition ddtForwarder.hpp:53
@ RUNNING
Definition ddtForwarder.hpp:53
@ IDLE
Definition ddtForwarder.hpp:53
@ ERROR
Definition ddtForwarder.hpp:53
log4cplus::Logger & m_logger
Definition ddtForwarder.hpp:166
State GetState() const
Get the state of the forwarder unit.
Definition ddtForwarder.hpp:90
const std::string & GetId() const
Get identifier of the forwarder unit.
Definition ddtForwarder.hpp:83
rtctk::componentFramework::RuntimeRepoIf RuntimeRepoIf
Definition ipcqDdtForwarder.hpp:73
void Idle() override
Stop publishing DDT streams.
Definition ipcqDdtForwarder.hpp:172
rtctk::componentFramework::ComponentMetricsIf ComponentMetricsIf
Definition ipcqDdtForwarder.hpp:75
void Recover() override
Stop the processing thread of the forwarder unit and clear errors.
Definition ipcqDdtForwarder.hpp:184
rtctk::componentFramework::FrequencyEstimator<> FrequencyEstimator
Definition ipcqDdtForwarder.hpp:76
IpcqDdtForwarder(const std::string &comp_id, ServiceContainer &services)
Definition ipcqDdtForwarder.hpp:84
typename FwdInfo::Topic Topic
Definition ipcqDdtForwarder.hpp:71
void Start() override
Start the processing thread of the forwarder unit.
Definition ipcqDdtForwarder.hpp:145
rtctk::componentFramework::BufferMonitor<> BufferMonitor
Definition ipcqDdtForwarder.hpp:78
rtctk::componentFramework::DurationMonitor<> DurationMonitor
Definition ipcqDdtForwarder.hpp:77
void Stop() override
Stop the processing thread of the forwarder unit.
Definition ipcqDdtForwarder.hpp:177
void Update() override
Reload dynamic configuration of the forwarder unit.
Definition ipcqDdtForwarder.hpp:197
~IpcqDdtForwarder() override
Definition ipcqDdtForwarder.hpp:138
void CheckErrors() override
Check for Errors, will rethrow errors thrown in the forwarder.
Definition ipcqDdtForwarder.hpp:208
rtctk::componentFramework::OldbIf OldbIf
Definition ipcqDdtForwarder.hpp:74
rtctk::componentFramework::ServiceContainer ServiceContainer
Definition ipcqDdtForwarder.hpp:72
void Run() override
Start publishing DDT streams.
Definition ipcqDdtForwarder.hpp:167
Header file for ComponentMetricsIf.
Base class defining common interface for all DDT forwarders.
Header file for Duration Monitor.
Provides macros and utilities for exception handling.
Header file for Frequency Estimator.
std::optional< numapp::NumaPolicies > GetNumaPolicies(RepositoryIf &repo, const DataPointPath &path)
Constructs a NumaPolicies object from the configuration datapoints found under the given datapoint pa...
Definition repositoryIfUtils.cpp:32
Definition dataPointPath.hpp:464
Definition commandReplier.cpp:21
Definition businessLogic.cpp:23
Definition ddsSub.hpp:155
Header file for OldbIf, which defines the API for OldbAdapters.
Provides utilities to simplify use of RepositoryIf.
Header file for RuntimeRepoIf, which defines the API for RuntimeRepoAdapters.