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.hpp"
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
133
134 int m_semaphoreNumber{ 5 }; ///< The image structure semaphore index.
135
136 uint32_t m_width{ 0 }; ///< The width of the images in the stream
137 uint32_t m_height{ 0 }; ///< The height of the images in the stream
138 uint32_t m_depth{ 0 }; ///< The depth of the circular buffer in the stream
139
140 uint8_t m_dataType{ 0 }; ///< The ImageStreamIO type code.
141 size_t m_typeSize{ 0 }; ///< The size of the type, in bytes. Result of sizeof.
142
143 IMAGE m_imageStream; ///< The ImageStreamIO shared memory buffer.
144
145 ino_t m_inode{ 0 }; ///< The inode of the image stream file
146
147 public:
148 const std::string &shmimName() const;
149
150 const uint32_t &width() const;
151
152 const uint32_t &height() const;
153
154 const uint32_t &depth() const;
155
156 const uint8_t &dataType() const;
157
158 const size_t &typeSize() const;
159
160 /// Setup the configuration system
161 /**
162 * This should be called in `derivedT::setupConfig` as
163 * \code
164 shmimMonitor<derivedT, specificT>::setupConfig(config);
165 \endcode
166 * with appropriate error checking.
167 */
168 int setupConfig( mx::app::appConfigurator &config /**< [out] the derived classes configurator*/ );
169
170 /// load the configuration system results
171 /**
172 * This should be called in `derivedT::loadConfig` as
173 * \code
174 shmimMonitor<derivedT, specificT>::loadConfig(config);
175 \endcode
176 * with appropriate error checking.
177 */
178 int loadConfig( mx::app::appConfigurator &config /**< [in] the derived classes configurator*/ );
179
180 /// Startup function
181 /** Starts the shmimMonitor thread
182 * This should be called in `derivedT::appStartup` as
183 * \code
184 shmimMonitor<derivedT, specificT>::appStartup();
185 \endcode
186 * with appropriate error checking.
187 *
188 * \returns 0 on success
189 * \returns -1 on error, which is logged.
190 */
192
193 /// Checks the shmimMonitor thread
194 /** This should be called in `derivedT::appLogic` as
195 * \code
196 shmimMonitor<derivedT, specificT>::appLogic();
197 \endcode
198 * with appropriate error checking.
199 *
200 * \returns 0 on success
201 * \returns -1 on error, which is logged.
202 */
203 int appLogic();
204
205 /// Shuts down the shmimMonitor thread
206 /** This should be called in `derivedT::appShutdown` as
207 * \code
208 shmimMonitor<derivedT, specificT>::appShutdown();
209 \endcode
210 * with appropriate error checking.
211 *
212 * \returns 0 on success
213 * \returns -1 on error, which is logged.
214 */
216
217 protected:
218 /** \name SIGSEGV & SIGBUS signal handling
219 * These signals occur as a result of a ImageStreamIO source server resetting (e.g. changing frame sizes).
220 * When they occur a restart of the shmim monitor thread main loops is triggered.
221 *
222 * @{
223 */
224 bool m_restart{ false }; ///< Flag indicating tha the shared memory should be reinitialized.
225
226 // static shmimMonitor *m_selfMonitor; ///< Static pointer to this (set in constructor). Used for getting out of
227 // the static SIGSEGV handler.
228
229 /** \name shmimmonitor Thread
230 * This thread actually monitors the shared memory buffer
231 * @{
232 */
233
234 bool m_smThreadInit{ true }; ///< Synchronizer for thread startup, to allow priority setting to finish.
235
236 pid_t m_smThreadID{ 0 }; ///< The s.m. thread PID.
237
238 pcf::IndiProperty m_smThreadProp; ///< The property to hold the s.m. thread details.
239
240 std::thread m_smThread; ///< A separate thread for the actual monitoring
241
242 /// Thread starter, called by MagAOXApp::threadStart on thread construction. Calls smThreadExec.
243 static void smThreadStart( shmimMonitor *s /**< [in] a pointer to a shmimMonitor instance (normally this) */ );
244
245 /// Execute the monitoring thread
247
248 /// Create the image
249 /** This will create the shared memory image, erasing an existing. This is independent of the actual
250 * shmim monitoring function, which will pick up the new inode change on its own and restart the
251 * allocate() and processImage() cycle.
252 *
253 * \returns 0 on success
254 * \returns <0 on error
255 */
256 int create( uint32_t width, ///< [in] width of the new image
257 uint32_t height, ///< [in] height of the new image
258 uint32_t depth, ///< [in] depth of the new image
259 uint8_t datatype, ///< [in] CACAO data type of the new image
260 void *initData = nullptr /**< [in] [optional] data to initialize the new image with. Must be of size
261 width*height*depth*sizeof(dataType) */
262 );
263 ///@}
264
265 /** \name INDI
266 *
267 *@{
268 */
269 protected:
270 // declare our properties
271
272 pcf::IndiProperty m_indiP_shmimName; ///< Property used to report the shmim buffer name
273
274 pcf::IndiProperty m_indiP_frameSize; ///< Property used to report the current frame size
275
276 public:
277 /// Update the INDI properties for this device controller
278 /** You should call this once per main loop.
279 * It is not called automatically.
280 *
281 * \returns 0 on success.
282 * \returns -1 on error.
283 */
285
286 ///@}
287
288 private:
289 derivedT &derived()
290 {
291 return *static_cast<derivedT *>( this );
292 }
293};
294
295// Set self pointer to null so app starts up uninitialized.
296// template <class derivedT, class specificT>
297// shmimMonitor<derivedT, specificT> *shmimMonitor<derivedT, specificT>::m_selfMonitor = nullptr;
298
299template <class derivedT, class specificT>
301{
302 return m_shmimName;
303}
304
305template <class derivedT, class specificT>
307{
308 return m_width;
309}
310
311template <class derivedT, class specificT>
313{
314 return m_height;
315}
316
317template <class derivedT, class specificT>
319{
320 return m_depth;
321}
322
323template <class derivedT, class specificT>
325{
326 return m_dataType;
327}
328
329template <class derivedT, class specificT>
331{
332 return m_typeSize;
333}
334
335template <class derivedT, class specificT>
336int shmimMonitor<derivedT, specificT>::setupConfig( mx::app::appConfigurator &config )
337{
338 config.add( specificT::configSection() + ".threadPrio",
339 "",
340 specificT::configSection() + ".threadPrio",
341 argType::Required,
342 specificT::configSection(),
343 "threadPrio",
344 false,
345 "int",
346 "The real-time priority of the shmimMonitor thread." );
347
348 config.add( specificT::configSection() + ".cpuset",
349 "",
350 specificT::configSection() + ".cpuset",
351 argType::Required,
352 specificT::configSection(),
353 "cpuset",
354 false,
355 "string",
356 "The cpuset for the shmimMonitor thread." );
357
358 config.add( specificT::configSection() + ".shmimName",
359 "",
360 specificT::configSection() + ".shmimName",
361 argType::Required,
362 specificT::configSection(),
363 "shmimName",
364 false,
365 "string",
366 "The name of the ImageStreamIO shared memory image. Will be used as /tmp/<shmimName>.im.shm." );
367
368 config.add( specificT::configSection() + ".getExistingFirst",
369 "",
370 specificT::configSection() + ".getExistingFirst",
371 argType::Required,
372 specificT::configSection(),
373 "getExistingFirst",
374 false,
375 "bool",
376 "If true an existing image is loaded. If false we wait for a new image." );
377
378 // Set this here to allow derived classes to set their own default before calling loadConfig
379 m_shmimName = derived().configName();
380
381 return 0;
382}
383
384template <class derivedT, class specificT>
385int shmimMonitor<derivedT, specificT>::loadConfig( mx::app::appConfigurator &config )
386{
387 config( m_smThreadPrio, specificT::configSection() + ".threadPrio" );
388 config( m_smCpuset, specificT::configSection() + ".cpuset" );
389 config( m_shmimName, specificT::configSection() + ".shmimName" );
390 config( m_getExistingFirst, specificT::configSection() + ".getExistingFirst" );
391
392 return 0;
393}
394
395template <class derivedT, class specificT>
397{
398 // Register the shmimName INDI property
399 m_indiP_shmimName = pcf::IndiProperty( pcf::IndiProperty::Text );
400 m_indiP_shmimName.setDevice( derived().configName() );
401 m_indiP_shmimName.setName( specificT::indiPrefix() + "_shmimName" );
402 m_indiP_shmimName.setPerm( pcf::IndiProperty::ReadOnly );
403 m_indiP_shmimName.setState( pcf::IndiProperty::Idle );
404 m_indiP_shmimName.add( pcf::IndiElement( "name" ) );
405 m_indiP_shmimName["name"] = m_shmimName;
406
407 if( derived().registerIndiPropertyNew( m_indiP_shmimName, nullptr ) < 0 )
408 {
409#ifndef SHMIMMONITOR_TEST_NOLOG
410 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
411#endif
412 return -1;
413 }
414
415 // Register the frameSize INDI property
416 m_indiP_frameSize = pcf::IndiProperty( pcf::IndiProperty::Number );
417 m_indiP_frameSize.setDevice( derived().configName() );
418 m_indiP_frameSize.setName( specificT::indiPrefix() + "_frameSize" );
419 m_indiP_frameSize.setPerm( pcf::IndiProperty::ReadOnly );
420 m_indiP_frameSize.setState( pcf::IndiProperty::Idle );
421 m_indiP_frameSize.add( pcf::IndiElement( "width" ) );
422 m_indiP_frameSize["width"] = 0;
423 m_indiP_frameSize.add( pcf::IndiElement( "height" ) );
424 m_indiP_frameSize["height"] = 0;
425
426 if( derived().registerIndiPropertyNew( m_indiP_frameSize, nullptr ) < 0 )
427 {
428#ifndef SHMIMMONITOR_TEST_NOLOG
429 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
430#endif
431 return -1;
432 }
433
434 // Install empty signal handler for USR1, which is used to interrupt sleeps in the monitor threads.
435 struct sigaction act;
436 sigset_t set;
437
438 act.sa_sigaction = &sigUsr1Handler;
439 act.sa_flags = SA_SIGINFO;
440 sigemptyset( &set );
441 act.sa_mask = set;
442
443 errno = 0;
444 if( sigaction( SIGUSR1, &act, 0 ) < 0 )
445 {
446 std::string logss = "Setting handler for SIGUSR1 failed. Errno says: ";
447 logss += strerror( errno );
448
449 derivedT::template log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
450
451 return -1;
452 }
453
454 if( derived().threadStart( m_smThread,
455 m_smThreadInit,
456 m_smThreadID,
457 m_smThreadProp,
458 m_smThreadPrio,
459 m_smCpuset,
460 specificT::configSection(),
461 this,
462 smThreadStart ) < 0 )
463 {
464 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
465 return -1;
466 }
467
468 return 0;
469}
470
471template <class derivedT, class specificT>
473{
474 // do a join check to see if other threads have exited.
475 if( pthread_tryjoin_np( m_smThread.native_handle(), 0 ) == 0 )
476 {
477 derivedT::template log<software_error>(
478 { __FILE__, __LINE__, "shmimMonitor thread " + std::to_string( m_smThreadID ) + " has exited" } );
479
480 return -1;
481 }
482
483 return 0;
484}
485
486template <class derivedT, class specificT>
488{
489 if( m_smThread.joinable() )
490 {
491 pthread_kill( m_smThread.native_handle(), SIGUSR1 );
492 try
493 {
494 m_smThread.join(); // this will throw if it was already joined
495 }
496 catch( ... )
497 {
498 }
499 }
500
501 return 0;
502}
503
504template <class derivedT, class specificT>
509
510template <class derivedT, class specificT>
512{
513 m_smThreadID = syscall( SYS_gettid );
514
515 // Wait for the thread starter to finish initializing this thread.
516 while( m_smThreadInit == true && derived().shutdown() == 0 )
517 {
518 sleep( 1 );
519 }
520
521 bool opened = false;
522
523 // bool semgot = false;
524
525 std::cerr << m_shmimName << " smThreadExec\n";
526
527 while( derived().shutdown() == 0 )
528 {
529 m_smState = shmimMonitorState::init;
530
531 while( ( derived().state() != m_targetState || m_shmimName == "" ) && !derived().shutdown() && !m_restart )
532 {
533 sleep( 1 );
534 }
535
536 if( derived().shutdown() )
537 {
538 return;
539 }
540
541 /* Initialize ImageStreamIO
542 */
543
544 opened = false;
545 m_restart = false; // Set this up front, since we're about to restart.
546
547 int logged = 0;
548 while( !opened && !derived().m_shutdown && !m_restart && derived().state() == m_targetState )
549 {
550 // b/c ImageStreamIO prints every single time, and latest version don't support stopping it yet, and that
551 // isn't thread-safe-able anyway we do our own checks. This is the same code in ImageStreamIO_openIm...
552 int SM_fd;
553 char SM_fname[200];
554 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
555 SM_fd = open( SM_fname, O_RDWR );
556 if( SM_fd == -1 )
557 {
558 m_smState = shmimMonitorState::notfound;
559
560 if( !logged )
561 derivedT::template log<text_log>(
562 "ImageStream " + m_shmimName + " not found (yet). Retrying . . .", logPrio::LOG_NOTICE );
563 logged = 1;
564 sleep( 1 ); // be patient
565 continue;
566 }
567
568 m_smState = shmimMonitorState::init; // Switch back to init if notfound
569
570 // Found and opened, close it and then use ImageStreamIO
571 logged = 0;
572 close( SM_fd );
573
574 if( ImageStreamIO_openIm( &m_imageStream, m_shmimName.c_str() ) == 0 )
575 {
576 if( m_imageStream.md[0].sem < SEMAPHORE_MAXVAL )
577 {
578 ImageStreamIO_closeIm( &m_imageStream );
579 mx::sys::sleep( 1 ); // We just need to wait for the server process to finish startup.
580 }
581 else
582 {
583 opened = true;
584 char SM_fname[200];
585 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
586
587 struct stat buffer;
588 int rv = stat( SM_fname, &buffer );
589
590 if( rv != 0 )
591 {
592 derivedT::template log<software_critical>(
593 { __FILE__,
594 __LINE__,
595 errno,
596 "Could not get inode for " + m_shmimName +
597 ". Source process will need to be restarted." } );
598 ImageStreamIO_closeIm( &m_imageStream );
599 return;
600 }
601
602 m_inode = buffer.st_ino;
603 }
604 }
605 else
606 {
607 mx::sys::sleep( 1 ); // be patient
608 }
609 }
610
611 if( m_restart )
612 {
613 continue;
614 }
615
616 if( derived().state() != m_targetState )
617 {
618 continue;
619 }
620
621 if( derived().m_shutdown )
622 {
623 if( !opened )
624 {
625 return;
626 }
627
628 ImageStreamIO_closeIm( &m_imageStream );
629 return;
630 }
631
632 m_semaphoreNumber =
633 ImageStreamIO_getsemwaitindex( &m_imageStream, m_semaphoreNumber ); // ask for semaphore we had before
634
635 if( m_semaphoreNumber < 0 )
636 {
637 derivedT::template log<software_critical>(
638 { __FILE__,
639 __LINE__,
640 "No valid semaphore found for " + m_shmimName + ". Source process will need to be restarted." } );
641 return;
642 }
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 valid 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 true, we always get the existing image without waiting on the semaphore.
686 if( m_getExistingFirst && !m_restart && derived().shutdown() == 0 )
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() == m_targetState )
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 {
751 curr_image = 0;
752 }
753
754 atype = m_imageStream.md[0].datatype;
755 snx = m_imageStream.md[0].size[0];
756
757 if( dim == 2 )
758 {
759 sny = m_imageStream.md[0].size[1];
760 snz = 1;
761 }
762 else if( dim == 3 )
763 {
764 sny = m_imageStream.md[0].size[1];
765 snz = m_imageStream.md[0].size[2];
766 }
767 else
768 {
769 sny = 1;
770 snz = 1;
771 }
772
773 if( atype != m_dataType || snx != m_width || sny != m_height || snz != m_depth )
774 {
775 break; // exit the nearest while loop and get the new image setup.
776 }
777
778 if( derived().shutdown() != 0 || m_restart || derived().state() != m_targetState )
779 {
780 break; // Check for exit signals
781 }
782
783 char *curr_src = (char *)m_imageStream.array.raw + curr_image * m_width * m_height * m_typeSize;
784
785 if( derived().processImage( curr_src, specificT() ) < 0 )
786 {
787 derivedT::template log<software_error>( { __FILE__, __LINE__ } );
788 }
789 }
790 else
791 {
792 if( m_imageStream.md[0].sem <= 0 )
793 break; // Indicates that the server has cleaned up.
794
795 // Check for why we timed out
796 if( errno == EINTR )
797 break; // This indicates signal interrupted us, time to restart or shutdown, loop will exit normally
798 // if flags set.
799
800 // ETIMEDOUT means we should check for deletion, and then wait more.
801 // Otherwise, report an error.
802 if( errno != ETIMEDOUT )
803 {
804 derivedT::template log<software_error>( { __FILE__, __LINE__, errno, "sem_timedwait" } );
805 break;
806 }
807
808 // Check if the file has disappeared.
809 int SM_fd;
810 char SM_fname[200];
811 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
812 SM_fd = open( SM_fname, O_RDWR );
813 if( SM_fd == -1 )
814 {
815 m_restart = true;
816 }
817 close( SM_fd );
818
819 // Check if the inode changed
820 struct stat buffer;
821 int rv = stat( SM_fname, &buffer );
822 if( rv != 0 )
823 {
824 m_restart = true;
825 }
826
827 if( buffer.st_ino != m_inode )
828 {
829 m_restart = true;
830 }
831 }
832 }
833
834 //*******
835 // call derived().cleanup()
836 //*******
837
838 // opened == true if we can get to this
839 if( m_semaphoreNumber >= 0 )
840 m_imageStream.semReadPID[m_semaphoreNumber] = 0; // release semaphore
841 ImageStreamIO_closeIm( &m_imageStream );
842 opened = false;
843
844 } // outer loop, will exit if m_shutdown==true
845
846 // derivedT::template log<software_error>({__FILE__,__LINE__, std::to_string(m_smThreadID) + " " +
847 // std::to_string(derived().shutdown() == 0)});
848
849 //*******
850 // call derived().cleanup()
851 //*******
852
853 if( opened )
854 {
855 ImageStreamIO_closeIm( &m_imageStream );
856 opened = false;
857 }
858}
859
860template <class derivedT, class specificT>
862 uint32_t width, uint32_t height, uint32_t depth, uint8_t dtype, void *initData )
863{
864 // This is all local to this function, so the smThread itself will find out about the changes when the inode
865 // goes stale.
866 IMAGE imageStream;
867
868 // First silently see if it exists and then destroy it if it does
869 int SM_fd;
870 char SM_fname[200];
871 ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
872 SM_fd = open( SM_fname, O_RDWR );
873 if( SM_fd != -1 )
874 {
875 close( SM_fd );
876
877 if( ImageStreamIO_openIm( &m_imageStream, m_shmimName.c_str() ) != IMAGESTREAMIO_SUCCESS )
878 {
879 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
880 }
881
882 if( ImageStreamIO_destroyIm( &m_imageStream ) != IMAGESTREAMIO_SUCCESS )
883 {
884 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
885 }
886 }
887
888 uint32_t imsize[3] = { 0, 0, 0 };
889
890 imsize[0] = width;
891 imsize[1] = height;
892 imsize[2] = depth;
893
894 std::cerr << "Creating: " << m_shmimName << " " << width << " " << height << " " << depth << "\n";
895
896 if( ImageStreamIO_createIm_gpu( &imageStream,
897 m_shmimName.c_str(),
898 3,
899 imsize,
900 dtype,
901 -1,
902 1,
903 IMAGE_NB_SEMAPHORE,
904 0,
905 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
906 0 ) != IMAGESTREAMIO_SUCCESS )
907 {
908 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
909 }
910
911 if( initData != nullptr )
912 {
913 memcpy( imageStream.array.raw, initData, width * height * ImageStreamIO_typesize( dtype ) );
914 }
915
916 imageStream.md->cnt1 = depth - 1;
917
918 if( ImageStreamIO_closeIm( &imageStream ) != IMAGESTREAMIO_SUCCESS )
919 {
920 return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
921 }
922
923 return 0;
924}
925
926template <class derivedT, class specificT>
928{
929 if( !derived().m_indiDriver )
930 return 0;
931
932 indi::updateIfChanged( m_indiP_shmimName, "name", m_shmimName, derived().m_indiDriver );
933 indi::updateIfChanged( m_indiP_frameSize, "width", m_width, derived().m_indiDriver );
934 indi::updateIfChanged( m_indiP_frameSize, "height", m_height, derived().m_indiDriver );
935
936 return 0;
937}
938
939/// Call shmimMonitorT::setupConfig with error checking for shmimMonitor
940/**
941 * \param cfig the application configurator
942 */
943#define SHMIMMONITOR_SETUP_CONFIG( cfig ) \
944 if( shmimMonitorT::setupConfig( cfig ) < 0 ) \
945 { \
946 log<software_error>( { __FILE__, __LINE__, "Error from shmimMonitorT::setupConfig" } ); \
947 m_shutdown = true; \
948 return; \
949 }
950
951/// Call shmimMonitorT::setupConfig with error checking for a typedef-ed shmimMonitor
952/**
953 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
954 * \param cfig the application configurator
955 */
956#define SHMIMMONITORT_SETUP_CONFIG( SHMIMMONITORT, cfig ) \
957 if( SHMIMMONITORT::setupConfig( cfig ) < 0 ) \
958 { \
959 log<software_error>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::setupConfig" } ); \
960 m_shutdown = true; \
961 return; \
962 }
963
964/// Call shmimMonitorT::loadConfig with error checking for shmimMonitor
965/** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
966 * \param cfig the application configurator
967 */
968#define SHMIMMONITOR_LOAD_CONFIG( cfig ) \
969 if( shmimMonitorT::loadConfig( cfig ) < 0 ) \
970 { \
971 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::loadConfig" } ); \
972 }
973
974/// Call shmimMonitorT::loadConfig with error checking for a typedef-ed shmimMonitor
975/** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
976 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
977 * \param cfig the application configurator
978 */
979#define SHMIMMONITORT_LOAD_CONFIG( SHMIMMONITORT, cfig ) \
980 if( SHMIMMONITORT::loadConfig( cfig ) < 0 ) \
981 { \
982 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::loadConfig" } ); \
983 }
984
985/// Call shmimMonitorT::appStartup with error checking for shmimMonitor
986#define SHMIMMONITOR_APP_STARTUP \
987 if( shmimMonitorT::appStartup() < 0 ) \
988 { \
989 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::appStartup" } ); \
990 }
991
992/// Call shmimMonitorT::appStartup with error checking for a typedef-ed shmimMonitor
993/**
994 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
995 */
996#define SHMIMMONITORT_APP_STARTUP( SHMIMMONITORT ) \
997 if( SHMIMMONITORT::appStartup() < 0 ) \
998 { \
999 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appStartup" } ); \
1000 }
1001
1002/// Call shmimMonitorT::appLogic with error checking for shmimMonitor
1003#define SHMIMMONITOR_APP_LOGIC \
1004 if( shmimMonitorT::appLogic() < 0 ) \
1005 { \
1006 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::appLogic" } ); \
1007 }
1008
1009/// Call shmimMonitorT::appLogic with error checking for a typedef-ed shmimMonitor
1010/**
1011 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
1012 */
1013#define SHMIMMONITORT_APP_LOGIC( SHMIMMONITORT ) \
1014 if( SHMIMMONITORT::appLogic() < 0 ) \
1015 { \
1016 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appLogic" } ); \
1017 }
1018
1019/// Call shmimMonitorT::updateINDI with error checking for shmimMonitor
1020#define SHMIMMONITOR_UPDATE_INDI \
1021 if( shmimMonitorT::updateINDI() < 0 ) \
1022 { \
1023 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::updateINDI" } ); \
1024 }
1025
1026/// Call shmimMonitorT::updateINDI with error checking for a typedef-ed shmimMonitor
1027/**
1028 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
1029 */
1030#define SHMIMMONITORT_UPDATE_INDI( SHMIMMONITORT ) \
1031 if( SHMIMMONITORT::updateINDI() < 0 ) \
1032 { \
1033 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::updateINDI" } ); \
1034 }
1035
1036/// Call shmimMonitorT::appShutdown with error checking for shmimMonitor
1037#define SHMIMMONITOR_APP_SHUTDOWN \
1038 if( shmimMonitorT::appShutdown() < 0 ) \
1039 { \
1040 return log<software_error, -1>( { __FILE__, __LINE__, "Error from shmimMonitorT::appShutdown" } ); \
1041 }
1042
1043/// Call shmimMonitorT::appShutodwn with error checking for a typedef-ed shmimMonitor
1044/**
1045 * \param SHMIMMONITORT is the typedef-ed name of the shmimMonitor class, e.g. darkShmimMonitorT.
1046 */
1047#define SHMIMMONITORT_APP_SHUTDOWN( SHMIMMONITORT ) \
1048 if( SHMIMMONITORT::appShutdown() < 0 ) \
1049 { \
1050 return log<software_error, -1>( { __FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appShutdown" } ); \
1051 }
1052
1053} // namespace dev
1054} // namespace app
1055} // namespace MagAOX
1056#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.
stateCodes::stateCodeT m_targetState
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:18
Definition dm.hpp:19
static std::string configSection()
static std::string indiPrefix()
int16_t stateCodeT
The type of the state code.