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