RTC Toolkit 6.0.0-pre2
Loading...
Searching...
No Matches
ipcqRecordingUnit.ipp
Go to the documentation of this file.
1
12
13// Note this is a template implementation file and should not be included directly.
14// The typical header protection macro is not added to avoid it showing up in Doxygen API
15// documentation.
16#pragma once
17
19
21
22#include <boost/algorithm/string.hpp>
23#include <ipcq/adapter.hpp>
24#include <ipcq/error.hpp>
25#include <ipcq/reader.hpp>
26
27#include <numapp/numapolicies.hpp>
28#include <numapp/thread.hpp>
29
31
32template <typename RecInfoType>
34 const std::string& unit_id,
35 ServiceContainer& services)
36 : RecordingUnit(comp_id, unit_id, "IPCQ", services)
37 , m_queue_name(m_rtr.GetDataPoint<std::string>(
38 DataPointPath(fmt::format(RTR_PATH_QUEUE_NAME, comp_id, unit_id))))
39 , m_output(RecInfoType::COLUMNS) {
40 auto queue_name_path = DataPointPath(fmt::format(OLDB_PATH_QUEUE_NAME, comp_id, unit_id));
41 m_oldb.CreateDataPoint<std::string>(queue_name_path);
42 m_oldb.SetDataPoint<std::string>(queue_name_path, m_queue_name);
43
44 auto cpu_affinity_path = DataPointPath(fmt::format(RTR_PATH_CPU_AFFINITY, comp_id, unit_id));
45 if (m_rtr.DataPointExists(cpu_affinity_path)) {
46 m_cpu_affinity = m_rtr.GetDataPoint<int32_t>(cpu_affinity_path);
47 }
48
49 const InfluxTagMap base_tags = {{"unit_id", unit_id}};
50
51 m_samples_written_reg =
52 m_metrics.AddCounter(&m_samples_written,
53 CounterMetricInfo(unit_id + "/samples_written",
54 "Number of samples successfully written to file",
55 base_tags,
56 "samples_written"));
57
58 m_last_observed_sample_id_reg =
59 m_metrics.AddCounter(&m_last_observed_sample_id,
60 CounterMetricInfo(unit_id + "/last_sample_id_written",
61 "Last sample id successfully written to file",
62 base_tags,
63 "last_sample_id_written"));
64
65 m_freq_estimator = std::make_unique<FrequencyEstimator<>>(
66 m_metrics, "Estimated frequency of the data writer", unit_id);
67
68 m_dur_monitor =
69 std::make_unique<DurationMonitor<>>(m_metrics, "Duration of writing data to file", unit_id);
70
71 m_buffer_monitor =
72 std::make_unique<BufferMonitor<>>(m_metrics, "SHM read buffer occupancy", unit_id);
73
74 LoadDynamicConfig();
75}
76
77template <typename RecInfoType>
79 m_stop = true;
80 if (m_process_thread.joinable()) {
81 m_process_thread.join();
82 }
83}
84
85template <typename RecInfoType>
86void IpcqRecordingUnit<RecInfoType>::Prepare(const std::filesystem::path& file_path) {
87 if (not IsEnabled()) {
88 return;
89 }
90
91 SetState(State::PREPARING, State::STOPPED, "tried to prepare a non-stopped ipcqRecordingUnit");
92
93 m_file_path = file_path / (GetId() + ".fits");
94
95 m_stop = false;
96 auto policies = numapp::NumaPolicies();
97 if (m_cpu_affinity.has_value()) {
98 auto cpu_mask =
99 numapp::Cpumask::MakeFromCpuStringAll(std::to_string(m_cpu_affinity.value()).c_str());
100 policies.SetCpuAffinity(numapp::CpuAffinity(cpu_mask));
101 }
102
103 m_process_thread =
104 numapp::MakeThread(m_unit_id.substr(0, 15), policies, [&]() { return Process(); });
105}
106
107template <typename RecInfoType>
109 m_start = true;
110}
111
112template <typename RecInfoType>
113std::vector<std::filesystem::path> IpcqRecordingUnit<RecInfoType>::Stop() {
114 m_stop = true;
115 if (m_process_thread.joinable()) {
116 m_process_thread.join();
117 }
118 std::vector<std::filesystem::path> files;
119 if (m_file_path) {
120 files.push_back(*m_file_path);
121 }
122 m_file_path.reset();
123 SetStopped();
124 return files;
125}
126
127template <typename RecInfoType>
129 if (GetState() == State::RUNNING) {
130 CII_THROW(InvalidStateChange, "tried to update a running IpcqRecordingUnit");
131 }
132
134 LoadDynamicConfig();
135}
136
137template <typename RecInfoType>
138void IpcqRecordingUnit<RecInfoType>::LoadDynamicConfig() {
139 auto subsample_factor_path =
140 DataPointPath(fmt::format(RTR_PATH_SUBSAMPLE_FACTOR, m_comp_id, m_unit_id));
141 m_subsample_factor = m_rtr.GetDataPoint<int64_t>(subsample_factor_path);
142
143 auto disabled_fields = GetDisabled(
144 m_rtr, DataPointPath(fmt::format(RTR_PATH_TELEMETRY_SUBSET, m_comp_id, m_unit_id)));
145 m_output.SetDisabledFields(disabled_fields);
146
147 auto start_sample_id_path =
148 DataPointPath(fmt::format(RTR_PATH_START_SAMPLE_ID, m_comp_id, m_unit_id));
149 m_start_sample_id = m_rtr.GetDataPoint<int64_t>(start_sample_id_path);
150
151 auto stop_after_samples_path =
152 DataPointPath(fmt::format(RTR_PATH_STOP_AFTER_NUM_SAMPLES, m_comp_id, m_unit_id));
153 m_stop_after_num_samples = m_rtr.GetDataPoint<int64_t>(stop_after_samples_path);
154}
155
156template <typename RecInfoType>
157void IpcqRecordingUnit<RecInfoType>::Process() {
158 using namespace std::chrono_literals;
159
160 try {
161 std::vector<typename RecInfoType::Topic> buffer;
162 buffer.reserve(MAX_SAMPLES_READ);
163
164 const std::error_code ok{};
165 std::pair<std::error_code, size_t> result;
166
167 bool files_open = false;
168 m_samples_written.Store(0);
169 m_last_observed_sample_id.Store(0);
170
171 auto reader = ipcq::Reader<typename RecInfoType::Topic>(m_queue_name.c_str());
172
173 size_t reader_capacity = reader.Capacity();
174
175 if (not SetState(State::IDLE, State::PREPARING)) {
176 try {
177 CII_THROW(InvalidStateChange,
178 "IpcqRecordingUnit not in preparing before starting thread");
179 } catch (...) {
180 SetFailed(std::current_exception());
181 return;
182 }
183 }
184
185 while (m_stop == false) {
186 switch (GetState()) {
187 case State::IDLE: {
188 if (m_start) {
189 if (auto ret = reader.Reset(); ret == ok || ret == ipcq::Error::Closed) {
190 SetState(State::WAITING,
191 State::IDLE,
192 "IpcqRecordingUnit not in IDLE before entering WAITING");
193 } else {
194 CII_THROW(IpcqError,
195 fmt::format("Error resetting ipcq Reader: {}", ret.message()));
196 }
197 break;
198 }
199 std::this_thread::sleep_for(1ms);
200 break;
201 }
202 case State::WAITING: {
203 result = reader.Read(ipcq::BackInserter(buffer), 1, 100ms);
204 if (not(result.first == ok or result.first == ipcq::Error::Timeout)) {
205 std::string error = "Error reading from ipcq: " + result.first.message();
206 CII_THROW(IpcqError, error);
207 }
208 for (const auto& element : buffer) {
209 m_last_observed_sample_id.Store(element.sample_id);
210 }
211 buffer.clear();
212
213 if ((m_last_observed_sample_id.Load() >= m_start_sample_id - 1) and
214 (not HasLeaders() or (HasLeaders() and HasFirstLeaderStarted()))) {
215 m_sampling_counter = 0;
216 SetState(State::RUNNING,
217 State::WAITING,
218 "Expected to be in WAITING, before going RUNNING");
219 }
220 break;
221 }
222 case State::RUNNING: {
223 if (HasLeaders() and HasLastLeaderFinished()) {
224 SetState(State::FINISHED,
225 State::RUNNING,
226 "Expected to be in RUNNING, before going FINISHED");
227 break;
228 }
229
230 size_t to_skip = 0;
231 size_t to_read = reader.NumAvailable();
232 if (to_read == 0) { // try to always read at least one
233 to_read = 1;
234 }
235 to_read = std::min(to_read, MAX_SAMPLES_READ);
236
237 if (m_subsample_factor > 1) {
238 m_sampling_counter = m_sampling_counter % m_subsample_factor;
239 if (m_sampling_counter == 0) {
240 to_read = 1;
241 } else {
242 to_skip = std::min(to_read, m_subsample_factor - m_sampling_counter);
243 }
244 }
245
246 if (to_skip == 0) {
247 m_buffer_monitor->Tick(reader.NumAvailable(), reader_capacity);
248 result = reader.Read(ipcq::BackInserter(buffer), to_read, 100ms);
249 for (const auto& element : buffer) {
250 m_last_observed_sample_id.Store(element.sample_id);
251 auto t1 = std::chrono::steady_clock::now();
252 auto sample = RecInfoType::AsTuple(element);
253 if (not files_open) {
254 // defer opening of files until we get first sample and can set sizes
255 m_output.SetColumnLength(sample);
256 m_output.Open(*m_file_path);
257 files_open = true;
258 }
259 m_output.Write(sample);
260 auto t2 = std::chrono::steady_clock::now();
261 m_dur_monitor->Tick(t2 - t1);
262 m_freq_estimator->Tick();
263 m_samples_written++;
264 if (m_stop_after_num_samples > 0 and
265 m_samples_written.Load() >= m_stop_after_num_samples) {
266 SetState(State::FINISHED,
267 State::RUNNING,
268 "Expected to be in RUNNING, before going FINISHED");
269 break;
270 }
271 }
272 buffer.clear();
273 } else {
274 result = reader.Skip(to_skip, 100ms);
275 }
276 m_sampling_counter += result.second;
277 // check for error
278 if (not(result.first == ok or result.first == ipcq::Error::Timeout)) {
279 std::string error = "Error reading from ipcq: " + result.first.message();
280 CII_THROW(IpcqError, error);
281 }
282 break;
283 }
284 default: {
285 std::this_thread::sleep_for(1ms);
286 }
287 }
288 }
289
290 // cleanup
291 m_output.Close();
292 m_start = false;
293 m_stop = false;
294 ResetLeaderStates();
295 // the actual STOPPED state will be set by the Stop function
296
297 } catch (...) {
298 // try closing the file even if it might fail
299 try {
300 m_output.Close();
301 } catch (const FitsDataRecorderFitsError& e) {
302 // ignore this error we will keep the original exception
303 }
304 m_start = false;
305 m_stop = false;
306 SetFailed(std::current_exception());
307 ResetLeaderStates();
308 }
309}
310
311template <typename RecInfoType>
312typename RecInfoType::Recorder::DisabledFields
314 typename RecInfoType::Recorder::DisabledFields result{};
315
316 if (not rtr.DataPointExists(path)) {
317 return result; // no fields disabled
318 }
319 auto fields = rtr.GetDataPoint<std::vector<std::string>>(path);
320
321 if (fields.empty()) {
322 return result; // no fields disabled
323 }
324
325 if (fields.size() > std::tuple_size<typename RecInfoType::Recorder::DisabledFields>::value) {
326 CII_THROW(InvalidSetting, "Invalid disabled fields setting. Too many fields");
327 }
328
329 // set them all to disabled an rean
330 for (size_t i = 0; i < std::tuple_size<typename RecInfoType::Recorder::TupleType>::value; i++) {
331 result[i] = true;
332 }
333
334 for (const auto& name : fields) {
335 bool found = false;
336 for (size_t i = 0; i < std::tuple_size<typename RecInfoType::Recorder::TupleType>::value;
337 i++) {
338 if (name == RecInfoType::COLUMNS[i].name) {
339 result[i] = false;
340 found = true;
341 break;
342 }
343 }
344 if (!found) {
345 CII_THROW(InvalidSetting,
346 std::string{"Invalid disabled fields setting: Could not find field '" + name +
347 "'"});
348 }
349 }
350 return result;
351}
352
353} // namespace rtctk::componentFramework
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
Helper class for passing tags in Telegraf.
Definition influxTagMap.hpp:27
This Exception is raised when a invalid setting was used in the runtime repo.
Definition exceptions.hpp:340
This Exception is raised when the state change requested is invalid.
Definition exceptions.hpp:326
This Exception is raised when the ipc queue returns an error that cannot be handled by the Telemetry ...
Definition exceptions.hpp:384
std::vector< std::filesystem::path > Stop() override
Stop the recording thread and wait for it's termination.
Definition ipcqRecordingUnit.ipp:113
void Update() override
Update settings from RuntimeRepo.
Definition ipcqRecordingUnit.ipp:128
void Prepare(const std::filesystem::path &file_path) override
Prepare the recording thread and start recording.
Definition ipcqRecordingUnit.ipp:86
static RecInfoType::Recorder::DisabledFields GetDisabled(RepositoryIf &rtr, const DataPointPath &sub_path)
get disabled fields from a DataPoint in the runtime repo.
Definition ipcqRecordingUnit.ipp:313
IpcqRecordingUnit(const std::string &comp_id, const std::string &unit_id, ServiceContainer &services)
Create a new Ipcq Recorder reading from a given queue and outputting to the given output stage.
Definition ipcqRecordingUnit.ipp:33
void Start() override
Start the recording.
Definition ipcqRecordingUnit.ipp:108
~IpcqRecordingUnit() override
Destructor.
Definition ipcqRecordingUnit.ipp:78
OldbIf & m_oldb
Definition recordingUnit.hpp:178
RecordingUnit(const std::string &comp_id, const std::string &unit_id, const std::string &unit_type, ServiceContainer &services)
Create a new RecordingIngestion.
Definition recordingUnit.cpp:19
virtual void Update()
Update dynamic settings.
Definition recordingUnit.cpp:178
std::string GetId()
Get the unit_it of this RecordingUnit.
Definition recordingUnit.cpp:128
std::string m_unit_id
Definition recordingUnit.hpp:176
ComponentMetricsIf & m_metrics
Definition recordingUnit.hpp:179
RuntimeRepoIf & m_rtr
Definition recordingUnit.hpp:177
void SetStopped()
Set the Unit state to STOPPED independent of the current State.
Definition recordingUnit.cpp:157
@ STOPPED
Definition recordingUnit.hpp:53
@ RUNNING
Definition recordingUnit.hpp:53
@ PREPARING
Definition recordingUnit.hpp:53
State GetState()
Get the current state of the Recording Unit.
Definition recordingUnit.cpp:162
bool SetState(State state, State precondition)
Sets the new state, only goes to new state, if expected state matches.
Definition recordingUnit.cpp:132
std::optional< std::filesystem::path > m_file_path
Definition recordingUnit.hpp:180
bool IsEnabled()
Checks whether the Recording Unit is enabled.
Definition recordingUnit.cpp:166
Abstract interface providing basic read and write facilities to a repository.
Definition repositoryIf.hpp:51
T GetDataPoint(const DataPointPath &path) const
Fetches a datapoint from the repository.
Definition repositoryIf.ipp:1843
bool DataPointExists(const DataPointPath &path) const
Checks for the existence of a datapoint in the repository.
Definition repositoryIf.cpp:766
Container class that holds services of any type.
Definition serviceContainer.hpp:39
FitsRecorder allows to write ColumnData to into fits files in a specified directory.
Recording Unit that can record from shared memory queue.
Definition commandReplier.cpp:22
Definition ddsSub.hpp:156