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