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