RTC Toolkit 5.1.0
Loading...
Searching...
No Matches
ipcqRecordingUnit.ipp
Go to the documentation of this file.
1
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
20#include <boost/algorithm/string.hpp>
21#include <ipcq/adapter.hpp>
22#include <ipcq/reader.hpp>
23
24#include <numapp/numapolicies.hpp>
25#include <numapp/thread.hpp>
26
28
29template <typename RecInfoType>
30void IpcqRecordingUnit<RecInfoType>::Prepare(const std::filesystem::path& file_path) {
31 SetState(State::PREPARING, State::STOPPED, "tried to prepare a non-stopped ipcqRecordingUnit");
32
33 m_file_path = file_path / (GetId() + ".fits");
34
35 m_stop = false;
36 auto policies = numapp::NumaPolicies();
37 if (m_cpu_affinity.has_value()) {
38 auto cpu_mask =
39 numapp::Cpumask::MakeFromCpuStringAll(std::to_string(m_cpu_affinity.value()).c_str());
40 policies.SetCpuAffinity(numapp::CpuAffinity(cpu_mask));
41 }
42
43 m_process_thread =
44 numapp::MakeThread(m_unit_id.substr(0, 15), policies, [&]() { return Process(); });
45}
46
47template <typename RecInfoType>
49 m_start = true;
50}
51
52template <typename RecInfoType>
53std::vector<std::filesystem::path> IpcqRecordingUnit<RecInfoType>::Stop() {
54 m_stop = true;
55 if (m_process_thread.joinable()) {
56 m_process_thread.join();
57 }
58 std::vector<std::filesystem::path> files;
59 if (m_file_path) {
60 files.push_back(*m_file_path);
61 }
62 m_file_path.reset();
63 SetStopped();
64 return files;
65}
66
67template <typename RecInfoType>
69 if (GetState() == State::RUNNING) {
70 CII_THROW(InvalidStateChange, "tried to update a running IpcqRecordingUnit");
71 }
72
74 DataPointPath(fmt::format(RTR_PATH_SUBSAMPLE_FACTOR, m_comp_id, m_unit_id));
75 m_subsample_factor = m_rtr.GetDataPoint<int64_t>(subsample_factor_path);
76
77 auto disabled_fields = GetDisabled(
78 m_rtr, DataPointPath(fmt::format(RTR_PATH_TELEMETRY_SUBSET, m_comp_id, m_unit_id)));
79 m_output.SetDisabledFields(disabled_fields);
80
82 DataPointPath(fmt::format(RTR_PATH_START_SAMPLE_ID, m_comp_id, m_unit_id));
83 m_start_sample_id = m_rtr.GetDataPoint<int64_t>(start_sample_id_path);
84
86 DataPointPath(fmt::format(RTR_PATH_STOP_AFTER_NUM_SAMPLES, m_comp_id, m_unit_id));
87 m_stop_after_num_samples = m_rtr.GetDataPoint<int64_t>(stop_after_samples_path);
88}
89
90template <typename RecInfoType>
92 using namespace std::chrono_literals;
93
94 try {
95 std::vector<typename RecInfoType::Topic> buffer;
96 buffer.reserve(MAX_SAMPLES_READ);
97
98 const std::error_code ok{};
99 std::pair<std::error_code, size_t> result;
100
101 bool files_open = false;
102 m_samples_written.Store(0);
103 m_last_observed_sample_id.Store(0);
104
105 auto reader = ipcq::Reader<typename RecInfoType::Topic>(m_queue_name.c_str());
106
107 size_t reader_capacity = reader.Capacity();
108
109 if (not SetState(State::IDLE, State::PREPARING)) {
110 try {
111 CII_THROW(InvalidStateChange,
112 "IpcqRecordingUnit not in preparing before starting thread");
113 } catch (...) {
114 SetFailed(std::current_exception());
115 return;
116 }
117 }
118
119 while (m_stop == false) {
120 switch (GetState()) {
121 case State::IDLE: {
122 if (m_start) {
123 if (auto ret = reader.Reset(); ret == ok) {
124 SetState(State::WAITING,
125 State::IDLE,
126 "IpcqRecordingUnit not in IDLE before entering WAITING");
127 } else {
128 CII_THROW(IpcqError, "Error resetting ipcq Reader");
129 }
130 break;
131 }
132 std::this_thread::sleep_for(1ms);
133 break;
134 }
135 case State::WAITING: {
136 result = reader.Read(ipcq::BackInserter(buffer), 1, 100ms);
137 if (not(result.first == ok or result.first == ipcq::Error::Timeout)) {
138 std::string error = "Error reading from ipcq: " + result.first.message();
139 CII_THROW(IpcqError, error);
140 }
141 for (const auto& element : buffer) {
142 m_last_observed_sample_id.Store(element.sample_id);
143 }
144 buffer.clear();
145
146 if ((m_last_observed_sample_id.Load() >= m_start_sample_id - 1) and
147 (not HasLeaders() or (HasLeaders() and HasFirstLeaderStarted()))) {
148 m_sampling_counter = 0;
149 SetState(State::RUNNING,
150 State::WAITING,
151 "Expected to be in WAITING, before going RUNNING");
152 }
153 break;
154 }
155 case State::RUNNING: {
156 if (HasLeaders() and HasLastLeaderFinished()) {
157 SetState(State::FINISHED,
158 State::RUNNING,
159 "Expected to be in WAITING, before going RUNNING");
160 break;
161 }
162
163 size_t to_skip = 0;
164 size_t to_read = reader.NumAvailable();
165 if (to_read == 0) { // try to always read at least one
166 to_read = 1;
167 }
168 to_read = std::min(to_read, MAX_SAMPLES_READ);
169
170 if (m_subsample_factor > 1) {
171 m_sampling_counter = m_sampling_counter % m_subsample_factor;
172 if (m_sampling_counter == 0) {
173 to_read = 1;
174 } else {
175 to_skip = std::min(to_read, m_subsample_factor - m_sampling_counter);
176 }
177 }
178
179 if (to_skip == 0) {
180 m_buffer_monitor->Tick(reader.NumAvailable(), reader_capacity);
181 result = reader.Read(ipcq::BackInserter(buffer), to_read, 100ms);
182 for (const auto& element : buffer) {
183 m_last_observed_sample_id.Store(element.sample_id);
184 auto t1 = std::chrono::steady_clock::now();
185 auto sample = RecInfoType::AsTuple(element);
186 if (not files_open) {
187 // defer opening of files until we get first sample and can set sizes
188 m_output.SetColumnLength(sample);
189 m_output.Open(*m_file_path);
190 files_open = true;
191 }
192 m_output.Write(sample);
193 auto t2 = std::chrono::steady_clock::now();
194 m_dur_monitor->Tick(t2 - t1);
195 m_freq_estimator->Tick();
196 m_samples_written++;
197 if (m_stop_after_num_samples > 0 and
198 m_samples_written.Load() >= m_stop_after_num_samples) {
199 SetState(State::FINISHED,
200 State::RUNNING,
201 "Expected to be in RUNNING, before going FINISHED");
202 break;
203 }
204 }
205 buffer.clear();
206 } else {
207 result = reader.Skip(to_skip, 100ms);
208 }
209 m_sampling_counter += result.second;
210 // check for error
211 if (not(result.first == ok or result.first == ipcq::Error::Timeout)) {
212 std::string error = "Error reading from ipcq: " + result.first.message();
213 CII_THROW(IpcqError, error);
214 }
215 break;
216 }
217 default: {
218 std::this_thread::sleep_for(1ms);
219 }
220 }
221 }
222
223 // cleanup
224 m_output.Close();
225 m_start = false;
226 m_stop = false;
227 ResetLeaderStates();
228 // the actual STOPPED state will be set by the Stop function
229
230 } catch (...) {
231 // try closing the file even if it might fail
232 try {
233 m_output.Close();
234 } catch (const FitsDataRecorderFitsError& e) {
235 // ignore this error we will keep the original exception
236 }
237 m_start = false;
238 m_stop = false;
239 SetFailed(std::current_exception());
240 ResetLeaderStates();
241 }
242}
243
244template <typename RecInfoType>
245typename RecInfoType::Recorder::DisabledFields
247 typename RecInfoType::Recorder::DisabledFields result{};
248
249 if (not rtr.DataPointExists(path)) {
250 return result; // no fields disabled
251 }
252 auto fields = rtr.GetDataPoint<std::vector<std::string>>(path);
253
254 if (fields.empty()) {
255 return result; // no fields disabled
256 }
257
258 if (fields.size() > std::tuple_size<typename RecInfoType::Recorder::DisabledFields>::value) {
259 CII_THROW(InvalidSetting, "Invalid disabled fields setting. Too many fields");
260 }
261
262 // set them all to disabled an rean
264 result[i] = true;
265 }
266
267 for (const auto& name : fields) {
268 bool found = false;
270 i++) {
271 if (name == RecInfoType::COLUMNS[i].name) {
272 result[i] = false;
273 found = true;
274 break;
275 }
276 }
277 if (!found) {
279 std::string{"Invalid disabled fields setting: Could not find field '" + name +
280 "'"});
281 }
282 }
283 return result;
284}
285
286} // namespace rtctk::componentFramework
This class provides a wrapper for a data point path.
Definition dataPointPath.hpp:74
This Exception is raised when a invalid setting was used in the runtime repo.
Definition exceptions.hpp:338
This Exception is raised when the state change requested is invalid.
Definition exceptions.hpp:324
Recording Unit that can record from shared memory queue.
Definition ipcqRecordingUnit.hpp:52
std::vector< std::filesystem::path > Stop() override
Stop the recording thread and wait for it's termination.
Definition ipcqRecordingUnit.ipp:53
void Update() override
Update settings from RuntimeRepo.
Definition ipcqRecordingUnit.ipp:68
void Prepare(const std::filesystem::path &file_path) override
Prepare the recording thread and start recording.
Definition ipcqRecordingUnit.ipp:30
static RecInfoType::Recorder::DisabledFields GetDisabled(RepositoryIf &rtr, const DataPointPath &sub_path)
get disabled fields from a DataPoint in the runtime repo.
Definition ipcqRecordingUnit.ipp:246
void Start() override
Start the recording.
Definition ipcqRecordingUnit.ipp:48
Abstract interface providing basic read and write facilities to a repository.
Definition repositoryIf.hpp:104
FitsRecorder allows to write ColumnData to into fits files in a specified directory.
Definition commandReplier.cpp:22
elt::mal::future< std::string > InjectReqRepEvent(StateMachineEngine &engine)
Definition malEventInjector.hpp:23