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