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 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
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
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,
515 -1,
516 1,
517 IMAGE_NB_SEMAPHORE,
518 0,
519 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
520 0 );
521
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.
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()
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.