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
24struct 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 */
96template <class derivedT, class specificT = shmimT>
98{
99
100protected:
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
127public:
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 */
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 */
197
198protected:
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
227
228 ///@}
229
230 /** \name INDI
231 *
232 *@{
233 */
234protected:
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
241public:
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 */
250
251 ///@}
252
253private:
254 derivedT &derived()
255 {
256 return *static_cast<derivedT *>(this);
257 }
258};
259
260// Set self pointer to null so app starts up uninitialized.
261template <class derivedT, class specificT>
262shmimMonitor<derivedT, specificT> *shmimMonitor<derivedT, specificT>::m_selfMonitor = nullptr;
263
264template <class derivedT, class specificT>
266{
267 return m_shmimName;
268}
269
270template <class derivedT, class specificT>
272{
273 return m_width;
274}
275
276template <class derivedT, class specificT>
278{
279 return m_height;
280}
281
282template <class derivedT, class specificT>
284{
285 return m_depth;
286}
287
288template <class derivedT, class specificT>
290{
291 return m_dataType;
292}
293
294template <class derivedT, class specificT>
296{
297 return m_typeSize;
298}
299
300template <class derivedT, class specificT>
301int 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
317template <class derivedT, class specificT>
318int 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
328template <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
396template <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
410template <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
428template <class derivedT, class specificT>
433
434template <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
749template <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.
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
Definition dm.hpp:24
static std::string configSection()
static std::string indiPrefix()