RTC Toolkit 6.1.0-pre1
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();
189 ret == ok || ret == ipcq::Error::Closed || ret == ipcq::Error::WouldBlock) {
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_v<typename RecInfoType::Recorder::DisabledFields>) {
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_v<typename RecInfoType::Recorder::TupleType>; 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_v<typename RecInfoType::Recorder::TupleType>; 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: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: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