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