API
xrif2shmim.hpp
Go to the documentation of this file.
1 /** \file xrif2shmim.hpp
2  * \brief The xrif2shmim class declaration and definition.
3  *
4  * \ingroup xrif2hmim_files
5  */
6 
7 #ifndef xrif2shmim_hpp
8 #define xrif2shmim_hpp
9 
10 #include <ImageStreamIO/ImageStruct.h>
11 #include <ImageStreamIO/ImageStreamIO.h>
12 
13 #include <xrif/xrif.h>
14 
15 #include <mx/ioutils/fileUtils.hpp>
16 #include <mx/improc/eigenCube.hpp>
17 #include <mx/ioutils/fits/fitsFile.hpp>
18 
19 #include <mx/sys/timeUtils.hpp>
20 
21 #include "../../libMagAOX/libMagAOX.hpp"
22 
23 /// Sleep for a specified period in microseconds.
24 /** \todo add timeutils to libMagAOX
25  */
26 inline void microsleep( unsigned usec /**< [in] the number of microseconds to sleep. */ )
27 {
28  std::this_thread::sleep_for( std::chrono::microseconds( usec ) );
29 }
30 
31 /** \defgroup xrif2shmim xrif2shmim: xrif-archive Streamer
32  * \brief Stream images from an xrif archive to shared memory.
33  *
34  * <a href="../handbook/utils/xrif2shmim.html">Utility Documentation</a>
35  *
36  * \ingroup utils
37  *
38  */
39 
40 /** \defgroup xrif2hmim_files xrif2shmim Files
41  * \ingroup xrif2shmim
42  */
43 
44 bool g_timeToDie = false;
45 
46 void sigTermHandler( int signum, siginfo_t *siginf, void *ucont )
47 {
48  // Suppress those warnings . . .
49  static_cast<void>( signum );
50  static_cast<void>( siginf );
51  static_cast<void>( ucont );
52 
53  std::cerr << "\n"; // clear out the ^C char
54 
55  g_timeToDie = true;
56 }
57 
58 /// A utility to stream MagaO-X images from xrif compressed archives to an ImageStreamIO stream.
59 /**
60  * \todo finish md doc for xrif2shmim
61  *
62  * \ingroup xrif2shmim
63  */
64 class xrif2shmim : public mx::app::application
65 {
66  protected:
67  /** \name Configurable Parameters
68  * @{
69  */
70 
71  std::string m_dir; ///< The directory to search for files. Can be empty if pull path given in files. If files is
72  ///< empty, all archives in dir will be used.
73 
74  std::vector<std::string>
75  m_files; ///< List of files to use. If dir is not empty, it will be pre-pended to each name.
76 
77  size_t m_numFrames{
78  0 }; ///< The number of frames to store in memory. This defines how many different images will be streamed. If
79  ///< 0 (the default), all frames found using dir and files are loaded and stored.
80 
81  bool m_earliest{ false }; ///< If true, then the earliest numFrames in the archive are used. By default (if not
82  ///< set) the latest numFrames are used.
83 
84  std::string m_shmimName{
85  "xrif2shmim" }; ///< The name of the shared memory buffer to stream to. Default is "xrif2shmim".
86 
87  uint32_t m_circBuffLength{ 1 }; ///< The length of the shared memory circular buffer. Default is 1.
88 
89  double m_fps{ 10 }; ///< The rate, in frames per second, at which to stream images. Default is 10 fps.
90 
91  ///@}
92 
93  xrif_t m_xrif{ nullptr };
94 
95  /** \name Image Data
96  * @{
97  */
98 
99  uint32_t m_width{ 0 }; ///< The width of the image.
100  uint32_t m_height{ 0 }; ///< The height of the image.
101 
102  mx::improc::eigenCube<uint16_t> m_frames;
103 
104  uint8_t m_dataType; ///< The ImageStreamIO type code.
105 
106  size_t m_typeSize{ 0 }; ///< The size of the type, in bytes. Result of sizeof.
107 
108  IMAGE m_imageStream; ///< The ImageStreamIO shared memory buffer.
109 
110  ///@}
111 
112  public:
113  ~xrif2shmim();
114 
115  virtual void setupConfig();
116 
117  virtual void loadConfig();
118 
119  virtual int execute();
120 };
121 
123 {
124  if( m_xrif )
125  {
126  xrif_delete( m_xrif );
127  }
128 }
129 
131 {
132  config.add( "dir",
133  "d",
134  "dir",
135  argType::Required,
136  "",
137  "dir",
138  false,
139  "string",
140  "The directory to search for files. Can be empty if pull path given in files." );
141  config.add( "files",
142  "f",
143  "files",
144  argType::Required,
145  "",
146  "files",
147  false,
148  "vector<string>",
149  "List of files to use. If dir is not empty, it will be pre-pended to each name." );
150  config.add( "numFrames",
151  "N",
152  "numFrames",
153  argType::Required,
154  "",
155  "numFrames",
156  false,
157  "int",
158  "The number of frames to store in memory. This defines how many different images will be streamed. "
159  "If 0 (the default), all frames found using dir and files are loaded and stored." );
160  config.add( "earliest",
161  "e",
162  "earliest",
163  argType::True,
164  "",
165  "earliest",
166  false,
167  "bool",
168  "If set or true, then the earliest numFrames in the archive are used. By default (if not set) the "
169  "latest numFrames are used." );
170  config.add( "shmimName",
171  "n",
172  "shmimName",
173  argType::Required,
174  "",
175  "shmimName",
176  false,
177  "string",
178  "The name of the shared memory buffer to stream to. Default is \"xrif2shmim\"" );
179  config.add( "circBuffLength",
180  "L",
181  "circBuffLength",
182  argType::Required,
183  "",
184  "circBuffLength",
185  false,
186  "int",
187  "The length of the shared memory circular buffer. Default is 1." );
188 
189  config.add( "fps",
190  "F",
191  "fps",
192  argType::Required,
193  "",
194  "fps",
195  false,
196  "float",
197  "The rate, in frames per second, at which to stream images. Default is 10 fps." );
198 }
199 
201 {
202  config( m_dir, "dir" );
203  config( m_files, "files" );
204  config( m_numFrames, "numFrames" );
205  config( m_earliest, "earliest" );
206  config( m_shmimName, "shmimName" );
207  config( m_circBuffLength, "circBuffLength" );
208  config( m_fps, "fps" );
209 }
210 
212 {
213  // Install signal handling
214  struct sigaction act;
215  sigset_t set;
216 
217  act.sa_sigaction = sigTermHandler;
218  act.sa_flags = SA_SIGINFO;
219  sigemptyset( &set );
220  act.sa_mask = set;
221 
222  errno = 0;
223  if( sigaction( SIGTERM, &act, 0 ) < 0 )
224  {
225  std::cerr << " (" << invokedName << "): error setting SIGTERM handler: " << strerror( errno ) << "\n";
226  return -1;
227  }
228 
229  errno = 0;
230  if( sigaction( SIGQUIT, &act, 0 ) < 0 )
231  {
232  std::cerr << " (" << invokedName << "): error setting SIGQUIT handler: " << strerror( errno ) << "\n";
233  return -1;
234  }
235 
236  errno = 0;
237  if( sigaction( SIGINT, &act, 0 ) < 0 )
238  {
239  std::cerr << " (" << invokedName << "): error setting SIGINT handler: " << strerror( errno ) << "\n";
240  return -1;
241  }
242 
243  // Figure out which files to use
244  if( m_files.size() == 0 )
245  {
246  if( m_dir == "" )
247  {
248  m_dir = "./";
249  }
250 
251  m_files = mx::ioutils::getFileNames( m_dir, "", "", ".xrif" );
252  }
253  else
254  {
255  if( m_dir != "" )
256  {
257  if( m_dir[m_dir.size() - 1] != '/' )
258  m_dir += '/';
259  }
260 
261  for( size_t n = 0; n < m_files.size(); ++n )
262  {
263  m_files[n] = m_dir + m_files[n];
264  }
265  }
266 
267  if( m_files.size() == 0 )
268  {
269  std::cerr << " (" << invokedName << "): No files found.\n";
270  return -1;
271  }
272 
273  xrif_error_t rv;
274  rv = xrif_new( &m_xrif );
275 
276  if( rv < 0 )
277  {
278  std::cerr << " (" << invokedName << "): Error allocating xrif.\n";
279  return -1;
280  }
281 
282  long st = 0;
283  long ed = m_files.size();
284  int stp = 1;
285 
286  if( m_numFrames != 0 && !m_earliest )
287  {
288  st = m_files.size() - 1;
289  ed = -1;
290  stp = -1;
291  }
292 
293  char header[XRIF_HEADER_SIZE];
294 
295  size_t nframes = 0;
296 
297  // First get number of frames.
298  for( long n = st; n != ed; n += stp )
299  {
300  errno = 0;
301  FILE *fp_xrif = fopen( m_files[n].c_str(), "rb" );
302 
303  if( !fp_xrif )
304  {
305  mx::errno_report( "xrif2shmim", errno, __FILE__, __LINE__, "null ptr from fopen for " + m_files[n] );
306  return -1;
307  }
308 
309  if( ferror( fp_xrif ) || errno != 0 )
310  {
311  mx::errno_report( "xrif2shmim", errno, __FILE__, __LINE__, "error from fopen for " + m_files[n] );
312  return -1;
313  }
314 
315  size_t nr = fread( header, 1, XRIF_HEADER_SIZE, fp_xrif );
316 
317  fclose( fp_xrif );
318  if( nr != XRIF_HEADER_SIZE )
319  {
320  std::cerr << " (" << invokedName << "): Error reading header of " << m_files[n] << "\n";
321  return -1;
322  }
323 
324  uint32_t header_size;
325  xrif_read_header( m_xrif, &header_size, header );
326 
327  if( n == st )
328  {
329  m_width = m_xrif->width;
330  m_height = m_xrif->height;
331  m_dataType = m_xrif->type_code;
332  }
333  else
334  {
335  if( m_xrif->width != m_width )
336  {
337  std::cerr << " (" << invokedName << "): width mis-match in " << m_files[n] << "\n";
338  return -1;
339  }
340  if( m_xrif->height != m_height )
341  {
342  std::cerr << " (" << invokedName << "): height mis-match in " << m_files[n] << "\n";
343  return -1;
344  }
345  if( m_xrif->type_code != m_dataType )
346  {
347  std::cerr << " (" << invokedName << "): data type mismatch in " << m_files[n] << "\n";
348  }
349  }
350 
351  if( m_xrif->depth != 1 )
352  {
353  std::cerr << " (" << invokedName << "): Cubes detected in " << m_files[n] << "\n";
354  return -1;
355  }
356 
357  if( m_dataType != XRIF_TYPECODE_INT16 )
358  {
359  std::cerr << " (" << invokedName << "): Only 16-bit signed integers (short) supported" << "\n";
360  return -1;
361  }
362 
363  nframes += m_xrif->frames;
364 
365  if( nframes >= m_numFrames && m_numFrames > 0 )
366  {
367  ed = n + stp;
368  break;
369  }
370  }
371 
372  if( g_timeToDie != false )
373  {
374  std::cerr << " (" << invokedName << "): exiting.\n";
375  return -1;
376  }
377 
378  // Now record the actual number of frames
379  if( m_numFrames == 0 || nframes < m_numFrames )
380  {
381  m_numFrames = nframes;
382  }
383 
384  std::cerr << " (" << invokedName << "): Reading " << m_numFrames << " frames in " << ( ed - st ) * stp << " file";
385  if( ( ed - st ) * stp > 1 )
386  {
387  std::cerr << "s"; // make it plural
388  }
389  std::cerr << "\n";
390 
391  // Allocate the storage
392  m_typeSize = xrif_typesize( m_dataType );
393 
394  m_frames.resize( m_width, m_height, m_numFrames );
395 
396  // Determine the order in which frames are copied
397  int findex = 0;
398  int fed = m_frames.planes();
399  if( stp == -1 )
400  {
401  findex = m_frames.planes() - 1;
402  fed = -1;
403  }
404 
405  // Now de-compress and load the frames
406  // Only decompressing the number of files needed, and only copying the number of frames needed
407  for( long n = st; n != ed; n += stp )
408  {
409  if( g_timeToDie == true )
410  {
411  break; // check before going on
412  }
413 
414  errno = 0;
415  FILE *fp_xrif = fopen( m_files[n].c_str(), "rb" );
416 
417  if( !fp_xrif )
418  {
419  mx::errno_report( "xrif2shmim", errno, __FILE__, __LINE__, "null ptr from fopen for " + m_files[n] );
420  return -1;
421  }
422 
423  if( ferror( fp_xrif ) || errno != 0 )
424  {
425  mx::errno_report( "xrif2shmim", errno, __FILE__, __LINE__, "error from fopen for " + m_files[n] );
426  return -1;
427  }
428 
429  size_t nr = fread( header, 1, XRIF_HEADER_SIZE, fp_xrif );
430 
431  if( nr != XRIF_HEADER_SIZE )
432  {
433  std::cerr << " (" << invokedName << "): Error reading header of " << m_files[n] << "\n";
434  fclose( fp_xrif );
435  return -1;
436  }
437 
438  uint32_t header_size;
439  xrif_read_header( m_xrif, &header_size, header );
440 
441  xrif_allocate_raw( m_xrif );
442 
443  xrif_allocate_reordered( m_xrif );
444 
445  nr = fread( m_xrif->raw_buffer, 1, m_xrif->compressed_size, fp_xrif );
446 
447  fclose( fp_xrif );
448 
449  if( g_timeToDie == true )
450  {
451  break; // check after the long read.
452  }
453 
454  if( nr != m_xrif->compressed_size )
455  {
456  std::cerr << " (" << invokedName << "): Error reading data from " << m_files[n] << "\n";
457  return -1;
458  }
459 
460  xrif_decode( m_xrif );
461 
462  if( g_timeToDie == true )
463  {
464  break; // check after the decompress.
465  }
466 
467  mx::improc::eigenCube<uint16_t> tmpc(
468  (uint16_t *)m_xrif->raw_buffer, m_xrif->width, m_xrif->height, m_xrif->frames );
469 
470  // Determine the order in which frames in tmpc are read
471  long pst = 0;
472  long ped = tmpc.planes();
473  if( stp == -1 )
474  {
475  pst = tmpc.planes() - 1;
476  ped = -1;
477  }
478 
479  for( int p = pst; p != ped; p += stp )
480  {
481  m_frames.image( findex ) = tmpc.image( p );
482  findex += stp;
483  if( findex == fed )
484  {
485  break;
486  }
487  }
488  }
489 
490  if( g_timeToDie != false )
491  {
492  std::cerr << " (" << invokedName << "): exiting.\n";
493  return -1;
494  }
495 
496  // De-allocate xrif
497  xrif_delete( m_xrif );
498  m_xrif = nullptr; // This is so destructor doesn't choke
499 
500  // Now create share memory stream.
501 
502  uint32_t imsize[3];
503  imsize[0] = m_width;
504  imsize[1] = m_height;
505  imsize[2] = m_circBuffLength;
506 
507  std::cerr << " (" << invokedName << "): Creating stream: " << m_shmimName << " (" << m_width << " x " << m_height
508  << " x " << m_circBuffLength << ")\n";
509 
510  ImageStreamIO_createIm_gpu( &m_imageStream,
511  m_shmimName.c_str(),
512  3,
513  imsize,
514  m_dataType,
515  -1,
516  1,
517  IMAGE_NB_SEMAPHORE,
518  0,
519  CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
520  0 );
521 
522  m_imageStream.md->cnt1 = m_circBuffLength;
523 
524  // Begin streaming
525  uint64_t next_cnt1 = 0;
526  char *next_dest = (char *)m_imageStream.array.raw;
527  timespec *next_wtimearr = &m_imageStream.writetimearray[0];
528  timespec *next_atimearr = &m_imageStream.atimearray[0];
529  uint64_t *next_cntarr = &m_imageStream.cntarray[0];
530 
531  findex = 0;
532  double lastSend = mx::sys::get_curr_time();
533  double delta = 0;
534 
535  while( g_timeToDie == false )
536  {
537  m_imageStream.md->write = 1;
538 
539  memcpy( next_dest, m_frames.image( findex ).data(), m_width * m_height * m_typeSize );
540 
541  // Set the time of last write
542  clock_gettime( CLOCK_REALTIME, &m_imageStream.md->writetime );
543  m_imageStream.md->atime = m_imageStream.md->writetime;
544 
545  // Update cnt1
546  m_imageStream.md->cnt1 = next_cnt1;
547 
548  // Update cnt0
549  m_imageStream.md->cnt0++;
550 
551  *next_wtimearr = m_imageStream.md->writetime;
552  *next_atimearr = m_imageStream.md->atime;
553  *next_cntarr = m_imageStream.md->cnt0;
554 
555  // And post
556  m_imageStream.md->write = 0;
557  ImageStreamIO_sempost( &m_imageStream, -1 );
558 
559  // Now we increment pointers outside the time-critical part of the loop.
560  next_cnt1 = m_imageStream.md->cnt1 + 1;
561  if( next_cnt1 >= m_circBuffLength )
562  next_cnt1 = 0;
563 
564  next_dest = (char *)m_imageStream.array.raw + next_cnt1 * m_width * m_height * m_typeSize;
565  next_wtimearr = &m_imageStream.writetimearray[next_cnt1];
566  next_atimearr = &m_imageStream.atimearray[next_cnt1];
567  next_cntarr = &m_imageStream.cntarray[next_cnt1];
568 
569  ++findex;
570  if( findex >= m_frames.planes() )
571  findex = 0;
572 
573  double ct = mx::sys::get_curr_time();
574  delta += 0.1 * ( ct - lastSend - 1.0 / m_fps );
575  lastSend = ct;
576 
577  if( 1. / m_fps - delta > 0 )
578  microsleep( ( 1. / m_fps - delta ) *
579  1e6 ); // Argument is unsigned, since we can't unsleep, so don't pass a big number by axe.
580  }
581 
582  ImageStreamIO_destroyIm( &m_imageStream );
583 
584  std::cerr << " (" << invokedName << "): exited normally.\n";
585 
586  return 0;
587 }
588 
589 #endif // xrif2shmim_hpp
A utility to stream MagaO-X images from xrif compressed archives to an ImageStreamIO stream.
Definition: xrif2shmim.hpp:65
std::vector< std::string > m_files
List of files to use. If dir is not empty, it will be pre-pended to each name.
Definition: xrif2shmim.hpp:75
uint32_t m_height
The height of the image.
Definition: xrif2shmim.hpp:100
uint32_t m_width
The width of the image.
Definition: xrif2shmim.hpp:99
size_t m_typeSize
The size of the type, in bytes. Result of sizeof.
Definition: xrif2shmim.hpp:106
uint8_t m_dataType
The ImageStreamIO type code.
Definition: xrif2shmim.hpp:104
double m_fps
The rate, in frames per second, at which to stream images. Default is 10 fps.
Definition: xrif2shmim.hpp:89
size_t m_numFrames
Definition: xrif2shmim.hpp:77
bool m_earliest
Definition: xrif2shmim.hpp:81
IMAGE m_imageStream
The ImageStreamIO shared memory buffer.
Definition: xrif2shmim.hpp:108
std::string m_shmimName
The name of the shared memory buffer to stream to. Default is "xrif2shmim".
Definition: xrif2shmim.hpp:84
virtual void setupConfig()
Definition: xrif2shmim.hpp:130
std::string m_dir
Definition: xrif2shmim.hpp:71
mx::improc::eigenCube< uint16_t > m_frames
Definition: xrif2shmim.hpp:102
xrif_t m_xrif
Definition: xrif2shmim.hpp:93
virtual int execute()
Definition: xrif2shmim.hpp:211
virtual void loadConfig()
Definition: xrif2shmim.hpp:200
uint32_t m_circBuffLength
The length of the shared memory circular buffer. Default is 1.
Definition: xrif2shmim.hpp:87
std::ostream & cerr()
bool g_timeToDie
Definition: xrif2shmim.hpp:44
void sigTermHandler(int signum, siginfo_t *siginf, void *ucont)
Definition: xrif2shmim.hpp:46
void microsleep(unsigned usec)
Sleep for a specified period in microseconds.
Definition: xrif2shmim.hpp:26