RTC Toolkit 6.0.0-pre2
Loading...
Searching...
No Matches
shmPub.hpp
Go to the documentation of this file.
1
12#ifndef RTCTK_STANDALONETOOLS_SHMPUB_HPP
13#define RTCTK_STANDALONETOOLS_SHMPUB_HPP
14
15// arg parsing
16#include <boost/program_options.hpp>
17
18// cfitsio
19#include <cfitsio/fitsio.h>
20
21// include the numapp for threading
22#include <numapp/mempolicy.hpp>
23#include <numapp/numapolicies.hpp>
24#include <numapp/thread.hpp>
25
26// include the ipcq for writer
27#include <ipcq/writer.hpp>
28
29#include <chrono>
30#include <ctime>
31#include <iostream>
32#include <vector>
33
34namespace rtctk::standaloneTools {
35
37static bool g_stop = false;
38
44void SignalHandler(int signal) {
45 std::cout << "\nSignal to exit received\n";
46 g_stop = true;
47}
48
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 ("numa-node,n", value<int>(&m_numa), "numa node for shm queue")
99 ("print-every,p",
100 value<int>(&m_print_every)->default_value(0),
101 "when to print to screen the number of sample written")
102 ("gen-frames,g",
103 value<int>(&m_gen_frames)->default_value(100),
104 "Number of frames to generate")
105 ("sample-id-increment,i",
106 value<unsigned>(&m_sample_id_increment)->default_value(1),
107 "sample_id increment")
108 ("repeat-mode,r",
109 bool_switch(&m_repeat_mode),
110 "Repeat output when all samples are written");
111 // clang-format on
112
113 variables_map vm;
114 store(command_line_parser(argc, argv).options(desc).run(), vm);
115 notify(vm);
116
117 if (vm.count("help")) {
118 m_help_only = true;
119 std::cout << desc << "\n";
120 } else {
121 m_help_only = false;
122 std::cout << "fits-file: " << m_filename << "\n";
123 std::cout << "queue-name: " << m_queue_name << "\n";
124 std::cout << "queue-size: " << m_queue_size << "\n";
125 std::cout << "sample-delay: " << m_sample_delay << "\n";
126 if (vm.count("numa-node")) {
127 std::cout << "numa-node: " << m_numa << "\n";
128 }
129 std::cout << "print-every: " << m_print_every << "\n";
130 std::cout << "gen-frames: " << m_gen_frames << "\n";
131 std::cout << "sample-id-increment: " << m_sample_id_increment << "\n";
132 std::cout << "repeat-mode: " << m_repeat_mode << "\n";
133
134 if (vm.count("numa-node")) {
135 m_writer =
136 std::make_unique<WriterType>(m_queue_name.c_str(),
137 m_queue_size,
138 numapp::MemPolicy::MakeBindNode(m_numa));
139 } else {
140 m_writer = std::make_unique<WriterType>(m_queue_name.c_str(), m_queue_size);
141 }
142 }
143 } catch (const std::exception& e) {
144 std::cerr << "Exception:" << e.what() << "\n";
145 }
146 }
147
148 virtual ~ShmPub() = default;
149
160 int Run() {
161 if (m_help_only) {
162 return 0;
163 }
164
165 int ret_val = 0;
166
167 try {
168 signal(SIGINT, SignalHandler);
169
170 std::vector<TopicType> data;
171
172 // checks if filename has been indicated if it has loads data by calling the user
173 // overloaded function ReadFits if not provided calls the user overloaded function
174 // GenData
175 if (not m_filename.empty()) {
176 std::cout << "Reading data from FITS file: " << m_filename << "\n";
177 data = ReadFits(m_filename);
178 } else {
179 std::cout << "Generating data\n";
180 data = GenData(m_gen_frames);
181 }
182
183 // check to make sure m_data is populated
184 if (data.empty()) {
185 throw std::runtime_error("Data vector is not populated so will exit");
186 }
187
188 // calls main loop
189 std::cout << "Writing data to shared memory queue\n";
190 WriteToShm(data);
191
192 } catch (const std::exception& e) {
193 std::cout << e.what() << "\n";
194 ret_val = -1;
195 }
196
197 // Close queue to signal and give readers time detach from queue
198 m_writer->Close();
199#ifndef UNIT_TEST
200 std::this_thread::sleep_for(std::chrono::seconds(2));
201#endif
202
203 return ret_val;
204 }
205
220 virtual std::vector<TopicType> ReadFits(std::string filename) = 0;
221
236 virtual std::vector<TopicType> GenData(int num_frames) = 0;
237
251 virtual void AdjustSample(TopicType& sample) const {};
252
253protected:
255 std::string GetQueueName() const {
256 return m_queue_name;
257 }
258
260 size_t GetQueueSize() const {
261 return m_queue_size;
262 }
263
265 int GetSampleDelay() const {
266 return m_sample_delay;
267 }
268
270 int GetNuma() const {
271 return m_numa;
272 }
273
275 bool GetRepeatMode() const {
276 return m_repeat_mode;
277 }
278
279private:
291 void WriteToShm(std::vector<TopicType>& data) {
292 using namespace std::chrono;
293
294 size_t n_written = 0;
295 auto t_sent = steady_clock::now();
296 auto t_last = t_sent;
297 do {
298 for (auto& sample : data) {
299 if (g_stop) {
300 return;
301 }
302 AdjustSample(sample);
303 if (m_repeat_mode) {
304 sample.sample_id = n_written * m_sample_id_increment;
305 }
306 t_sent = steady_clock::now();
307 std::error_code err = m_writer->Write(sample, ipcq::Notify::All);
308 if (err) {
309 throw std::runtime_error("Error writing to shm: " + err.message());
310 }
311
312 n_written++;
313 if (n_written && m_print_every && (n_written % m_print_every == 0)) {
314 auto dur = duration_cast<milliseconds>(t_sent - t_last).count();
315 std::cout << "Samples written: " << n_written << "\n";
316 std::cout << "Total time to write " << m_print_every << " : " << dur << " ms\n";
317 std::cout << "Average frame time: " << static_cast<float>(dur) / m_print_every
318 << " ms\n";
319 t_last = t_sent;
320 }
321 while (duration_cast<milliseconds>(steady_clock::now() - t_sent).count() <
322 m_sample_delay) {
323 }
324 }
325
326 } while (m_repeat_mode);
327 }
328
329 std::string m_queue_name; //< Queue name to be used by the writer
330 size_t m_queue_size{0}; //< number of position in shm queue
331 std::string m_filename; //< path to fits file being read
332 int m_sample_delay{0}; //< delay between samples being writter (ms)
333 int m_numa{0}; //< which numa node to provide writer
334
335 int m_print_every{0}; //< print status every N samples
336 int m_gen_frames{0}; //< if generation data how many sample to be generated
337 unsigned m_sample_id_increment{1}; //< sample id increment
338 bool m_repeat_mode{false}; //< data will loop forever with an ever increasing sample_id
339 bool m_help_only{false}; //< if help only will not enter writing loop
340
341 std::unique_ptr<WriterType> m_writer; //< the ipcq writer
342};
343
358template <class T>
359std::vector<T>
360ReadColumnFromFits(fitsfile* fptr, const std::string& name, long nrows, bool output = false) {
361 int status = 0;
362 int col, typecode, anynul;
363 long repeat, width;
364 float nulval;
365
366 // The const_cast is a workaround for a buggy cfitsio API. The argument is never actually
367 // modified and should have been declared const.
368 fits_get_colnum(fptr, CASESEN, const_cast<char*>(name.c_str()), &col, &status);
369 if (status) {
370 fits_report_error(stderr, status);
371 throw std::runtime_error("Error getting column: " + name);
372 }
373
374 fits_get_coltype(fptr, col, &typecode, &repeat, &width, &status);
375 if (status) {
376 fits_report_error(stderr, status);
377 throw std::runtime_error("Error getting coltype of:" + name);
378 }
379
380 if (output) {
381 std::cout << "name: " << name << "\n";
382 std::cout << "col: " << col << "\n";
383 std::cout << "typecode: " << typecode << "\n";
384 std::cout << "repeat: " << repeat << "\n";
385 std::cout << "width: " << width << "\n";
386 }
387
388 // load in required data
389 std::vector<T> data;
390 data.resize(repeat * nrows); // we are assuming the vector to be matrix with row major.
391 T* d = data.data();
392 fits_read_col(fptr, typecode, col, 1, 1, repeat * nrows, &nulval, d, &anynul, &status);
393 if (status) {
394 fits_report_error(stderr, status);
395 throw std::runtime_error("Error reading column: " + name);
396 }
397 return data;
398}
399
400} // namespace rtctk::standaloneTools
401
402#endif // RTCTK_STANDALONETOOLS_SHMPUB_HPP
int GetSampleDelay() const
Returns the sample delay argument set on the command line.
Definition shmPub.hpp:265
int GetNuma() const
Returns the NUMA node argument set on the command line.
Definition shmPub.hpp:270
size_t GetQueueSize() const
Returns the shared memory queue size argument set on the command line.
Definition shmPub.hpp:260
int Run()
Entry point for running the ShmPub.
Definition shmPub.hpp:160
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:251
virtual std::vector< TopicType > ReadFits(std::string filename)=0
Reads in data from a FITS file.
std::string GetQueueName() const
Returns the shared memory queue name argument set on the command line.
Definition shmPub.hpp:255
bool GetRepeatMode() const
Returns the repeat mode flag set on the command line.
Definition shmPub.hpp:275
Definition genDdsPublisher.hpp:20
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:360
void SignalHandler(int signal)
Handles basic signals to allow simple exiting from a ShmPub process.
Definition shmPub.hpp:44