LCOV - code coverage report
Current view: top level - libMagAOX/app/dev - shmimMonitor.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 0.8 % 260 2
Test Date: 2026-01-03 21:03:39 Functions: 6.2 % 16 1

            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
        

Generated by: LCOV version 2.0-1