API
 
Loading...
Searching...
No Matches
shmimMonitor.hpp
Go to the documentation of this file.
1/** \file shmimMonitor.hpp
2 * \brief The MagAO-X generic shared memory monitor.
3 *
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 * \ingroup app_files
7 */
8
9#ifndef shmimMonitor_hpp
10#define shmimMonitor_hpp
11
12#include <ImageStreamIO/ImageStruct.h>
13#include <ImageStreamIO/ImageStreamIO.h>
14
15#include "../../libMagAOX/common/paths.hpp"
16
17namespace MagAOX
18{
19namespace app
20{
21namespace dev
22{
23
25{
26 init,
29};
30
31struct shmimT
32{
33 static std::string configSection()
34 {
35 return "shmimMonitor";
36 };
37
38 static std::string indiPrefix()
39 {
40 return "sm";
41 };
42};
43
44/** MagAO-X generic shared memory monitor
45 *
46 *
47 * The derived class `derivedT` has the following requirements (see below for discussion of specificT):
48 *
49 * - Must be derived from MagAOXApp<true>
50 *
51 * - Must contain the following friend declaration:
52 * \code
53 * friend class dev::shmimMonitor<derivedT, specificT>; //specificT may not need to be included
54 * \endcode
55 *
56 * * - Must contain the following typedef:
57 * \code
58 * typedef dev::shmimMonitor<derivedT, specificT> shmimMonitorT; //specificT may not need to be included
59 * \endcode
60 *
61 * - Must provide the following interfaces:
62 * \code
63 *
64 * //The allocate function is called after connecting to the shared memory buffer
65 * //It should check that the buffer has the expected size, and perform any internal allocations
66 * //to prepare for processing.
67 * int derivedT::allocate( const specificT & ///< [in] tag to differentiate shmimMonitor parents. Normally this
68 * is dev::shmimT for a single parent.
69 * );
70 *
71 * int derivedT::processImage( void * curr_src, ///< [in] pointer to the start of the current frame
72 * const specificT & ///< [in] tag to differentiate shmimMonitor parents. Normally
73 * this is dev::shmimT for a single parent.
74 * )
75 * \endcode
76 * Each of the above functions should return 0 on success, and -1 on an error.
77 *
78 * - Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic`, `updateINDI` and `appShutdown`
79 * functions must be placed in the derived class's functions of the same name. For convenience the
80 * following macros are defined to provide error checking:
81 * \code
82 * SHMIMMONITOR_SETUP_CONFIG( cfig )
83 * SHMIMMONITOR_LOAD_CONFIG( cfig )
84 * SHMIMMONITOR_APP_STARTUP
85 * SHMIMMONITOR_APP_LOGIC
86 * SHMIMMONITOR_UPDATE_INDI
87 * SHMIMMONITOR_APP_SHUTDOWN
88 * \endcode
89 * See below for the macros to use if `specificT` is used to specialize.
90 *
91 * The template specifier `specificT` allows inheritance of multiple shmimMonitor classes. This type must have at least
92 * the static member function:
93 * \code
94 * static std::string indiPrefix()
95 * \endcode
96 * which returns the string which is prefixed to INDI properties. The default `shmimT` uses "sm".
97 *
98 * Additionally, if `specificT` is used, the following convenience macros can be used:
99 * \code
100 * SHMIMMONITORT_SETUP_CONFIG( SHMIMMONITORT, cfig )
101 * SHMIMMONITORT_LOAD_CONFIG( SHMIMMONITORT, cfig )
102 * SHMIMMONITORT_APP_STARTUP( SHMIMMONITORT )
103 * SHMIMMONITORT_APP_LOGIC( SHMIMMONITORT )
104 * SHMIMMONITORT_APP_SHUTDOWN( SHMIMMONITORT )
105 * \endcode
106 *
107 *
108 * \ingroup appdev
109 */
110template <class derivedT, class specificT = shmimT>
112{
113
114 protected:
115 /** \name Configurable Parameters
116 * @{
117 */
118 std::string m_shmimName{ "" }; ///< The name of the shared memory image, is used in `/tmp/<shmimName>.im.shm`.
119 ///< Derived classes should set a default.
120
121 int m_smThreadPrio{ 2 }; ///< Priority of the shmimMonitor thread, should normally be > 00.
122
123 std::string m_smCpuset; ///< The cpuset to assign the shmimMonitor thread to. Ignored if empty (the default).
124
125 ///@}
126
127 bool m_getExistingFirst{ false }; ///< If set to true by derivedT, any existing image will be grabbed and sent to
128 ///< processImage before waiting on the semaphore.
129
131
132 int m_semaphoreNumber{ 5 }; ///< The image structure semaphore index.
133
134 uint32_t m_width{ 0 }; ///< The width of the images in the stream
135 uint32_t m_height{ 0 }; ///< The height of the images in the stream
136 uint32_t m_depth{ 0 }; ///< The depth of the circular buffer in the stream
137
138 uint8_t m_dataType{ 0 }; ///< The ImageStreamIO type code.
139 size_t m_typeSize{ 0 }; ///< The size of the type, in bytes. Result of sizeof.
140
141 IMAGE m_imageStream; ///< The ImageStreamIO shared memory buffer.
142
143 ino_t m_inode{ 0 }; ///< The inode of the image stream file
144
145 public:
146 const std::string &shmimName() const;
147
148 const uint32_t &width() const;
149
150 const uint32_t &height() const;
151
152 const uint32_t &depth() const;
153
154 const uint8_t &dataType() const;
155
156 const size_t &typeSize() const;
157
158 /// Setup the configuration system
159 /**
160 * This should be called in `derivedT::setupConfig` as
161 * \code
162 shmimMonitor<derivedT, specificT>::setupConfig(config);
163 \endcode
164 * with appropriate error checking.
165 */
166 int setupConfig( mx::app::appConfigurator &config /**< [out] the derived classes configurator*/ );
167
168 /// load the configuration system results
169 /**
170 * This should be called in `derivedT::loadConfig` as
171 * \code
172 shmimMonitor<derivedT, specificT>::loadConfig(config);
173 \endcode
174 * with appropriate error checking.
175 */
176 int loadConfig( mx::app::appConfigurator &config /**< [in] the derived classes configurator*/ );
177
178 /// Startup function
179 /** Starts the shmimMonitor thread
180 * This should be called in `derivedT::appStartup` as
181 * \code
182 shmimMonitor<derivedT, specificT>::appStartup();
183 \endcode
184 * with appropriate error checking.
185 *
186 * \returns 0 on success
187 * \returns -1 on error, which is logged.
188 */
190
191 /// Checks the shmimMonitor thread
192 /** This should be called in `derivedT::appLogic` as
193 * \code
194 shmimMonitor<derivedT, specificT>::appLogic();
195 \endcode
196 * with appropriate error checking.
197 *
198 * \returns 0 on success
199 * \returns -1 on error, which is logged.
200 */
201 int appLogic();
202
203 /// Shuts down the shmimMonitor thread
204 /** This should be called in `derivedT::appShutdown` as
205 * \code
206 shmimMonitor<derivedT, specificT>::appShutdown();
207 \endcode
208 * with appropriate error checking.
209 *
210 * \returns 0 on success
211 * \returns -1 on error, which is logged.
212 */
214
215 protected:
216 /** \name SIGSEGV & SIGBUS signal handling
217 * These signals occur as a result of a ImageStreamIO source server resetting (e.g. changing frame sizes).
218 * When they occur a restart of the shmim monitor thread main loops is triggered.
219 *
220 * @{
221 */
222 bool m_restart{ false }; ///< Flag indicating tha the shared memory should be reinitialized.
223
224 // static shmimMonitor *m_selfMonitor; ///< Static pointer to this (set in constructor). Used for getting out of
225 // the static SIGSEGV handler.
226
227 /** \name shmimmonitor Thread
228 * This thread actually monitors the shared memory buffer
229 * @{
230 */
231
232 bool m_smThreadInit{ true }; ///< Synchronizer for thread startup, to allow priority setting to finish.
233
234 pid_t m_smThreadID{ 0 }; ///< The s.m. thread PID.
235
236 pcf::IndiProperty m_smThreadProp; ///< The property to hold the s.m. thread details.
237
238 std::thread m_smThread; ///< A separate thread for the actual monitoring
239
240 /// Thread starter, called by MagAOXApp::threadStart on thread construction. Calls smThreadExec.
241 static void smThreadStart( shmimMonitor *s /**< [in] a pointer to a shmimMonitor instance (normally this) */ );
242
243 /// Execute the monitoring thread
245
246 /// Create the image
247 /** This will create the shared memory image, erasing an existing. This is independent of the actual
248 * shmim monitoring function, which will pick up the new inode change on its own and restart the
249 * allocate() and processImage() cycle.
250 *
251 * \returns 0 on success
252 * \returns <0 on error
253 */
254 int create( uint32_t width, ///< [in] width of the new image
255 uint32_t height, ///< [in] height of the new image
256 uint32_t depth, ///< [in] depth of the new image
257 uint8_t datatype, ///< [in] CACAO data type of the new image
258 void *initData = nullptr /**< [in] [optional] data to initialize the new image with. Must be of size
259 width*height*depth*sizeof(dataType) */
260 );
261 ///@}
262
263 /** \name INDI
264 *
265 *@{
266 */
267 protected:
268 // declare our properties
269
270 pcf::IndiProperty m_indiP_shmimName; ///< Property used to report the shmim buffer name
271
272 pcf::IndiProperty m_indiP_frameSize; ///< Property used to report the current frame size
273
274 public:
275 /// Update the INDI properties for this device controller
276 /** You should call this once per main loop.
277 * It is not called automatically.
278 *
279 * \returns 0 on success.
280 * \returns -1 on error.
281 */
283
284 ///@}
285
286 private:
287 derivedT &derived()
288 {
289 return *static_cast<derivedT *>( this );
290 }
291};
292
293// Set self pointer to null so app starts up uninitialized.
294// template <class derivedT, class specificT>
295// shmimMonitor<derivedT, specificT> *shmimMonitor<derivedT, specificT>::m_selfMonitor = nullptr;
296
297template <class derivedT, class specificT>
299{
300 return m_shmimName;
301}
302
303template <class derivedT, class specificT>
305{
306 return m_width;
307}
308
309template <class derivedT, class specificT>
311{
312 return m_height;
313}
314
315template <class derivedT, class specificT>
317{
318 return m_depth;
319}
320
321template <class derivedT, class specificT>
323{
324 return m_dataType;
325}
326
327template <class derivedT, class specificT>
329{
330 return m_typeSize;
331}
332
333template <class derivedT, class specificT>
334int shmimMonitor<derivedT, specificT>::setupConfig( mx::app::appConfigurator &config )
335{
336 config.add( specificT::configSection() + ".threadPrio",
337 "",
338 specificT::configSection() + ".threadPrio",
339 argType::Required,
340 specificT::configSection(),
341 "threadPrio",
342 false,
343 "int",
344 "The real-time priority of the shmimMonitor thread." );
345
346 config.add( specificT::configSection() + ".cpuset",
347 "",
348 specificT::configSection() + ".cpuset",
349 argType::Required,
350 specificT::configSection(),
351 "cpuset",
352 false,
353 "string",
354 "The cpuset for the shmimMonitor thread." );
355
356 config.add( specificT::configSection() + ".shmimName",
357 "",
358 specificT::configSection() + ".shmimName",
359 argType::Required,
360 specificT::configSection(),
361 "shmimName",
362 false,
363 "string",
364 "The name of the ImageStreamIO shared memory image. Will be used as /tmp/<shmimName>.im.shm." );
365
366 config.add( specificT::configSection() + ".getExistingFirst",
367 "",
368 specificT::configSection() + ".getExistingFirst",
369 argType::Required,
370 specificT::configSection(),
371 "getExistingFirst",
372 false,
373 "bool",
374 "If true an existing image is loaded. If false we wait for a new image." );
375
376 // Set this here to allow derived classes to set their own default before calling loadConfig
377 m_shmimName = derived().configName();
378
379 return 0;
380}
381
382template <class derivedT, class specificT>
383int shmimMonitor<derivedT, specificT>::loadConfig( mx::app::appConfigurator &config )
384{
385 config( m_smThreadPrio, specificT::configSection() + ".threadPrio" );
386 config( m_smCpuset, specificT::configSection() + ".cpuset" );
387 config( m_shmimName, specificT::configSection() + ".shmimName" );
388 config( m_getExistingFirst, specificT::configSection() + ".getExistingFirst" );
389
390 return 0;
391}
392
393template <class derivedT, class specificT>
395{
396 // Register the shmimName INDI property
397 m_indiP_shmimName = pcf::IndiProperty( pcf::IndiProperty::Text );
398 m_indiP_shmimName.setDevice( derived().configName() );
399 m_indiP_shmimName.setName( specificT::indiPrefix() + "_shmimName" );
400 m_indiP_shmimName.setPerm( pcf::IndiProperty::ReadOnly );
401 m_indiP_shmimName.setState( pcf::IndiProperty::Idle );
402 m_indiP_shmimName.add( pcf::IndiElement( "name" ) );
403 m_indiP_shmimName["name"] = m_shmimName;
404
405 if( derived().registerIndiPropertyNew( m_indiP_shmimName, nullptr ) < 0 )
406 {
407#ifndef SHMIMMONITOR_TEST_NOLOG
408 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
409#endif
410 return -1;
411 }
412
413 // Register the frameSize INDI property
414 m_indiP_frameSize = pcf::IndiProperty( pcf::IndiProperty::Number );
415 m_indiP_frameSize.setDevice( derived().configName() );
416 m_indiP_frameSize.setName( specificT::indiPrefix() + "_frameSize" );
417 m_indiP_frameSize.setPerm( pcf::IndiProperty::ReadOnly );
418 m_indiP_frameSize.setState( pcf::IndiProperty::Idle );
419 m_indiP_frameSize.add( pcf::IndiElement( "width" ) );
420 m_indiP_frameSize["width"] = 0;
421 m_indiP_frameSize.add( pcf::IndiElement( "height" ) );
422 m_indiP_frameSize["height"] = 0;
423
424 if( derived().registerIndiPropertyNew( m_indiP_frameSize, nullptr ) < 0 )
425 {
426#ifndef SHMIMMONITOR_TEST_NOLOG
427 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
428#endif
429 return -1;
430 }
431
432 // Install empty signal handler for USR1, which is used to interrupt sleeps in the monitor threads.
433 struct sigaction act;
434 sigset_t set;
435
436 act.sa_sigaction = &sigUsr1Handler;
437 act.sa_flags = SA_SIGINFO;
438 sigemptyset( &set );
439 act.sa_mask = set;
440
441 errno = 0;
442 if( sigaction( SIGUSR1, &act, 0 ) < 0 )
443 {
444 std::string logss = "Setting handler for SIGUSR1 failed. Errno says: ";
445 logss += strerror( errno );
446
447 derivedT::template log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
448
449 return -1;
450 }
451
452 if( derived().threadStart( m_smThread,
453 m_smThreadInit,
454 m_smThreadID,
455 m_smThreadProp,
456 m_smThreadPrio,
457 m_smCpuset,
458 specificT::configSection(),
459 this,
460 smThreadStart ) < 0 )
461 {
462 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
463 return -1;
464 }
465
466 return 0;
467}
468
469template <class derivedT, class specificT>
471{
472 // do a join check to see if other threads have exited.
473 if( pthread_tryjoin_np( m_smThread.native_handle(), 0 ) == 0 )
474 {
475 derivedT::template log<software_error>(
476 { __FILE__, __LINE__, "shmimMonitor thread " + std::to_string( m_smThreadID ) + " has exited" } );
477
478 return -1;
479 }
480
481 return 0;
482}
483
484template <class derivedT, class specificT>
486{
487 if( m_smThread.joinable() )
488 {
489 pthread_kill( m_smThread.native_handle(), SIGUSR1 );
490 try
491 {
492 m_smThread.join(); // this will throw if it was already joined
493 }
494 catch( ... )
495 {
496 }
497 }
498
499 return 0;
500}
501
502template <class derivedT, class specificT>
507
508template <class derivedT, class specificT>
510{
511 m_smThreadID = syscall( SYS_gettid );
512
513 // Wait for the thread starter to finish initializing this thread.
514 while( m_smThreadInit == true && derived().shutdown() == 0 )
515 {
516 sleep( 1 );
517 }
518
519 bool opened = false;
520
521 // bool semgot = false;
522
523 while( derived().shutdown() == 0 )
524 {
525 m_smState = shmimMonitorState::init;
526
527 while( ( derived().state() != stateCodes::OPERATING || m_shmimName == "" ) && !derived().shutdown() &&
528 !m_restart )
529 {
530 sleep( 1 );
531 }
532
533 if( derived().shutdown() )
534 return;
535
536 /* Initialize ImageStreamIO
537 */
538
539 opened = false;
540 m_restart = false; // Set this up front, since we're about to restart.
541
542 int logged = 0;
543 while( !opened && !derived().m_shutdown && !m_restart && derived().state() == stateCodes::OPERATING )
544 {
545 // b/c ImageStreamIO prints every single time, and latest version don't support stopping it yet, and that
546 // isn't thread-safe-able anyway we do our own checks. This is the same code in ImageStreamIO_openIm...
547 int SM_fd;
548 char SM_fname[200];
549 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
550 SM_fd = open( SM_fname, O_RDWR );
551 if( SM_fd == -1 )
552 {
553 m_smState = shmimMonitorState::notfound;
554
555 if( !logged )
556 derivedT::template log<text_log>(
557 "ImageStream " + m_shmimName + " not found (yet). Retrying . . .", logPrio::LOG_NOTICE );
558 logged = 1;
559 sleep( 1 ); // be patient
560 continue;
561 }
562
563 m_smState = shmimMonitorState::init; // Switch back to init if notfound
564
565 // Found and opened, close it and then use ImageStreamIO
566 logged = 0;
567 close( SM_fd );
568
569 if( ImageStreamIO_openIm( &m_imageStream, m_shmimName.c_str() ) == 0 )
570 {
571 if( m_imageStream.md[0].sem <=
572 m_semaphoreNumber ) ///<\todo this isn't right--> isn't there a define in cacao to use?
573 {
574 ImageStreamIO_closeIm( &m_imageStream );
575 mx::sys::sleep( 1 ); // We just need to wait for the server process to finish startup.
576 }
577 else
578 {
579 opened = true;
580 char SM_fname[200];
581 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
582
583 struct stat buffer;
584 int rv = stat( SM_fname, &buffer );
585
586 if( rv != 0 )
587 {
588 derivedT::template log<software_critical>(
589 { __FILE__,
590 __LINE__,
591 errno,
592 "Could not get inode for " + m_shmimName +
593 ". Source process will need to be restarted." } );
594 ImageStreamIO_closeIm( &m_imageStream );
595 return;
596 }
597 m_inode = buffer.st_ino;
598 }
599 }
600 else
601 {
602 mx::sys::sleep( 1 ); // be patient
603 }
604 }
605
606 if( m_restart )
607 {
608 continue;
609 }
610
611 if( derived().state() != stateCodes::OPERATING )
612 {
613 continue;
614 }
615
616 if( derived().m_shutdown )
617 {
618 if( !opened )
619 {
620 return;
621 }
622
623 ImageStreamIO_closeIm( &m_imageStream );
624 return;
625 }
626
627 m_semaphoreNumber =
628 ImageStreamIO_getsemwaitindex( &m_imageStream, m_semaphoreNumber ); // ask for semaphore we had before
629
630 if( m_semaphoreNumber < 0 )
631 {
632 derivedT::template log<software_critical>(
633 { __FILE__,
634 __LINE__,
635 "No valid semaphore found for " + m_shmimName + ". Source process will need to be restarted." } );
636 return;
637 }
638
639 derivedT::template log<software_info>(
640 { __FILE__,
641 __LINE__,
642 "got semaphore index " + std::to_string( m_semaphoreNumber ) + " for " + m_shmimName } );
643
644 ImageStreamIO_semflush( &m_imageStream, m_semaphoreNumber );
645
646 sem_t *sem = m_imageStream.semptr[m_semaphoreNumber]; ///< The semaphore to monitor for new image data
647
648 m_dataType = m_imageStream.md[0].datatype;
649 m_typeSize = ImageStreamIO_typesize( m_dataType );
650 m_width = m_imageStream.md[0].size[0];
651 int dim = 1;
652 if( m_imageStream.md[0].naxis > 1 )
653 {
654 m_height = m_imageStream.md[0].size[1];
655
656 if( m_imageStream.md[0].naxis > 2 )
657 {
658 dim = 3;
659 m_depth = m_imageStream.md[0].size[2];
660 }
661 else
662 {
663 dim = 2;
664 m_depth = 1;
665 }
666 }
667 else
668 {
669 m_height = 1;
670 m_depth = 1;
671 }
672
673 m_smState = shmimMonitorState::connected; // this means we now have vaild sizes, etc.
674
675 if( derived().allocate( specificT() ) < 0 )
676 {
677 derivedT::template log<software_error>( { __FILE__, __LINE__, "allocation failed" } );
678 break;
679 }
680
681 uint8_t atype;
682 size_t snx, sny, snz;
683 uint64_t curr_image; // The current cnt1 index
684
685 if( m_getExistingFirst && !m_restart &&
686 derived().shutdown() == 0 ) // If true, we always get the existing image without waiting on the semaphore.
687 {
688 if( m_imageStream.md[0].size[2] > 0 ) ///\todo change to naxis?
689 {
690 curr_image = m_imageStream.md[0].cnt1;
691 }
692 else
693 {
694 curr_image = 0;
695 }
696
697 atype = m_imageStream.md[0].datatype;
698 snx = m_imageStream.md[0].size[0];
699
700 if( dim == 2 )
701 {
702 sny = m_imageStream.md[0].size[1];
703 snz = 1;
704 }
705 else if( dim == 3 )
706 {
707 sny = m_imageStream.md[0].size[1];
708 snz = m_imageStream.md[0].size[2];
709 }
710 else
711 {
712 sny = 1;
713 snz = 1;
714 }
715
716 if( atype != m_dataType || snx != m_width || sny != m_height || snz != m_depth )
717 {
718 continue; // exit the nearest while loop and get the new image setup.
719 }
720
721 char *curr_src = (char *)m_imageStream.array.raw + curr_image * m_width * m_height * m_typeSize;
722
723 if( derived().processImage( curr_src, specificT() ) < 0 )
724 {
725 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
726 }
727 }
728
729 // This is the main image grabbing loop.
730 while( derived().shutdown() == 0 && !m_restart && derived().state() == stateCodes::OPERATING )
731 {
732
733 timespec ts;
734
735 if( clock_gettime( CLOCK_REALTIME, &ts ) < 0 )
736 {
737 derivedT::template log<software_critical>( { __FILE__, __LINE__, errno, 0, "clock_gettime" } );
738 return;
739 }
740
741 ts.tv_sec += 1;
742
743 if( sem_timedwait( sem, &ts ) == 0 )
744 {
745 if( m_imageStream.md[0].size[2] > 0 ) ///\todo change to naxis?
746 {
747 curr_image = m_imageStream.md[0].cnt1;
748 }
749 else
750 curr_image = 0;
751
752 atype = m_imageStream.md[0].datatype;
753 snx = m_imageStream.md[0].size[0];
754
755 if( dim == 2 )
756 {
757 sny = m_imageStream.md[0].size[1];
758 snz = 1;
759 }
760 else if( dim == 3 )
761 {
762 sny = m_imageStream.md[0].size[1];
763 snz = m_imageStream.md[0].size[2];
764 }
765 else
766 {
767 sny = 1;
768 snz = 1;
769 }
770
771 if( atype != m_dataType || snx != m_width || sny != m_height || snz != m_depth )
772 {
773 break; // exit the nearest while loop and get the new image setup.
774 }
775
776 if( derived().shutdown() != 0 || m_restart || derived().state() != stateCodes::OPERATING )
777 break; // Check for exit signals
778
779 char *curr_src = (char *)m_imageStream.array.raw + curr_image * m_width * m_height * m_typeSize;
780
781 if( derived().processImage( curr_src, specificT() ) < 0 )
782 {
783 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
784 }
785 }
786 else
787 {
788 if( m_imageStream.md[0].sem <= 0 )
789 break; // Indicates that the server has cleaned up.
790
791 // Check for why we timed out
792 if( errno == EINTR )
793 break; // This indicates signal interrupted us, time to restart or shutdown, loop will exit normally
794 // if flags set.
795
796 // ETIMEDOUT means we should check for deletion, and then wait more.
797 // Otherwise, report an error.
798 if( errno != ETIMEDOUT )
799 {
800 derivedT::template log<software_error>( { __FILE__, __LINE__, errno, "sem_timedwait" } );
801 break;
802 }
803
804 // Check if the file has disappeared.
805 int SM_fd;
806 char SM_fname[200];
807 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
808 SM_fd = open( SM_fname, O_RDWR );
809 if( SM_fd == -1 )
810 {
811 m_restart = true;
812 }
813 close( SM_fd );
814
815 // Check if the inode changed
816 struct stat buffer;
817 int rv = stat( SM_fname, &buffer );
818 if( rv != 0 )
819 {
820 m_restart = true;
821 }
822
823 if( buffer.st_ino != m_inode )
824 {
825 m_restart = true;
826 }
827 }
828 }
829
830 //*******
831 // call derived().cleanup()
832 //*******
833
834 // opened == true if we can get to this
835 if( m_semaphoreNumber >= 0 )
836 m_imageStream.semReadPID[m_semaphoreNumber] = 0; // release semaphore
837 ImageStreamIO_closeIm( &m_imageStream );
838 opened = false;
839
840 } // outer loop, will exit if m_shutdown==true
841
842 // derivedT::template log<software_error>({__FILE__,__LINE__, std::to_string(m_smThreadID) + " " +
843 // std::to_string(derived().shutdown() == 0)});
844
845 //*******
846 // call derived().cleanup()
847 //*******
848
849 if( opened )
850 {
851 ImageStreamIO_closeIm( &m_imageStream );
852 opened = false;
853 }
854}
855
856template <class derivedT, class specificT>
858 uint32_t width, uint32_t height, uint32_t depth, uint8_t dtype, void *initData )
859{
860 // This is all local to this function, so the smThread itself will find out about the changes when the inode
861 // goes stale.
862 IMAGE imageStream;
863
864 // First silently see if it exists and then destroy it if it does
865 int SM_fd;
866 char SM_fname[200];
867 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
868 SM_fd = open( SM_fname, O_RDWR );
869 if( SM_fd != -1 )
870 {
871 close( SM_fd );
872
873 if( ImageStreamIO_openIm( &m_imageStream, m_shmimName.c_str() ) != IMAGESTREAMIO_SUCCESS )
874 {
875 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
876 }
877
878 if( ImageStreamIO_destroyIm( &m_imageStream ) != IMAGESTREAMIO_SUCCESS )
879 {
880 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
881 }
882 }
883
884 uint32_t imsize[3] = { 0, 0, 0 };
885
886 imsize[0] = width;
887 imsize[1] = height;
888 imsize[2] = depth;
889
890 std::cerr << "Creating: " << m_shmimName << " " << width << " " << height << " " << depth << "\n";
891
892 if( ImageStreamIO_createIm_gpu( &imageStream,
893 m_shmimName.c_str(),
894 3,
895 imsize,
896 dtype,
897 -1,
898 1,
899 IMAGE_NB_SEMAPHORE,
900 0,
901 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
902 0 ) != IMAGESTREAMIO_SUCCESS )
903 {
904 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
905 }
906
907 if( initData != nullptr )
908 {
909 memcpy( imageStream.array.raw, initData, width * height * ImageStreamIO_typesize( dtype ) );
910 }
911
912 imageStream.md->cnt1 = depth - 1;
913
914 if( ImageStreamIO_closeIm( &imageStream ) != IMAGESTREAMIO_SUCCESS )
915 {
916 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
917 }
918
919 return 0;
920}
921
922template <class derivedT, class specificT>
924{
925 if( !derived().m_indiDriver )
926 return 0;
927
928 indi::updateIfChanged( m_indiP_shmimName, "name", m_shmimName, derived().m_indiDriver );
929 indi::updateIfChanged( m_indiP_frameSize, "width", m_width, derived().m_indiDriver );
930 indi::updateIfChanged( m_indiP_frameSize, "height", m_height, derived().m_indiDriver );
931
932 return 0;
933}
934
935/// Call shmimMonitorT::setupConfig with error checking for shmimMonitor
936/**
937 * \param cfig the application configurator
938 */
939#define SHMIMMONITOR_SETUP_CONFIG( cfig ) \
940 if( shmimMonitorT::setupConfig( cfig ) < 0 ) \
941 { \
942 log<software_error>( { __FILE__, __LINE__, "Error from shmimMonitorT::setupConfig" } ); \
943 m_shutdown = true; \
944 return; \
945 }
946
947/// Call shmimMonitorT::setupConfig with error checking for a typedef-ed shmimMonitor
948/**
949 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
950 * \param cfig the application configurator
951 */
952#define SHMIMMONITORT_SETUP_CONFIG( SHMIMMONITORT, cfig ) \
953 if( SHMIMMONITORT::setupConfig( cfig ) < 0 ) \
954 { \
955 log<software_error>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::setupConfig" } ); \
956 m_shutdown = true; \
957 return; \
958 }
959
960/// Call shmimMonitorT::loadConfig with error checking for shmimMonitor
961/** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
962 * \param cfig the application configurator
963 */
964#define SHMIMMONITOR_LOAD_CONFIG( cfig ) \
965 if( shmimMonitorT::loadConfig( cfig ) < 0 ) \
966 { \
967 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::loadConfig" } ); \
968 }
969
970/// Call shmimMonitorT::loadConfig with error checking for a typedef-ed shmimMonitor
971/** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
972 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
973 * \param cfig the application configurator
974 */
975#define SHMIMMONITORT_LOAD_CONFIG( SHMIMMONITORT, cfig ) \
976 if( SHMIMMONITORT::loadConfig( cfig ) < 0 ) \
977 { \
978 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::loadConfig" } ); \
979 }
980
981/// Call shmimMonitorT::appStartup with error checking for shmimMonitor
982#define SHMIMMONITOR_APP_STARTUP \
983 if( shmimMonitorT::appStartup() < 0 ) \
984 { \
985 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::appStartup" } ); \
986 }
987
988/// Call shmimMonitorT::appStartup with error checking for a typedef-ed shmimMonitor
989/**
990 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
991 */
992#define SHMIMMONITORT_APP_STARTUP( SHMIMMONITORT ) \
993 if( SHMIMMONITORT::appStartup() < 0 ) \
994 { \
995 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appStartup" } ); \
996 }
997
998/// Call shmimMonitorT::appLogic with error checking for shmimMonitor
999#define SHMIMMONITOR_APP_LOGIC \
1000 if( shmimMonitorT::appLogic() < 0 ) \
1001 { \
1002 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::appLogic" } ); \
1003 }
1004
1005/// Call shmimMonitorT::appLogic with error checking for a typedef-ed shmimMonitor
1006/**
1007 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
1008 */
1009#define SHMIMMONITORT_APP_LOGIC( SHMIMMONITORT ) \
1010 if( SHMIMMONITORT::appLogic() < 0 ) \
1011 { \
1012 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appLogic" } ); \
1013 }
1014
1015/// Call shmimMonitorT::updateINDI with error checking for shmimMonitor
1016#define SHMIMMONITOR_UPDATE_INDI \
1017 if( shmimMonitorT::updateINDI() < 0 ) \
1018 { \
1019 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::updateINDI" } ); \
1020 }
1021
1022/// Call shmimMonitorT::updateINDI with error checking for a typedef-ed shmimMonitor
1023/**
1024 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
1025 */
1026#define SHMIMMONITORT_UPDATE_INDI( SHMIMMONITORT ) \
1027 if( SHMIMMONITORT::updateINDI() < 0 ) \
1028 { \
1029 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::updateINDI" } ); \
1030 }
1031
1032/// Call shmimMonitorT::appShutdown with error checking for shmimMonitor
1033#define SHMIMMONITOR_APP_SHUTDOWN \
1034 if( shmimMonitorT::appShutdown() < 0 ) \
1035 { \
1036 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::appShutdown" } ); \
1037 }
1038
1039/// Call shmimMonitorT::appShutodwn with error checking for a typedef-ed shmimMonitor
1040/**
1041 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
1042 */
1043#define SHMIMMONITORT_APP_SHUTDOWN( SHMIMMONITORT ) \
1044 if( SHMIMMONITORT::appShutdown() < 0 ) \
1045 { \
1046 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appShutdown" } ); \
1047 }
1048
1049} // namespace dev
1050} // namespace app
1051} // namespace MagAOX
1052#endif
bool m_smThreadInit
Synchronizer for thread startup, to allow priority setting to finish.
uint32_t m_depth
The depth of the circular buffer in the stream.
int appStartup()
Startup function.
pid_t m_smThreadID
The s.m. thread PID.
void smThreadExec()
Execute the monitoring thread.
uint32_t m_width
The width of the images in the stream.
const uint8_t & dataType() const
const size_t & typeSize() const
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
std::thread m_smThread
A separate thread for the actual monitoring.
int m_smThreadPrio
Priority of the shmimMonitor thread, should normally be > 00.
IMAGE m_imageStream
The ImageStreamIO shared memory buffer.
pcf::IndiProperty m_indiP_shmimName
Property used to report the shmim buffer name.
const uint32_t & depth() const
pcf::IndiProperty m_indiP_frameSize
Property used to report the current frame size.
static void smThreadStart(shmimMonitor *s)
Thread starter, called by MagAOXApp::threadStart on thread construction. Calls smThreadExec.
int m_semaphoreNumber
The image structure semaphore index.
int create(uint32_t width, uint32_t height, uint32_t depth, uint8_t datatype, void *initData=nullptr)
Create the image.
const uint32_t & width() const
int appShutdown()
Shuts down the shmimMonitor thread.
const uint32_t & height() const
uint8_t m_dataType
The ImageStreamIO type code.
bool m_restart
Flag indicating tha the shared memory should be reinitialized.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
pcf::IndiProperty m_smThreadProp
The property to hold the s.m. thread details.
size_t m_typeSize
The size of the type, in bytes. Result of sizeof.
ino_t m_inode
The inode of the image stream file.
const std::string & shmimName() const
std::string m_smCpuset
The cpuset to assign the shmimMonitor thread to. Ignored if empty (the default).
@ OPERATING
The device is operating, other than homing.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition indiUtils.hpp:92
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
Definition MagAOXApp.cpp:42
Definition dm.hpp:26
static std::string configSection()
static std::string indiPrefix()