Line data Source code
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 :
17 : namespace MagAOX
18 : {
19 : namespace app
20 : {
21 : namespace dev
22 : {
23 :
24 : enum class shmimMonitorState
25 : {
26 : init,
27 : notfound,
28 : connected
29 : };
30 :
31 : struct shmimT
32 : {
33 0 : static std::string configSection()
34 : {
35 0 : return "shmimMonitor";
36 : };
37 :
38 0 : static std::string indiPrefix()
39 : {
40 0 : 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 : */
110 : template <class derivedT, class specificT = shmimT>
111 : class shmimMonitor
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 :
130 : stateCodes::stateCodeT m_targetState{ stateCodes::OPERATING };
131 :
132 : shmimMonitorState m_smState{ shmimMonitorState::init };
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 : */
191 : int appStartup();
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 : */
215 : int appShutdown();
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
246 : void smThreadExec();
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 : */
284 : int updateINDI();
285 :
286 : ///@}
287 :
288 : private:
289 0 : derivedT &derived()
290 : {
291 0 : 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 :
299 : template <class derivedT, class specificT>
300 2 : const std::string &shmimMonitor<derivedT, specificT>::shmimName() const
301 : {
302 2 : return m_shmimName;
303 : }
304 :
305 : template <class derivedT, class specificT>
306 0 : const uint32_t &shmimMonitor<derivedT, specificT>::width() const
307 : {
308 0 : return m_width;
309 : }
310 :
311 : template <class derivedT, class specificT>
312 0 : const uint32_t &shmimMonitor<derivedT, specificT>::height() const
313 : {
314 0 : return m_height;
315 : }
316 :
317 : template <class derivedT, class specificT>
318 : const uint32_t &shmimMonitor<derivedT, specificT>::depth() const
319 : {
320 : return m_depth;
321 : }
322 :
323 : template <class derivedT, class specificT>
324 0 : const uint8_t &shmimMonitor<derivedT, specificT>::dataType() const
325 : {
326 0 : return m_dataType;
327 : }
328 :
329 : template <class derivedT, class specificT>
330 : const size_t &shmimMonitor<derivedT, specificT>::typeSize() const
331 : {
332 : return m_typeSize;
333 : }
334 :
335 : template <class derivedT, class specificT>
336 0 : int shmimMonitor<derivedT, specificT>::setupConfig( mx::app::appConfigurator &config )
337 : {
338 0 : 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 0 : 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 0 : 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 0 : 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 0 : m_shmimName = derived().configName();
380 :
381 0 : return 0;
382 : }
383 :
384 : template <class derivedT, class specificT>
385 0 : int shmimMonitor<derivedT, specificT>::loadConfig( mx::app::appConfigurator &config )
386 : {
387 0 : config( m_smThreadPrio, specificT::configSection() + ".threadPrio" );
388 0 : config( m_smCpuset, specificT::configSection() + ".cpuset" );
389 0 : config( m_shmimName, specificT::configSection() + ".shmimName" );
390 0 : config( m_getExistingFirst, specificT::configSection() + ".getExistingFirst" );
391 :
392 0 : return 0;
393 : }
394 :
395 : template <class derivedT, class specificT>
396 0 : int shmimMonitor<derivedT, specificT>::appStartup()
397 : {
398 : // Register the shmimName INDI property
399 0 : m_indiP_shmimName = pcf::IndiProperty( pcf::IndiProperty::Text );
400 0 : m_indiP_shmimName.setDevice( derived().configName() );
401 0 : m_indiP_shmimName.setName( specificT::indiPrefix() + "_shmimName" );
402 0 : m_indiP_shmimName.setPerm( pcf::IndiProperty::ReadOnly );
403 0 : m_indiP_shmimName.setState( pcf::IndiProperty::Idle );
404 0 : m_indiP_shmimName.add( pcf::IndiElement( "name" ) );
405 0 : m_indiP_shmimName["name"] = m_shmimName;
406 :
407 0 : if( derived().registerIndiPropertyNew( m_indiP_shmimName, nullptr ) < 0 )
408 : {
409 : #ifndef SHMIMMONITOR_TEST_NOLOG
410 0 : derivedT::template log<software_error>( { __FILE__, __LINE__ } );
411 : #endif
412 0 : return -1;
413 : }
414 :
415 : // Register the frameSize INDI property
416 0 : m_indiP_frameSize = pcf::IndiProperty( pcf::IndiProperty::Number );
417 0 : m_indiP_frameSize.setDevice( derived().configName() );
418 0 : m_indiP_frameSize.setName( specificT::indiPrefix() + "_frameSize" );
419 0 : m_indiP_frameSize.setPerm( pcf::IndiProperty::ReadOnly );
420 0 : m_indiP_frameSize.setState( pcf::IndiProperty::Idle );
421 0 : m_indiP_frameSize.add( pcf::IndiElement( "width" ) );
422 0 : m_indiP_frameSize["width"] = 0;
423 0 : m_indiP_frameSize.add( pcf::IndiElement( "height" ) );
424 0 : m_indiP_frameSize["height"] = 0;
425 :
426 0 : if( derived().registerIndiPropertyNew( m_indiP_frameSize, nullptr ) < 0 )
427 : {
428 : #ifndef SHMIMMONITOR_TEST_NOLOG
429 0 : derivedT::template log<software_error>( { __FILE__, __LINE__ } );
430 : #endif
431 0 : 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 0 : act.sa_sigaction = &sigUsr1Handler;
439 0 : act.sa_flags = SA_SIGINFO;
440 0 : sigemptyset( &set );
441 0 : act.sa_mask = set;
442 :
443 0 : errno = 0;
444 0 : if( sigaction( SIGUSR1, &act, 0 ) < 0 )
445 : {
446 0 : std::string logss = "Setting handler for SIGUSR1 failed. Errno says: ";
447 0 : logss += strerror( errno );
448 :
449 0 : derivedT::template log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
450 :
451 0 : return -1;
452 0 : }
453 :
454 0 : if( derived().threadStart( m_smThread,
455 0 : m_smThreadInit,
456 0 : m_smThreadID,
457 0 : m_smThreadProp,
458 : m_smThreadPrio,
459 0 : m_smCpuset,
460 : specificT::configSection(),
461 : this,
462 0 : smThreadStart ) < 0 )
463 : {
464 0 : derivedT::template log<software_error>( { __FILE__, __LINE__ } );
465 0 : return -1;
466 : }
467 :
468 0 : return 0;
469 : }
470 :
471 : template <class derivedT, class specificT>
472 0 : int shmimMonitor<derivedT, specificT>::appLogic()
473 : {
474 : // do a join check to see if other threads have exited.
475 0 : if( pthread_tryjoin_np( m_smThread.native_handle(), 0 ) == 0 )
476 : {
477 0 : derivedT::template log<software_error>(
478 : { __FILE__, __LINE__, "shmimMonitor thread " + std::to_string( m_smThreadID ) + " has exited" } );
479 :
480 0 : return -1;
481 : }
482 :
483 0 : return 0;
484 : }
485 :
486 : template <class derivedT, class specificT>
487 0 : int shmimMonitor<derivedT, specificT>::appShutdown()
488 : {
489 0 : if( m_smThread.joinable() )
490 : {
491 0 : pthread_kill( m_smThread.native_handle(), SIGUSR1 );
492 : try
493 : {
494 0 : m_smThread.join(); // this will throw if it was already joined
495 : }
496 0 : catch( ... )
497 : {
498 : }
499 : }
500 :
501 0 : return 0;
502 : }
503 :
504 : template <class derivedT, class specificT>
505 0 : void shmimMonitor<derivedT, specificT>::smThreadStart( shmimMonitor *s )
506 : {
507 0 : s->smThreadExec();
508 0 : }
509 :
510 : template <class derivedT, class specificT>
511 0 : void shmimMonitor<derivedT, specificT>::smThreadExec()
512 : {
513 0 : m_smThreadID = syscall( SYS_gettid );
514 :
515 : // Wait for the thread starter to finish initializing this thread.
516 0 : while( m_smThreadInit == true && derived().shutdown() == 0 )
517 : {
518 0 : sleep( 1 );
519 : }
520 :
521 0 : bool opened = false;
522 :
523 : // bool semgot = false;
524 :
525 0 : std::cerr << m_shmimName << " smThreadExec\n";
526 :
527 0 : while( derived().shutdown() == 0 )
528 : {
529 0 : m_smState = shmimMonitorState::init;
530 :
531 0 : while( ( derived().state() != m_targetState || m_shmimName == "" ) && !derived().shutdown() && !m_restart )
532 : {
533 0 : sleep( 1 );
534 : }
535 :
536 0 : if( derived().shutdown() )
537 : {
538 0 : return;
539 : }
540 :
541 : /* Initialize ImageStreamIO
542 : */
543 :
544 0 : opened = false;
545 0 : m_restart = false; // Set this up front, since we're about to restart.
546 :
547 0 : int logged = 0;
548 0 : 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 0 : ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
555 0 : SM_fd = open( SM_fname, O_RDWR );
556 0 : if( SM_fd == -1 )
557 : {
558 0 : m_smState = shmimMonitorState::notfound;
559 :
560 0 : if( !logged )
561 0 : derivedT::template log<text_log>(
562 0 : "ImageStream " + m_shmimName + " not found (yet). Retrying . . .", logPrio::LOG_NOTICE );
563 0 : logged = 1;
564 0 : sleep( 1 ); // be patient
565 0 : continue;
566 : }
567 :
568 0 : m_smState = shmimMonitorState::init; // Switch back to init if notfound
569 :
570 : // Found and opened, close it and then use ImageStreamIO
571 0 : logged = 0;
572 0 : close( SM_fd );
573 :
574 0 : if( ImageStreamIO_openIm( &m_imageStream, m_shmimName.c_str() ) == 0 )
575 : {
576 0 : if( m_imageStream.md[0].sem < SEMAPHORE_MAXVAL )
577 : {
578 0 : ImageStreamIO_closeIm( &m_imageStream );
579 0 : mx::sys::sleep( 1 ); // We just need to wait for the server process to finish startup.
580 : }
581 : else
582 : {
583 0 : opened = true;
584 : char SM_fname[200];
585 0 : ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
586 :
587 : struct stat buffer;
588 0 : int rv = stat( SM_fname, &buffer );
589 :
590 0 : if( rv != 0 )
591 : {
592 0 : derivedT::template log<software_critical>(
593 : { __FILE__,
594 : __LINE__,
595 0 : errno,
596 0 : "Could not get inode for " + m_shmimName +
597 : ". Source process will need to be restarted." } );
598 0 : ImageStreamIO_closeIm( &m_imageStream );
599 0 : return;
600 : }
601 :
602 0 : m_inode = buffer.st_ino;
603 : }
604 : }
605 : else
606 : {
607 0 : mx::sys::sleep( 1 ); // be patient
608 : }
609 : }
610 :
611 0 : if( m_restart )
612 : {
613 0 : continue;
614 : }
615 :
616 0 : if( derived().state() != m_targetState )
617 : {
618 0 : continue;
619 : }
620 :
621 0 : if( derived().m_shutdown )
622 : {
623 0 : if( !opened )
624 : {
625 0 : return;
626 : }
627 :
628 0 : ImageStreamIO_closeIm( &m_imageStream );
629 0 : return;
630 : }
631 :
632 0 : m_semaphoreNumber =
633 0 : ImageStreamIO_getsemwaitindex( &m_imageStream, m_semaphoreNumber ); // ask for semaphore we had before
634 :
635 0 : if( m_semaphoreNumber < 0 )
636 : {
637 0 : derivedT::template log<software_critical>(
638 : { __FILE__,
639 : __LINE__,
640 0 : "No valid semaphore found for " + m_shmimName + ". Source process will need to be restarted." } );
641 0 : return;
642 : }
643 :
644 0 : ImageStreamIO_semflush( &m_imageStream, m_semaphoreNumber );
645 :
646 0 : sem_t *sem = m_imageStream.semptr[m_semaphoreNumber]; ///< The semaphore to monitor for new image data
647 :
648 0 : m_dataType = m_imageStream.md[0].datatype;
649 0 : m_typeSize = ImageStreamIO_typesize( m_dataType );
650 0 : m_width = m_imageStream.md[0].size[0];
651 0 : int dim = 1;
652 0 : if( m_imageStream.md[0].naxis > 1 )
653 : {
654 0 : m_height = m_imageStream.md[0].size[1];
655 :
656 0 : if( m_imageStream.md[0].naxis > 2 )
657 : {
658 0 : dim = 3;
659 0 : m_depth = m_imageStream.md[0].size[2];
660 : }
661 : else
662 : {
663 0 : dim = 2;
664 0 : m_depth = 1;
665 : }
666 : }
667 : else
668 : {
669 0 : m_height = 1;
670 0 : m_depth = 1;
671 : }
672 :
673 0 : m_smState = shmimMonitorState::connected; // this means we now have valid sizes, etc.
674 :
675 0 : if( derived().allocate( specificT() ) < 0 )
676 : {
677 0 : derivedT::template log<software_error>( { __FILE__, __LINE__, "allocation failed" } );
678 0 : 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 0 : if( m_getExistingFirst && !m_restart && derived().shutdown() == 0 )
687 : {
688 0 : if( m_imageStream.md[0].size[2] > 0 ) ///\todo change to naxis?
689 : {
690 0 : curr_image = m_imageStream.md[0].cnt1;
691 : }
692 : else
693 : {
694 0 : curr_image = 0;
695 : }
696 :
697 0 : atype = m_imageStream.md[0].datatype;
698 0 : snx = m_imageStream.md[0].size[0];
699 :
700 0 : if( dim == 2 )
701 : {
702 0 : sny = m_imageStream.md[0].size[1];
703 0 : snz = 1;
704 : }
705 0 : else if( dim == 3 )
706 : {
707 0 : sny = m_imageStream.md[0].size[1];
708 0 : snz = m_imageStream.md[0].size[2];
709 : }
710 : else
711 : {
712 0 : sny = 1;
713 0 : snz = 1;
714 : }
715 :
716 0 : if( atype != m_dataType || snx != m_width || sny != m_height || snz != m_depth )
717 : {
718 0 : continue; // exit the nearest while loop and get the new image setup.
719 : }
720 :
721 0 : char *curr_src = (char *)m_imageStream.array.raw + curr_image * m_width * m_height * m_typeSize;
722 :
723 0 : if( derived().processImage( curr_src, specificT() ) < 0 )
724 : {
725 0 : derivedT::template log<software_error>( { __FILE__, __LINE__ } );
726 : }
727 : }
728 :
729 : // This is the main image grabbing loop.
730 0 : while( derived().shutdown() == 0 && !m_restart && derived().state() == m_targetState )
731 : {
732 :
733 : timespec ts;
734 :
735 0 : if( clock_gettime( CLOCK_REALTIME, &ts ) < 0 )
736 : {
737 0 : derivedT::template log<software_critical>( { __FILE__, __LINE__, errno, 0, "clock_gettime" } );
738 0 : return;
739 : }
740 :
741 0 : ts.tv_sec += 1;
742 :
743 0 : if( sem_timedwait( sem, &ts ) == 0 )
744 : {
745 0 : if( m_imageStream.md[0].size[2] > 0 ) ///\todo change to naxis?
746 : {
747 0 : curr_image = m_imageStream.md[0].cnt1;
748 : }
749 : else
750 : {
751 0 : curr_image = 0;
752 : }
753 :
754 0 : atype = m_imageStream.md[0].datatype;
755 0 : snx = m_imageStream.md[0].size[0];
756 :
757 0 : if( dim == 2 )
758 : {
759 0 : sny = m_imageStream.md[0].size[1];
760 0 : snz = 1;
761 : }
762 0 : else if( dim == 3 )
763 : {
764 0 : sny = m_imageStream.md[0].size[1];
765 0 : snz = m_imageStream.md[0].size[2];
766 : }
767 : else
768 : {
769 0 : sny = 1;
770 0 : snz = 1;
771 : }
772 :
773 0 : 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 0 : if( derived().shutdown() != 0 || m_restart || derived().state() != m_targetState )
779 : {
780 0 : break; // Check for exit signals
781 : }
782 :
783 0 : char *curr_src = (char *)m_imageStream.array.raw + curr_image * m_width * m_height * m_typeSize;
784 :
785 0 : if( derived().processImage( curr_src, specificT() ) < 0 )
786 : {
787 0 : derivedT::template log<software_error>( { __FILE__, __LINE__ } );
788 : }
789 : }
790 : else
791 : {
792 0 : if( m_imageStream.md[0].sem <= 0 )
793 0 : break; // Indicates that the server has cleaned up.
794 :
795 : // Check for why we timed out
796 0 : if( errno == EINTR )
797 0 : 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 0 : if( errno != ETIMEDOUT )
803 : {
804 0 : derivedT::template log<software_error>( { __FILE__, __LINE__, errno, "sem_timedwait" } );
805 0 : break;
806 : }
807 :
808 : // Check if the file has disappeared.
809 : int SM_fd;
810 : char SM_fname[200];
811 0 : ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
812 0 : SM_fd = open( SM_fname, O_RDWR );
813 0 : if( SM_fd == -1 )
814 : {
815 0 : m_restart = true;
816 : }
817 0 : close( SM_fd );
818 :
819 : // Check if the inode changed
820 : struct stat buffer;
821 0 : int rv = stat( SM_fname, &buffer );
822 0 : if( rv != 0 )
823 : {
824 0 : m_restart = true;
825 : }
826 :
827 0 : if( buffer.st_ino != m_inode )
828 : {
829 0 : m_restart = true;
830 : }
831 : }
832 : }
833 :
834 : //*******
835 : // call derived().cleanup()
836 : //*******
837 :
838 : // opened == true if we can get to this
839 0 : if( m_semaphoreNumber >= 0 )
840 0 : m_imageStream.semReadPID[m_semaphoreNumber] = 0; // release semaphore
841 0 : ImageStreamIO_closeIm( &m_imageStream );
842 0 : 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 0 : if( opened )
854 : {
855 0 : ImageStreamIO_closeIm( &m_imageStream );
856 0 : opened = false;
857 : }
858 : }
859 :
860 : template <class derivedT, class specificT>
861 0 : int shmimMonitor<derivedT, specificT>::create(
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 0 : ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
872 0 : SM_fd = open( SM_fname, O_RDWR );
873 0 : if( SM_fd != -1 )
874 : {
875 0 : close( SM_fd );
876 :
877 0 : if( ImageStreamIO_openIm( &m_imageStream, m_shmimName.c_str() ) != IMAGESTREAMIO_SUCCESS )
878 : {
879 0 : return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
880 : }
881 :
882 0 : if( ImageStreamIO_destroyIm( &m_imageStream ) != IMAGESTREAMIO_SUCCESS )
883 : {
884 0 : return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
885 : }
886 : }
887 :
888 0 : uint32_t imsize[3] = { 0, 0, 0 };
889 :
890 0 : imsize[0] = width;
891 0 : imsize[1] = height;
892 0 : imsize[2] = depth;
893 :
894 0 : std::cerr << "Creating: " << m_shmimName << " " << width << " " << height << " " << depth << "\n";
895 :
896 0 : 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 : 0 ) != IMAGESTREAMIO_SUCCESS )
907 : {
908 0 : return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
909 : }
910 :
911 0 : if( initData != nullptr )
912 : {
913 0 : memcpy( imageStream.array.raw, initData, width * height * ImageStreamIO_typesize( dtype ) );
914 : }
915 :
916 0 : imageStream.md->cnt1 = depth - 1;
917 :
918 0 : if( ImageStreamIO_closeIm( &imageStream ) != IMAGESTREAMIO_SUCCESS )
919 : {
920 0 : return derivedT::template log<software_error, -1>( { __FILE__, __LINE__, "error from ImageStreamIO" } );
921 : }
922 :
923 0 : return 0;
924 : }
925 :
926 : template <class derivedT, class specificT>
927 0 : int shmimMonitor<derivedT, specificT>::updateINDI()
928 : {
929 0 : if( !derived().m_indiDriver )
930 0 : return 0;
931 :
932 0 : indi::updateIfChanged( m_indiP_shmimName, "name", m_shmimName, derived().m_indiDriver );
933 0 : indi::updateIfChanged( m_indiP_frameSize, "width", m_width, derived().m_indiDriver );
934 0 : indi::updateIfChanged( m_indiP_frameSize, "height", m_height, derived().m_indiDriver );
935 :
936 0 : 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
|