RTC Toolkit 6.1.0-pre1
Loading...
Searching...
No Matches
shmPub.hpp
Go to the documentation of this file.
1
11#ifndef RTCTK_STANDALONETOOLS_SHMPUB_HPP
12#define RTCTK_STANDALONETOOLS_SHMPUB_HPP
13
14// arg parsing
15#include <boost/program_options.hpp>
16
17// cfitsio
18#include <cfitsio/fitsio.h>
19
20// include the numapp for threading
21#include <numapp/mempolicy.hpp>
22#include <numapp/numapolicies.hpp>
23#include <numapp/thread.hpp>
24
25// include the ipcq for writer
26#include <ipcq/writer.hpp>
27
28#include <chrono>
29#include <ctime>
30#include <iostream>
31#include <vector>
32
33namespace rtctk::standaloneTools {
34
36static bool g_stop = false;
37
43void SignalHandler(int signal) {
44 std::cout << "\nSignal to exit received\n";
45 g_stop = true;
46}
47
75template <class TopicType, class WriterType = ipcq::Writer<TopicType>>
76class ShmPub {
77public:
78 ShmPub(int argc, char* argv[]) {
79 using namespace boost::program_options;
80
81 try {
82 options_description desc("Allowed options");
83 // clang-format off
84 desc.add_options()
85 ("help,h", "produce help message")
86 ("fits-file,f",
87 value<std::string>(&m_filename)->default_value(""),
88 "fits input file: if not provided the app will generate data")
89 ("queue-name,q",
90 value<std::string>(&m_queue_name)->default_value("default_shm_queue"),
91 "shm queue name")
92 ("queue-size,s",
93 value<size_t>(&m_queue_size)->default_value(1000),
94 "size of the queue")
95 ("sample-delay,d",
96 value<int>(&m_sample_delay)->default_value(10),
97 "inter-sample delay in ms")
98 ("initial-delay,w",
99 value<int>(&m_initial_delay)->default_value(0),
100 "initial delay in ms")
101 ("numa-node,n", value<int>(&m_numa), "numa node for shm queue")
102 ("print-every,p",
103 value<int>(&m_print_every)->default_value(0),
104 "when to print to screen the number of sample written")
105 ("gen-frames,g",
106 value<int>(&m_gen_frames)->default_value(100),
107 "Number of frames to generate")
108 ("sample-id-increment,i",
109 value<unsigned>(&m_sample_id_increment)->default_value(1),
110 "sample_id increment")
111 ("repeat-mode,r",
112 bool_switch(&m_repeat_mode),
113 "Repeat output when all samples are written");
114 // clang-format on
115
116 variables_map vm;
117 store(command_line_parser(argc, argv).options(desc).run(), vm);
118 notify(vm);
119
120 if (vm.count("help")) {
121 m_help_only = true;
122 std::cout << desc << "\n";
123 } else {
124 m_help_only = false;
125 std::cout << "fits-file: " << m_filename << "\n";
126 std::cout << "queue-name: " << m_queue_name << "\n";
127 std::cout << "queue-size: " << m_queue_size << "\n";
128 std::cout << "sample-delay: " << m_sample_delay << "\n";
129 std::cout << "initital-delay: " << m_initial_delay << "\n";
130 if (vm.count("numa-node")) {
131 std::cout << "numa-node: " << m_numa << "\n";
132 }
133 std::cout << "print-every: " << m_print_every << "\n";
134 std::cout << "gen-frames: " << m_gen_frames << "\n";
135 std::cout << "sample-id-increment: " << m_sample_id_increment << "\n";
136 std::cout << "repeat-mode: " << m_repeat_mode << "\n";
137
138 if (vm.count("numa-node")) {
139 m_writer =
140 std::make_unique<WriterType>(m_queue_name.c_str(),
141 m_queue_size,
142 numapp::MemPolicy::MakeBindNode(m_numa));
143 } else {
144 m_writer = std::make_unique<WriterType>(m_queue_name.c_str(), m_queue_size);
145 }
146 }
147 } catch (const std::exception& e) {
148 std::cerr << "Exception:" << e.what() << "\n";
149 }
150 }
151
152 virtual ~ShmPub() = default;
153
164 int Run() {
165 if (m_help_only) {
166 return 0;
167 }
168
169 int ret_val = 0;
170
171 try {
172 signal(SIGINT, SignalHandler);
173
174 std::vector<TopicType> data;
175
176 // checks if filename has been indicated if it has loads data by calling the user
177 // overloaded function ReadFits if not provided calls the user overloaded function
178 // GenData
179 if (not m_filename.empty()) {
180 std::cout << "Reading data from FITS file: " << m_filename << "\n";
181 data = ReadFits(m_filename);
182 } else {
183 std::cout << "Generating data\n";
184 data = GenData(m_gen_frames);
185 }
186
187 // check to make sure m_data is populated
188 if (data.empty()) {
189 throw std::runtime_error("Data vector is not populated so will exit");
190 }
191
192 WaitFor(std::chrono::milliseconds{m_initial_delay});
193
194 // calls main loop
195 std::cout << "Writing data to shared memory queue\n";
196 WriteToShm(data);
197
198 } catch (const std::exception& e) {
199 std::cout << e.what() << "\n";
200 ret_val = -1;
201 }
202
203 // Close queue to signal and give readers time detach from queue
204 m_writer->Close();
205#ifndef UNIT_TEST
206 std::this_thread::sleep_for(std::chrono::seconds(2));
207#endif
208
209 return ret_val;
210 }
211
226 virtual std::vector<TopicType> ReadFits(std::string filename) = 0;
227
242 virtual std::vector<TopicType> GenData(int num_frames) = 0;
243
257 virtual void AdjustSample(TopicType& sample) const {};
258
259protected:
261 const std::string& GetQueueName() const {
262 return m_queue_name;
263 }
264
266 size_t GetQueueSize() const {
267 return m_queue_size;
268 }
269
271 int GetSampleDelay() const {
272 return m_sample_delay;
273 }
274
276 int GetNuma() const {
277 return m_numa;
278 }
279
281 bool GetRepeatMode() const {
282 return m_repeat_mode;
283 }
284
285private:
286
292 void WaitFor(std::chrono::milliseconds delay) {
293 using namespace std::chrono;
294
295 if(g_stop or delay == 0ms) {
296 return;
297 }
298
299 auto t_start = steady_clock::now();
300 while(!g_stop and ((steady_clock::now() - t_start) < delay)) {
301 std::this_thread::sleep_for(1ms);
302 };
303 }
304
316 void WriteToShm(std::vector<TopicType>& data) {
317 using namespace std::chrono;
318
319 size_t n_written = 0;
320 auto t_sent = steady_clock::now();
321 auto t_last = t_sent;
322 do {
323 for (auto& sample : data) {
324 if (g_stop) {
325 return;
326 }
327 AdjustSample(sample);
328 if (m_repeat_mode) {
329 sample.sample_id = n_written * m_sample_id_increment;
330 }
331 t_sent = steady_clock::now();
332 std::error_code err = m_writer->Write(sample, ipcq::Notify::All);
333 if (err) {
334 throw std::runtime_error("Error writing to shm: " + err.message());
335 }
336
337 n_written++;
338 if (n_written && m_print_every && (n_written % m_print_every == 0)) {
339 auto dur = duration_cast<milliseconds>(t_sent - t_last).count();
340 std::cout << "Samples written: " << n_written << "\n";
341 std::cout << "Total time to write " << m_print_every << " : " << dur << " ms\n";
342 std::cout << "Average frame time: " << static_cast<float>(dur) / m_print_every
343 << " ms\n";
344 t_last = t_sent;
345 }
346 while (duration_cast<milliseconds>(steady_clock::now() - t_sent).count() <
347 m_sample_delay) {
348 }
349 }
350
351 } while (m_repeat_mode);
352 }
353
354 std::string m_queue_name; //< Queue name to be used by the writer
355 size_t m_queue_size{0}; //< number of position in shm queue
356 std::string m_filename; //< path to fits file being read
357 int m_sample_delay{0}; //< delay between samples being writter (ms)
358 int m_initial_delay{0}; //< delay before starting to publish samples (ms)
359 int m_numa{0}; //< which numa node to provide writer
360
361 int m_print_every{0}; //< print status every N samples
362 int m_gen_frames{0}; //< if generation data how many sample to be generated
363 unsigned m_sample_id_increment{1}; //< sample id increment
364 bool m_repeat_mode{false}; //< data will loop forever with an ever increasing sample_id
365 bool m_help_only{false}; //< if help only will not enter writing loop
366
367 std::unique_ptr<WriterType> m_writer; //< the ipcq writer
368};
369
384template <class T>
385std::vector<T>
386ReadColumnFromFits(fitsfile* fptr, const std::string& name, long nrows, bool output = false) {
387 int status = 0;
388 int col, typecode, anynul;
389 long repeat, width;
390 float nulval;
391
392 // The const_cast is a workaround for a buggy cfitsio API. The argument is never actually
393 // modified and should have been declared const.
394 fits_get_colnum(fptr, CASESEN, const_cast<char*>(name.c_str()), &col, &status);
395 if (status) {
396 fits_report_error(stderr, status);
397 throw std::runtime_error("Error getting column: " + name);
398 }
399
400 fits_get_coltype(fptr, col, &typecode, &repeat, &width, &status);
401 if (status) {
402 fits_report_error(stderr, status);
403 throw std::runtime_error("Error getting coltype of:" + name);
404 }
405
406 if (output) {
407 std::cout << "name: " << name << "\n";
408 std::cout << "col: " << col << "\n";
409 std::cout << "typecode: " << typecode << "\n";
410 std::cout << "repeat: " << repeat << "\n";
411 std::cout << "width: " << width << "\n";
412 }
413
414 // load in required data
415 std::vector<T> data;
416 data.resize(repeat * nrows); // we are assuming the vector to be matrix with row major.
417 T* d = data.data();
418 fits_read_col(fptr, typecode, col, 1, 1, repeat * nrows, &nulval, d, &anynul, &status);
419 if (status) {
420 fits_report_error(stderr, status);
421 throw std::runtime_error("Error reading column: " + name);
422 }
423 return data;
424}
425
426} // namespace rtctk::standaloneTools
427
428#endif // RTCTK_STANDALONETOOLS_SHMPUB_HPP
int GetSampleDelay() const
Returns the sample delay argument set on the command line.
Definition shmPub.hpp:271
int GetNuma() const
Returns the NUMA node argument set on the command line.
Definition shmPub.hpp:276
size_t GetQueueSize() const
Returns the shared memory queue size argument set on the command line.
Definition shmPub.hpp:266
int Run()
Entry point for running the ShmPub.
Definition shmPub.hpp:164
ShmPub(int argc, char *argv[])
Definition shmPub.hpp:78
virtual std::vector< TopicType > GenData(int num_frames)=0
Generates data to be circulated.
virtual void AdjustSample(TopicType &sample) const
Adjust the contents of a data sample just before publishing to shared memory.
Definition shmPub.hpp:257
virtual std::vector< TopicType > ReadFits(std::string filename)=0
Reads in data from a FITS file.
bool GetRepeatMode() const
Returns the repeat mode flag set on the command line.
Definition shmPub.hpp:281
const std::string & GetQueueName() const
Returns the shared memory queue name argument set on the command line.
Definition shmPub.hpp:261
Definition genDdsPublisher.hpp:19
std::vector< T > ReadColumnFromFits(fitsfile *fptr, const std::string &name, long nrows, bool output=false)
helper function for reading columns of fits table
Definition shmPub.hpp:386
void SignalHandler(int signal)
Handles basic signals to allow simple exiting from a ShmPub process.
Definition shmPub.hpp:43