LCOV - code coverage report
Current view: top level - apps/ocam2KCtrl - ocam2KCtrl.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 95.2 % 691 658
Test Date: 2026-04-15 19:34:29 Functions: 100.0 % 42 42

            Line data    Source code
       1              : /** \file ocam2KCtrl.hpp
       2              :  * \brief The MagAO-X OCAM2K EMCCD camera controller.
       3              :  *
       4              :  * \ingroup ocam2KCtrl_files
       5              :  */
       6              : 
       7              : #ifndef ocam2KCtrl_hpp
       8              : #define ocam2KCtrl_hpp
       9              : 
      10              : #include <fcntl.h>
      11              : #include <edtinc.h>
      12              : #include <unistd.h>
      13              : 
      14              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      15              : #include "../../magaox_git_version.h"
      16              : 
      17              : typedef MagAOX::app::MagAOXApp<true> MagAOXAppT; // This needs to be before pdvUtils.hpp for logging to work.
      18              : 
      19              : #include "fli/ocam2_sdk.h"
      20              : #include "ocamUtils.hpp"
      21              : 
      22              : namespace MagAOX
      23              : {
      24              : namespace app
      25              : {
      26              : 
      27              : /** \defgroup ocam2KCtrl OCAM2K EMCCD Camera
      28              :  * \brief Control of the OCAM2K EMCCD Camera.
      29              :  *
      30              :  * <a href="../handbook/operating/software/apps/ocam2KCtrl.html">Application Documentation</a>
      31              :  *
      32              :  * \ingroup apps
      33              :  *
      34              :  */
      35              : 
      36              : /** \defgroup ocam2KCtrl_files OCAM2K EMCCD Camera Files
      37              :  * \ingroup ocam2KCtrl
      38              :  */
      39              : 
      40              : /** MagAO-X application to control the OCAM 2K EMCCD
      41              :  *
      42              :  * \ingroup ocam2KCtrl
      43              :  *
      44              :  */
      45              : class ocam2KCtrl : public MagAOXApp<>,
      46              :                    public dev::stdCamera<ocam2KCtrl>,
      47              :                    public dev::edtCamera<ocam2KCtrl>,
      48              :                    public dev::frameGrabber<ocam2KCtrl>,
      49              :                    public dev::dssShutter<ocam2KCtrl>,
      50              :                    public dev::telemeter<ocam2KCtrl>
      51              : {
      52              :     friend class dev::stdCamera<ocam2KCtrl>;
      53              :     friend class dev::edtCamera<ocam2KCtrl>;
      54              :     friend class dev::frameGrabber<ocam2KCtrl>;
      55              :     friend class dev::dssShutter<ocam2KCtrl>;
      56              :     friend class dev::telemeter<ocam2KCtrl>;
      57              : 
      58              :     typedef MagAOXApp<> MagAOXAppT;
      59              : 
      60              :   public:
      61              :     /** \name app::dev Configurations
      62              :      *@{
      63              :      */
      64              :     static constexpr bool c_stdCamera_tempControl =
      65              :         true; ///< app::dev config to tell stdCamera to expose temperature controls
      66              : 
      67              :     static constexpr bool c_stdCamera_temp =
      68              :         true; ///< app::dev config to tell stdCamera to expose temperature (ignored since tempControl==true)
      69              : 
      70              :     static constexpr bool c_stdCamera_readoutSpeed =
      71              :         false; ///< app::dev config to tell stdCamera not to expose readout speed controls
      72              : 
      73              :     static constexpr bool c_stdCamera_vShiftSpeed =
      74              :         false; ///< app:dev config to tell stdCamera not to expose vertical shift speed control
      75              :     static constexpr bool c_stdCamera_fanSpeed =
      76              :         false; ///< app::dev config to tell stdCamera not to expose fan-speed control
      77              : 
      78              :     static constexpr bool c_stdCamera_emGain = true; ///< app::dev config to tell stdCamera to expose EM gain controls
      79              : 
      80              :     static constexpr bool c_stdCamera_exptimeCtrl =
      81              :         false; ///< app::dev config to tell stdCamera not to expose exposure time controls
      82              : 
      83              :     static constexpr bool c_stdCamera_fpsCtrl = true; ///< app::dev config to tell stdCamera to expose FPS controls
      84              : 
      85              :     static constexpr bool c_stdCamera_fps =
      86              :         true; ///< app::dev config to tell stdCamera not to expose FPS status (ignored since fpsCtrl==true)
      87              : 
      88              :     static constexpr bool c_stdCamera_synchro =
      89              :         true; ///< app::dev config to tell stdCamera to expose synchro mode controls
      90              : 
      91              :     static constexpr bool c_stdCamera_usesModes =
      92              :         true; ///< app:dev config to tell stdCamera not to expose mode controls
      93              : 
      94              :     static constexpr bool c_stdCamera_usesROI = false; ///< app:dev config to tell stdCamera to expose ROI controls
      95              : 
      96              :     static constexpr bool c_stdCamera_cropMode =
      97              :         false; ///< app:dev config to tell stdCamera to expose Crop Mode controls
      98              : 
      99              :     static constexpr bool c_stdCamera_hasShutter =
     100              :         true; ///< app:dev config to tell stdCamera to expose shutter controls
     101              : 
     102              :     static constexpr bool c_stdCamera_usesStateString =
     103              :         true; ///< app::dev confg to tell stdCamera to expose the state string property
     104              : 
     105              :     static constexpr bool c_edtCamera_relativeConfigPath =
     106              :         true; ///< app::dev config to tell edtCamera to use relative path to camera config file
     107              : 
     108              :     static constexpr bool c_frameGrabber_flippable =
     109              :         false; ///< app:dev config to tell framegrabber these images can not be flipped
     110              : 
     111              :     ///@}
     112              :   protected:
     113              :     /** \name configurable parameters
     114              :      *@{
     115              :      */
     116              : 
     117              :     std::string m_ocamDescrambleFile; ///< Path to the OCAM 2K pixel descrambling file, relative to the MagAO-X config
     118              :                                       ///< directory.
     119              : 
     120              :     std::string m_syncShmimName; ///< Name of the sync-only ImageStreamIO output. Defaults to `framegrabber.shmimName +
     121              :                                  ///< "_sync"`.
     122              : 
     123              :     ///@}
     124              : 
     125              :     static constexpr uint32_t c_syncStreamWidth{ 1 };  ///< Width of the sync-only ImageStreamIO stream.
     126              :     static constexpr uint32_t c_syncStreamHeight{ 1 }; ///< Height of the sync-only ImageStreamIO stream.
     127              :     static constexpr uint32_t c_syncStreamDepth{ 1 };  ///< Depth of the sync-only ImageStreamIO stream.
     128              :     static constexpr uint8_t  c_syncStreamDataType{ _DATATYPE_UINT8 }; ///< Data type of the sync-only stream.
     129              : 
     130              :     ocam2_id m_ocam2_id{ 0 }; ///< OCAM SDK id.
     131              : 
     132              :     long m_currImageNumber{ -1 }; ///< The current image number, retrieved from the raw image itself.
     133              : 
     134              :     long m_lastImageNumber{ -1 }; ///< The last image number saved from the previous acquisition loop.
     135              : 
     136              :     bool m_protectionReset{ false }; ///< Flag indicating that protection has been reset at least once.
     137              : 
     138              :     unsigned m_protectionResetConfirmed{ 0 }; ///< Counter indicating the number of times that the protection reset has
     139              :                                               ///< been requested within 10 seconds, for confirmation.
     140              : 
     141              :     double m_protectionResetReqTime{
     142              :         0 }; ///< The time at which protection reset was requested.  You have 10 seconds to confirm.
     143              : 
     144              :     bool m_poweredOn{
     145              :         false }; ///< Tracks that power-on defaults still need to restore the configured temperature setpoint.
     146              : 
     147              :     ocamTemps m_temps; ///< Structure holding the last temperature measurement.
     148              : 
     149              :     unsigned m_digitalBinX{ 1 }; ///< Digital x-binning factor applied after descrambling.
     150              : 
     151              :     unsigned m_digitalBinY{ 1 }; ///< Digital y-binning factor applied after descrambling.
     152              : 
     153              :     bool m_digitalBin{ false }; ///< Indicates whether post-descramble digital binning is active for the current mode.
     154              : 
     155              :     mx::improc::eigenImage<int16_t>
     156              :         m_digitalBinWork; ///< Scratch image holding the full descrambled frame before digital binning.
     157              : 
     158              :     std::string m_syncDevice{
     159              :         "fxngensync" }; ///< INDI device providing the external sync frequency when synchro mode is enabled.
     160              :     std::string m_syncFreqProp{ "C1freq" }; ///< INDI property name reporting the external sync frequency.
     161              : 
     162              :     float m_syncFreq{ 0 }; ///< Current externally supplied sync frequency in Hz.
     163              : 
     164              :     IMAGE *m_syncImageStream{ nullptr }; ///< Secondary 1x1 uint8 ImageStreamIO stream used only for frame metadata
     165              :                                          ///< and semaphores. Its lifetime is sequenced by the framegrabber thread and
     166              :                                          ///< app shutdown so per-frame publication can remain lock-free.
     167              : 
     168              :     std::recursive_mutex
     169              :         m_cameraMutex; ///< Protects EDT PDV access and OCAM SDK lifetime across reconfigure, grab, and control paths.
     170              : 
     171              :   public:
     172              :     /// Default c'tor
     173              :     ocam2KCtrl();
     174              : 
     175              :     /// Destructor
     176              :     ~ocam2KCtrl() noexcept;
     177              : 
     178              :     /// Setup the configuration system (called by MagAOXApp::setup())
     179              :     virtual void setupConfig();
     180              : 
     181              :     /// load the configuration system results (called by MagAOXApp::setup())
     182              :     virtual void loadConfig();
     183              : 
     184              :     /// Startup functions
     185              :     /** Sets up the INDI vars, and the f.g. thread.
     186              :      *
     187              :      */
     188              :     virtual int appStartup();
     189              : 
     190              :     /// Implementation of the FSM for the OCAM 2K.
     191              :     virtual int appLogic();
     192              : 
     193              :     /// Implementation of the on-power-off FSM logic
     194              :     virtual int onPowerOff();
     195              : 
     196              :     /// Implementation of the while-powered-off FSM
     197              :     virtual int whilePowerOff();
     198              : 
     199              :     /// Do any needed shutdown tasks.
     200              :     virtual int appShutdown();
     201              : 
     202              :     /// Get the current device temperatures
     203              :     /**
     204              :      * \returns 0 on success
     205              :      * \returns -1 on error
     206              :      */
     207              :     int getTemps();
     208              : 
     209              :     /// Get the current frame rate.
     210              :     /**
     211              :      * \returns 0 on success
     212              :      * \returns -1 on error
     213              :      */
     214              :     int getFPS();
     215              : 
     216              :     /** \name stdCamera Interface
     217              :      *
     218              :      * @{
     219              :      */
     220              : 
     221              :     /// Set defaults for a power on state.
     222              :     /**
     223              :      * \returns 0 on success
     224              :      * \returns -1 on error
     225              :      */
     226              :     int powerOnDefaults();
     227              : 
     228              :     /// Turn temperature control on or off.
     229              :     /** Sets temperature control on or off based on the current value of m_tempControlStatus
     230              :      * \returns 0 on success
     231              :      * \returns -1 on error
     232              :      */
     233              :     int setTempControl();
     234              : 
     235              :     /// Set the CCD temperature setpoint [stdCamera interface].
     236              :     /** Sets the temperature to m_ccdTempSetpt.
     237              :      * \returns 0 on success
     238              :      * \returns -1 on error
     239              :      */
     240              :     int setTempSetPt();
     241              : 
     242              :     /// Set the frame rate. [stdCamera interface]
     243              :     /** Sets the frame rate to m_fpsSet.
     244              :      *
     245              :      * \returns 0 on success
     246              :      * \returns -1 on error
     247              :      */
     248              :     int setFPS();
     249              : 
     250              :     /// Set the synchro state. [stdCamera interface]
     251              :     /** Sets the synchro state to m_synchroSet.
     252              :      *
     253              :      * \returns 0 on success
     254              :      * \returns -1 on error
     255              :      */
     256              :     int setSynchro();
     257              : 
     258              :     /// Required by stdCamera, but this does not do anything for this camera [stdCamera interface]
     259              :     /**
     260              :      * \returns 0 always
     261              :      */
     262              :     int setExpTime();
     263              : 
     264              :     /// Required by stdCamera, but this does not do anything for this camera [stdCamera interface]
     265              :     /**
     266              :      * \returns 0 always
     267              :      */
     268              :     int setNextROI();
     269              : 
     270              :     /// Sets the shutter state, via call to dssShutter::setShutterState(int) [stdCamera interface]
     271              :     /**
     272              :      * \returns 0 always
     273              :      */
     274              :     int setShutter( int sh /**< [in] requested shutter state */ );
     275              : 
     276              :     /// Return the current stdCamera state string.
     277              :     std::string stateString();
     278              : 
     279              :     /// Report whether the current stdCamera state string is valid for persistence and telemetry.
     280              :     bool stateStringValid();
     281              : 
     282              :     ///@}
     283              : 
     284              :     /// Reset the EM Protection
     285              :     /**
     286              :      * \returns 0 on success
     287              :      * \returns -1 on error
     288              :      */
     289              :     int resetEMProtection();
     290              : 
     291              :     /// Get the current EM Gain.
     292              :     /**
     293              :      * \returns 0 on success
     294              :      * \returns -1 on error
     295              :      */
     296              :     int getEMGain();
     297              : 
     298              :     /// Set the EM gain.
     299              :     /** Sets it to the value of stdCamera::m_emGainSet
     300              :      *
     301              :      * \returns 0 on success
     302              :      * \returns -1 on error
     303              :      */
     304              :     int setEMGain();
     305              : 
     306              :     /// Implementation of the framegrabber configureAcquisition interface
     307              :     /** Sends the mode command over serial, sets the FPS, and initializes the OCAM SDK.
     308              :      *
     309              :      * \returns 0 on success
     310              :      * \returns -1 on error
     311              :      */
     312              :     int configureAcquisition();
     313              : 
     314              :     /// Implementation of the frameGrabber fps interface
     315              :     /** Just returns the value of m_fps
     316              :      */
     317              :     float fps();
     318              : 
     319              :     /// Implementation of the framegrabber startAcquisition interface
     320              :     /** Initializes m_lastImageNumber, and calls edtCamera::pdvStartAcquisition
     321              :      *
     322              :      * \returns 0 on success
     323              :      * \returns -1 on error
     324              :      */
     325              :     int startAcquisition();
     326              : 
     327              :     /// Implementation of the framegrabber acquireAndCheckValid interface
     328              :     /** Calls edtCamera::pdvAcquire, then analyzes the OCAM generated framenumber for skips and corruption.
     329              :      *
     330              :      * \returns 0 on success
     331              :      * \returns -1 on error
     332              :      */
     333              :     int acquireAndCheckValid();
     334              : 
     335              :     /// Implementation of the framegrabber loadImageIntoStream interface
     336              :     /** Conducts the OCAM descramble.
     337              :      *
     338              :      * \returns 0 on success
     339              :      * \returns -1 on error
     340              :      */
     341              :     int loadImageIntoStream( void *dest /**< [in] destination image buffer */ );
     342              : 
     343              :     /// Implementation of the framegrabber reconfig interface
     344              :     /** Locks the INDI mutex and calls edtCamera::pdvReconfig.
     345              :      * \returns 0 on success
     346              :      * \returns -1 on error
     347              :      */
     348              :     int reconfig();
     349              : 
     350              :     /// Publish the sync-only stream immediately after the main framegrabber publication.
     351              :     /** This hot path relies on framegrabber sequencing: configureAcquisition() and this hook both run on the
     352              :      * framegrabber thread, while appShutdown() destroys the sync stream only after frameGrabber::appShutdown()
     353              :      * joins that thread.
     354              :      */
     355              :     int frameGrabberPostPublish( IMAGE *imageStream /**< [in] main framegrabber stream that was just posted */ );
     356              : 
     357              :   protected:
     358              :     /// Ensure the sync-only ImageStreamIO stream exists with the expected 1x1 uint8 layout.
     359              :     int ensureSyncStream();
     360              : 
     361              :     /// Destroy the sync-only ImageStreamIO stream owned by this app.
     362              :     int destroySyncStream();
     363              : 
     364              :     // INDI:
     365              :   protected:
     366              :     // declare our properties
     367              :     pcf::IndiProperty m_indiP_temps;       ///< INDI property publishing the latest camera temperatures.
     368              :     pcf::IndiProperty m_indiP_emProt;      ///< INDI property publishing the EM protection state.
     369              :     pcf::IndiProperty m_indiP_emProtReset; ///< INDI property handling operator requests to reset EM protection.
     370              : 
     371              :     pcf::IndiProperty m_indiP_syncFreq; ///< INDI subscription to the external sync-frequency source.
     372              : 
     373              :   public:
     374            1 :     INDI_NEWCALLBACK_DECL( ocam2KCtrl, m_indiP_emProtReset );
     375              : 
     376            1 :     INDI_SETCALLBACK_DECL( ocam2KCtrl, m_indiP_syncFreq );
     377              : 
     378              :     /** \name Telemeter Interface
     379              :      *
     380              :      * @{
     381              :      */
     382              :     int checkRecordTimes();
     383              : 
     384              :     int recordTelem( const ocam_temps * );
     385              : 
     386              :     int recordTelem( const telem_stdcam * );
     387              : 
     388              :     int recordTelem( const telem_fgtimings * );
     389              : 
     390              :     int
     391              :     recordTemps( bool force = false /**< [in] whether to force a telemetry record even if values have not changed */ );
     392              : 
     393              :     ///@}
     394              : };
     395              : 
     396          777 : inline ocam2KCtrl::ocam2KCtrl() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     397              : {
     398              :     //--- MagAOXApp Power Mgt. ---
     399          111 :     m_powerMgtEnabled = true;
     400          111 :     m_powerOnWait     = 10;
     401              : 
     402              :     //--- stdCamera ---
     403          111 :     m_startupTemp = 20;
     404              : 
     405          111 :     m_maxEMGain = 600;
     406              : 
     407          111 :     return;
     408            0 : }
     409              : 
     410          110 : inline ocam2KCtrl::~ocam2KCtrl() noexcept
     411              : {
     412          110 :     return;
     413          110 : }
     414              : 
     415            4 : inline void ocam2KCtrl::setupConfig()
     416              : {
     417            4 :     dev::stdCamera<ocam2KCtrl>::setupConfig( config );
     418              : 
     419            4 :     dev::edtCamera<ocam2KCtrl>::setupConfig( config );
     420              : 
     421           56 :     config.add( "camera.ocamDescrambleFile",
     422              :                 "",
     423              :                 "camera.ocamDescrambleFile",
     424              :                 argType::Required,
     425              :                 "camera",
     426              :                 "ocamDescrambleFile",
     427              :                 false,
     428              :                 "string",
     429              :                 "The path of the OCAM descramble file, relative to MagAOX/config." );
     430              : 
     431            4 :     dev::frameGrabber<ocam2KCtrl>::setupConfig( config );
     432              : 
     433           56 :     config.add( "framegrabber.syncShmimName",
     434              :                 "",
     435              :                 "framegrabber.syncShmimName",
     436              :                 argType::Required,
     437              :                 "framegrabber",
     438              :                 "syncShmimName",
     439              :                 false,
     440              :                 "string",
     441              :                 "The name of the 1x1 uint8 ImageStreamIO sync stream used only for frame metadata and semaphores. "
     442              :                 "Defaults to framegrabber.shmimName + \"_sync\"." );
     443              : 
     444            4 :     dev::dssShutter<ocam2KCtrl>::setupConfig( config );
     445              : 
     446            4 :     dev::telemeter<ocam2KCtrl>::setupConfig( config );
     447            4 : }
     448              : 
     449            4 : inline void ocam2KCtrl::loadConfig()
     450              : {
     451            4 :     dev::stdCamera<ocam2KCtrl>::loadConfig( config );
     452            4 :     dev::edtCamera<ocam2KCtrl>::loadConfig( config );
     453              : 
     454            8 :     config( m_ocamDescrambleFile, "camera.ocamDescrambleFile" );
     455              : 
     456            4 :     if( m_maxEMGain < 1 )
     457              :     {
     458            1 :         m_maxEMGain = 1;
     459            1 :         log<text_log>( "maxEMGain set to 1" );
     460              :     }
     461              : 
     462            4 :     if( m_maxEMGain > 600 )
     463              :     {
     464            1 :         m_maxEMGain = 600;
     465            1 :         log<text_log>( "maxEMGain set to 600" );
     466              :     }
     467              : 
     468            4 :     dev::frameGrabber<ocam2KCtrl>::loadConfig( config );
     469            8 :     config( m_syncShmimName, "framegrabber.syncShmimName" );
     470            4 :     if( m_syncShmimName == "" )
     471              :     {
     472            1 :         m_syncShmimName = m_shmimName + "_sync";
     473              :     }
     474            4 :     dev::dssShutter<ocam2KCtrl>::loadConfig( config );
     475            4 :     dev::telemeter<ocam2KCtrl>::loadConfig( config );
     476            4 : }
     477              : 
     478           14 : inline int ocam2KCtrl::ensureSyncStream()
     479              : {
     480           14 :     if( m_syncImageStream != nullptr )
     481              :     {
     482            2 :         uint32_t syncWidth  = m_syncImageStream->md[0].size[0];
     483            2 :         uint32_t syncHeight = 1;
     484            2 :         uint32_t syncDepth  = 1;
     485              : 
     486            2 :         if( m_syncImageStream->md[0].naxis > 1 )
     487              :         {
     488            2 :             syncHeight = m_syncImageStream->md[0].size[1];
     489              :         }
     490              : 
     491            2 :         if( m_syncImageStream->md[0].naxis > 2 )
     492              :         {
     493            2 :             syncDepth = m_syncImageStream->md[0].size[2];
     494              :         }
     495              : 
     496            2 :         if( m_syncImageStream->md[0].datatype == c_syncStreamDataType && syncWidth == c_syncStreamWidth &&
     497            1 :             syncHeight == c_syncStreamHeight && syncDepth == c_syncStreamDepth )
     498              :         {
     499            1 :             return 0;
     500              :         }
     501              : 
     502            1 :         ImageStreamIO_destroyIm( m_syncImageStream );
     503            1 :         free( m_syncImageStream );
     504            1 :         m_syncImageStream = nullptr;
     505              :     }
     506              : 
     507              :     char syncFileName[1024];
     508           13 :     ImageStreamIO_filename( syncFileName, sizeof( syncFileName ), m_syncShmimName.c_str() );
     509              : 
     510           13 :     int syncFd = ::open( syncFileName, O_RDWR );
     511           13 :     if( syncFd != -1 )
     512              :     {
     513            2 :         ::close( syncFd );
     514              : 
     515              :         IMAGE staleSyncStream;
     516            2 :         if( ImageStreamIO_openIm( &staleSyncStream, m_syncShmimName.c_str() ) != IMAGESTREAMIO_SUCCESS )
     517              :         {
     518            3 :             return log<software_error, -1>(
     519            1 :                 { __FILE__, __LINE__, "error opening existing sync ImageStreamIO stream" } );
     520              :         }
     521              : 
     522            1 :         if( ImageStreamIO_destroyIm( &staleSyncStream ) != IMAGESTREAMIO_SUCCESS )
     523              :         {
     524            0 :             return log<software_error, -1>(
     525            0 :                 { __FILE__, __LINE__, "error destroying existing sync ImageStreamIO stream" } );
     526              :         }
     527              :     }
     528              : 
     529           12 :     m_syncImageStream = reinterpret_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
     530           12 :     if( m_syncImageStream == nullptr )
     531              :     {
     532            0 :         return log<software_error, -1>( { __FILE__, __LINE__, "error allocating sync ImageStreamIO stream" } );
     533              :     }
     534              : 
     535           12 :     uint32_t imsize[3] = { c_syncStreamWidth, c_syncStreamHeight, c_syncStreamDepth };
     536              : 
     537           12 :     if( ImageStreamIO_createIm_gpu( m_syncImageStream,
     538              :                                     m_syncShmimName.c_str(),
     539              :                                     3,
     540              :                                     imsize,
     541              :                                     c_syncStreamDataType,
     542              :                                     -1,
     543              :                                     1,
     544              :                                     IMAGE_NB_SEMAPHORE,
     545              :                                     0,
     546              :                                     CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
     547           12 :                                     0 ) != IMAGESTREAMIO_SUCCESS )
     548              :     {
     549            0 :         free( m_syncImageStream );
     550            0 :         m_syncImageStream = nullptr;
     551            0 :         return log<software_error, -1>( { __FILE__, __LINE__, "error creating sync ImageStreamIO stream" } );
     552              :     }
     553              : 
     554           12 :     m_syncImageStream->array.UI8[0]      = 0;
     555           12 :     m_syncImageStream->md[0].cnt0        = 0;
     556           12 :     m_syncImageStream->md[0].cnt1        = 0;
     557           12 :     m_syncImageStream->md[0].write       = 0;
     558           12 :     m_syncImageStream->writetimearray[0] = m_syncImageStream->md[0].writetime;
     559           12 :     m_syncImageStream->atimearray[0]     = m_syncImageStream->md[0].atime;
     560           12 :     m_syncImageStream->cntarray[0]       = 0;
     561              : 
     562           12 :     return 0;
     563              : }
     564              : 
     565          112 : inline int ocam2KCtrl::destroySyncStream()
     566              : {
     567          112 :     if( m_syncImageStream == nullptr )
     568              :     {
     569          100 :         return 0;
     570              :     }
     571              : 
     572           12 :     int rv = 0;
     573              : 
     574           12 :     if( ImageStreamIO_destroyIm( m_syncImageStream ) != IMAGESTREAMIO_SUCCESS )
     575              :     {
     576            0 :         rv = log<software_error, -1>( { __FILE__, __LINE__, "error destroying sync ImageStreamIO stream" } );
     577              :     }
     578              : 
     579           12 :     free( m_syncImageStream );
     580           12 :     m_syncImageStream = nullptr;
     581              : 
     582           12 :     return rv;
     583              : }
     584              : 
     585            3 : inline int ocam2KCtrl::appStartup()
     586              : {
     587            9 :     REG_INDI_NEWPROP_NOCB( m_indiP_temps, "temps", pcf::IndiProperty::Number );
     588            6 :     m_indiP_temps.add( pcf::IndiElement( "cpu" ) );
     589            6 :     m_indiP_temps["cpu"].set( 0 );
     590            6 :     m_indiP_temps.add( pcf::IndiElement( "power" ) );
     591            6 :     m_indiP_temps["power"].set( 0 );
     592            6 :     m_indiP_temps.add( pcf::IndiElement( "bias" ) );
     593            6 :     m_indiP_temps["bias"].set( 0 );
     594            6 :     m_indiP_temps.add( pcf::IndiElement( "water" ) );
     595            6 :     m_indiP_temps["water"].set( 0 );
     596            6 :     m_indiP_temps.add( pcf::IndiElement( "left" ) );
     597            6 :     m_indiP_temps["left"].set( 0 );
     598            6 :     m_indiP_temps.add( pcf::IndiElement( "right" ) );
     599            6 :     m_indiP_temps["right"].set( 0 );
     600            6 :     m_indiP_temps.add( pcf::IndiElement( "cooling" ) );
     601            6 :     m_indiP_temps["cooling"].set( 0 );
     602              : 
     603            9 :     REG_INDI_NEWPROP_NOCB( m_indiP_emProt, "emProtection", pcf::IndiProperty::Text );
     604            6 :     m_indiP_emProt.add( pcf::IndiElement( "status" ) );
     605            6 :     m_indiP_emProt["status"].set( "UNKNOWN" );
     606            3 :     m_indiP_emProt.setState( INDI_IDLE );
     607              : 
     608           18 :     createStandardIndiRequestSw( m_indiP_emProtReset, "emProtectionReset", "Reset", "EM Protection" );
     609            3 :     registerIndiPropertyNew( m_indiP_emProtReset, INDI_NEWCALLBACK( m_indiP_emProtReset ) );
     610              : 
     611            3 :     REG_INDI_SETPROP( m_indiP_syncFreq, m_syncDevice, m_syncFreqProp );
     612              : 
     613            3 :     if( dev::stdCamera<ocam2KCtrl>::appStartup() < 0 )
     614              :     {
     615            1 :         return log<software_critical, -1>( { "" } );
     616              :     }
     617              : 
     618            2 :     if( dev::edtCamera<ocam2KCtrl>::appStartup() < 0 )
     619              :     {
     620            1 :         return log<software_critical, -1>( { "" } );
     621              :     }
     622              : 
     623            1 :     if( dev::frameGrabber<ocam2KCtrl>::appStartup() < 0 )
     624              :     {
     625            0 :         return log<software_critical, -1>( { "" } );
     626              :     }
     627              : 
     628            1 :     if( dev::dssShutter<ocam2KCtrl>::appStartup() < 0 )
     629              :     {
     630            0 :         return log<software_critical, -1>( { "" } );
     631              :     }
     632              : 
     633            1 :     m_temps.setInvalid();
     634            1 :     if( dev::telemeter<ocam2KCtrl>::appStartup() < 0 )
     635              :     {
     636            0 :         return log<software_error, -1>( { "" } );
     637              :     }
     638              : 
     639            1 :     return 0;
     640              : }
     641              : 
     642           20 : inline int ocam2KCtrl::appLogic()
     643              : {
     644              : 
     645              :     // and run stdCamera's appLogic
     646           20 :     if( dev::stdCamera<ocam2KCtrl>::appLogic() < 0 )
     647              :     {
     648            0 :         return log<software_error, -1>( { "" } );
     649              :     }
     650              : 
     651              :     // and run edtCamera's appLogic
     652           20 :     if( dev::edtCamera<ocam2KCtrl>::appLogic() < 0 )
     653              :     {
     654            0 :         return log<software_error, -1>( { "" } );
     655              :     }
     656              : 
     657              :     // first run frameGrabber's appLogic to see if the f.g. thread has exited.
     658           20 :     if( dev::frameGrabber<ocam2KCtrl>::appLogic() < 0 )
     659              :     {
     660            1 :         return log<software_error, -1>( { "" } );
     661              :     }
     662              : 
     663              :     // and run dssShutter's appLogic
     664           19 :     if( dev::dssShutter<ocam2KCtrl>::appLogic() < 0 )
     665              :     {
     666            0 :         return log<software_error, -1>( { "" } );
     667              :     }
     668              : 
     669           19 :     if( state() == stateCodes::POWERON )
     670            1 :         return 0;
     671              : 
     672           18 :     if( state() == stateCodes::NOTCONNECTED || state() == stateCodes::ERROR )
     673              :     {
     674            3 :         m_temps.setInvalid();
     675              : 
     676            3 :         std::string response;
     677              : 
     678              :         // Might have gotten here because of a power off.
     679            3 :         if( MagAOXAppT::m_powerState == 0 )
     680            1 :             return 0;
     681              : 
     682            2 :         int ret = 0;
     683              :         { // mutex scope
     684            2 :             std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
     685            4 :             ret = pdvSerialWriteRead( response, "fps" ); // m_pdv, "fps", m_readTimeout);
     686            2 :         }
     687            2 :         if( ret == 0 )
     688              :         {
     689            1 :             state( stateCodes::CONNECTED );
     690              :         }
     691              :         else
     692              :         {
     693            1 :             sleep( 1 );
     694            1 :             return 0;
     695              :         }
     696            3 :     }
     697              : 
     698           16 :     if( state() == stateCodes::CONNECTED )
     699              :     {
     700              :         // Get a lock
     701            7 :         std::unique_lock<std::mutex> lock( m_indiMutex );
     702              : 
     703            7 :         if( getFPS() == 0 )
     704              :         {
     705            5 :             if( m_fpsSet == 0 )
     706            4 :                 state( stateCodes::READY );
     707              :             else
     708            1 :                 state( stateCodes::OPERATING );
     709              : 
     710            5 :             if( m_poweredOn && m_ccdTempSetpt > -999 )
     711              :             {
     712            3 :                 m_poweredOn = false;
     713            3 :                 if( setTempSetPt() < 0 )
     714              :                 {
     715            2 :                     if( powerState() != 1 || powerStateTarget() != 1 )
     716              :                     {
     717            1 :                         return 0;
     718              :                     }
     719            1 :                     return log<software_error, 0>( { "" } );
     720              :                 }
     721              :             }
     722              : 
     723              :             // We always have to set synchro on connecting, b/c otherwise we don't know the state.
     724            3 :             m_synchroSet = false;
     725            3 :             if( setSynchro() != 0 )
     726              :             {
     727            1 :                 log<software_error>( { "error from setSynchro on CONNECT" } );
     728              :             }
     729              :         }
     730              :         else
     731              :         {
     732            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
     733            1 :                 return 0;
     734            1 :             state( stateCodes::ERROR );
     735            1 :             return log<software_error, 0>( { "" } );
     736              :         }
     737            7 :     }
     738              : 
     739           12 :     if( state() == stateCodes::READY || state() == stateCodes::OPERATING )
     740              :     {
     741              :         // Get a lock if we can
     742           11 :         std::unique_lock<std::mutex> lock( m_indiMutex, std::try_to_lock );
     743              : 
     744              :         // but don't wait for it, just go back around.
     745           11 :         if( !lock.owns_lock() )
     746            1 :             return 0;
     747              : 
     748           10 :         if( getTemps() < 0 )
     749              :         {
     750            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
     751            1 :                 return 0;
     752            1 :             m_temps.setInvalid();
     753            1 :             state( stateCodes::ERROR );
     754            1 :             return 0;
     755              :         }
     756              : 
     757            8 :         if( getFPS() < 0 )
     758              :         {
     759            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
     760            1 :                 return 0;
     761              : 
     762            1 :             state( stateCodes::ERROR );
     763            1 :             return 0;
     764              :         }
     765              : 
     766            6 :         if( m_protectionResetConfirmed > 0 )
     767              :         {
     768            2 :             if( mx::sys::get_curr_time() - m_protectionResetReqTime > 10.0 )
     769              :             {
     770            2 :                 m_protectionResetConfirmed = 0;
     771            8 :                 updateIfChanged( m_indiP_emProt, "status", std::string( "UNCONFIRMED" ) );
     772            2 :                 log<text_log>( "protection reset request not confirmed", logPrio::LOG_NOTICE );
     773              :             }
     774              :         }
     775              : 
     776            6 :         if( getEMGain() < 0 )
     777              :         {
     778            2 :             if( MagAOXAppT::m_powerState == 0 )
     779            1 :                 return 0;
     780              : 
     781            1 :             state( stateCodes::ERROR );
     782            1 :             return 0;
     783              :         }
     784              : 
     785            4 :         if( frameGrabber<ocam2KCtrl>::updateINDI() < 0 )
     786              :         {
     787            0 :             log<software_error>( { "" } );
     788            0 :             state( stateCodes::ERROR );
     789            0 :             return 0;
     790              :         }
     791              : 
     792            4 :         if( stdCamera<ocam2KCtrl>::updateINDI() < 0 )
     793              :         {
     794            0 :             log<software_error>( { "" } );
     795            0 :             state( stateCodes::ERROR );
     796            0 :             return 0;
     797              :         }
     798              : 
     799            4 :         if( edtCamera<ocam2KCtrl>::updateINDI() < 0 )
     800              :         {
     801            0 :             log<software_error>( { "" } );
     802            0 :             state( stateCodes::ERROR );
     803            0 :             return 0;
     804              :         }
     805              : 
     806            4 :         if( telemeter<ocam2KCtrl>::appLogic() < 0 )
     807              :         {
     808            4 :             log<software_error>( { "" } );
     809            4 :             return 0;
     810              :         }
     811           11 :     }
     812              : 
     813              :     ///\todo Fall through check?
     814              : 
     815            1 :     return 0;
     816              : }
     817              : 
     818            1 : inline int ocam2KCtrl::onPowerOff()
     819              : {
     820            1 :     m_powerOnCounter = 0;
     821              : 
     822            1 :     std::lock_guard<std::mutex> lock( m_indiMutex );
     823              : 
     824            4 :     updateIfChanged( m_indiP_emProt, "status", std::string( "UNKNOWN" ), INDI_IDLE );
     825              : 
     826            1 :     m_temps.setInvalid();
     827              : 
     828            2 :     updateIfChanged( m_indiP_temps, "cpu", m_temps.CPU );
     829            2 :     updateIfChanged( m_indiP_temps, "power", m_temps.POWER );
     830            2 :     updateIfChanged( m_indiP_temps, "bias", m_temps.BIAS );
     831            2 :     updateIfChanged( m_indiP_temps, "water", m_temps.WATER );
     832            2 :     updateIfChanged( m_indiP_temps, "left", m_temps.LEFT );
     833            2 :     updateIfChanged( m_indiP_temps, "right", m_temps.RIGHT );
     834            2 :     updateIfChanged( m_indiP_temps, "cooling", m_temps.COOLING_POWER );
     835              : 
     836            1 :     if( stdCamera<ocam2KCtrl>::onPowerOff() < 0 )
     837              :     {
     838            0 :         log<software_error>( { "" } );
     839              :     }
     840              : 
     841            1 :     if( edtCamera<ocam2KCtrl>::onPowerOff() < 0 )
     842              :     {
     843            0 :         log<software_error>( { "" } );
     844              :     }
     845              : 
     846            1 :     if( frameGrabber<ocam2KCtrl>::onPowerOff() < 0 )
     847              :     {
     848            0 :         log<software_error>( { "" } );
     849              :     }
     850              : 
     851            1 :     if( dssShutter<ocam2KCtrl>::onPowerOff() < 0 )
     852              :     {
     853            0 :         log<software_error>( { "" } );
     854              :     }
     855              : 
     856              :     // Setting m_poweredOn
     857            1 :     m_poweredOn = true;
     858              : 
     859            1 :     return 0;
     860            1 : }
     861              : 
     862            1 : inline int ocam2KCtrl::whilePowerOff()
     863              : {
     864            1 :     std::lock_guard<std::mutex> lock( m_indiMutex );
     865              : 
     866            1 :     if( stdCamera<ocam2KCtrl>::whilePowerOff() < 0 )
     867              :     {
     868            0 :         log<software_error>( { "" } );
     869              :     }
     870              : 
     871            1 :     if( edtCamera<ocam2KCtrl>::whilePowerOff() < 0 )
     872              :     {
     873            0 :         log<software_error>( { "" } );
     874              :     }
     875              : 
     876            1 :     if( dssShutter<ocam2KCtrl>::whilePowerOff() < 0 )
     877              :     {
     878            0 :         log<software_error>( { "" } );
     879              :     }
     880              : 
     881            1 :     return 0;
     882            1 : }
     883              : 
     884            2 : inline int ocam2KCtrl::appShutdown()
     885              : {
     886              :     ///\todo error check these base class fxns.
     887              : 
     888            2 :     dev::stdCamera<ocam2KCtrl>::appShutdown();
     889              : 
     890            2 :     dev::edtCamera<ocam2KCtrl>::appShutdown();
     891              : 
     892            2 :     dev::frameGrabber<ocam2KCtrl>::appShutdown();
     893              : 
     894            2 :     destroySyncStream();
     895              : 
     896            2 :     dev::dssShutter<ocam2KCtrl>::appShutdown();
     897              : 
     898            2 :     dev::telemeter<ocam2KCtrl>::appShutdown();
     899              : 
     900            2 :     return 0;
     901              : }
     902              : 
     903           16 : inline int ocam2KCtrl::getTemps()
     904              : {
     905           16 :     std::string response;
     906              : 
     907              :     { // mutex scope
     908           16 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
     909           48 :         if( pdvSerialWriteRead( response, "temp" ) != 0 )
     910              :         {
     911            4 :             if( powerState() != 1 || powerStateTarget() != 1 )
     912            2 :                 return -1;
     913            2 :             return log<software_error, -1>( { "" } );
     914              :         }
     915           16 :     }
     916              : 
     917              :     {
     918           12 :         ocamTemps temps;
     919              : 
     920           12 :         if( parseTemps( temps, response ) < 0 )
     921              :         {
     922            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
     923            1 :                 return -1;
     924              : 
     925            1 :             m_temps.setInvalid();
     926            1 :             m_ccdTemp              = m_temps.CCD;
     927            1 :             m_ccdTempSetpt         = m_temps.SET;
     928            1 :             m_tempControlStatus    = false;
     929            1 :             m_tempControlStatusStr = "UNKNOWN";
     930              : 
     931            1 :             recordTemps();
     932            1 :             recordCamera();
     933              : 
     934            1 :             std::cerr << "Temp. parse error. Response:\n" << response << std::endl;
     935              : 
     936              :             // We don't trust the temps, but don't reconfig just for this.
     937            1 :             return log<software_error, 0>( { "Temp. parse error" } );
     938              :         }
     939              : 
     940           10 :         m_temps = temps;
     941              : 
     942              :         // stdCamera temp control:
     943           10 :         m_ccdTemp      = m_temps.CCD;
     944           10 :         m_ccdTempSetpt = m_temps.SET;
     945              : 
     946              :         // Detect that temperature control is off
     947           10 :         if( m_temps.COOLING_POWER < 5 )
     948              :         {
     949            1 :             if( m_temps.CCD - m_temps.SET > 2.99 )
     950              :             {
     951            1 :                 m_tempControlStatus = false;
     952              :             }
     953              :         }
     954              :         else
     955            9 :             m_tempControlStatus = true;
     956              : 
     957           10 :         if( m_tempControlStatus == true )
     958              :         {
     959            9 :             if( fabs( m_temps.CCD - m_temps.SET ) < 1.0 )
     960              :             {
     961            8 :                 m_tempControlStatusStr = "ON TARGET";
     962            8 :                 m_tempControlOnTarget  = true;
     963              :             }
     964              :             else
     965              :             {
     966            1 :                 m_tempControlStatusStr = "OFF TARGET";
     967            1 :                 m_tempControlOnTarget  = false;
     968              :             }
     969              :         }
     970              :         else
     971              :         {
     972            1 :             m_tempControlStatusStr = "TEMP OFF";
     973            1 :             m_tempControlOnTarget  = false;
     974              :         }
     975              : 
     976              :         // Telemeter:
     977           10 :         recordTemps();
     978           10 :         recordCamera();
     979              : 
     980           20 :         updateIfChanged( m_indiP_temps, "cpu", m_temps.CPU );
     981           20 :         updateIfChanged( m_indiP_temps, "power", m_temps.POWER );
     982           20 :         updateIfChanged( m_indiP_temps, "bias", m_temps.BIAS );
     983           20 :         updateIfChanged( m_indiP_temps, "water", m_temps.WATER );
     984           20 :         updateIfChanged( m_indiP_temps, "left", m_temps.LEFT );
     985           20 :         updateIfChanged( m_indiP_temps, "right", m_temps.RIGHT );
     986           20 :         updateIfChanged( m_indiP_temps, "cooling", m_temps.COOLING_POWER );
     987           10 :         return 0;
     988              :     }
     989           16 : }
     990              : 
     991            2 : inline int ocam2KCtrl::powerOnDefaults()
     992              : {
     993              :     // Camera boots up with this true in most cases.
     994            2 :     m_tempControlStatusSet = false;
     995            2 :     m_tempControlStatus    = false;
     996              : 
     997            2 :     return 0;
     998              : }
     999              : 
    1000            5 : inline int ocam2KCtrl::setTempControl()
    1001              : {
    1002            5 :     std::string response;
    1003              : 
    1004            5 :     std::string command;
    1005              : 
    1006            5 :     std::string comStr = "temp ";
    1007            5 :     if( m_tempControlStatusSet )
    1008              :     {
    1009            3 :         command                = "on";
    1010            3 :         m_tempControlStatusSet = true;
    1011            3 :         m_tempControlStatus    = true;
    1012              :     }
    1013              :     else
    1014              :     {
    1015            2 :         if( m_ccdTemp > 19 ) // 19 is 20 with a 1 C slop
    1016              :         {
    1017            1 :             command                = "off";
    1018            1 :             m_tempControlStatusSet = false;
    1019            1 :             m_tempControlStatus    = false;
    1020              :         }
    1021              :         else
    1022              :         {
    1023            1 :             return log<text_log, -1>( "Can not turn temp control off when not at 20 C or higher", logPrio::LOG_ERROR );
    1024              :         }
    1025              :     }
    1026              : 
    1027            4 :     comStr += command;
    1028              : 
    1029              :     { // mutex scope
    1030            4 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1031            4 :         if( pdvSerialWriteRead( response, comStr ) != 0 )
    1032              :         {
    1033            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1034            1 :                 return -1;
    1035            1 :             return log<software_error, -1>( { "" } );
    1036              :         }
    1037            4 :     }
    1038              : 
    1039              :     {
    1040            2 :         std::cerr << "response: " << response << "\n";
    1041              :         ///\todo check response
    1042            2 :         log<text_log, 0>( { "Set temperature control to " + command } );
    1043              :     }
    1044              : 
    1045            2 :     if( m_tempControlStatusSet && m_ccdTempSetpt > -999 )
    1046              :     {
    1047            1 :         return setTempSetPt();
    1048              :     }
    1049              : 
    1050            1 :     recordCamera();
    1051              : 
    1052            1 :     return 0;
    1053            5 : }
    1054              : 
    1055            9 : inline int ocam2KCtrl::setTempSetPt()
    1056              : {
    1057            9 :     std::string response;
    1058              : 
    1059            9 :     std::string tempStr = std::to_string( m_ccdTempSetpt );
    1060              : 
    1061              :     ///\todo make more configurable
    1062            9 :     if( m_ccdTempSetpt >= 30 || m_ccdTempSetpt < -50 )
    1063              :     {
    1064            4 :         return log<text_log, -1>( { "attempt to set temperature outside valid range: " + tempStr },
    1065            2 :                                   logPrio::LOG_ERROR );
    1066              :     }
    1067              : 
    1068              :     { // mutex scope
    1069            7 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1070            7 :         if( pdvSerialWriteRead( response, "temp " + tempStr ) != 0 )
    1071              :         {
    1072            4 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1073            2 :                 return -1;
    1074            2 :             return log<software_error, -1>( { "" } );
    1075              :         }
    1076            7 :     }
    1077              : 
    1078              :     {
    1079            3 :         std::cerr << "response: " << response << "\n";
    1080              : 
    1081            3 :         recordCamera();
    1082              : 
    1083              :         ///\todo check response
    1084            3 :         std::cerr << "temp " << tempStr << " response: " << response << "\n";
    1085              : 
    1086            3 :         return log<text_log, 0>( { "set temperature: " + tempStr } );
    1087              :     }
    1088            9 : }
    1089              : 
    1090           20 : inline int ocam2KCtrl::getFPS()
    1091              : {
    1092           20 :     if( !m_synchro )
    1093              :     {
    1094           19 :         std::string response;
    1095              : 
    1096              :         { // mutex scope
    1097           19 :             std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1098           57 :             if( pdvSerialWriteRead( response, "fps" ) != 0 )
    1099            5 :                 return log<software_error, -1>( { "" } ); // m_pdv, "fps", m_readTimeout) == 0)
    1100           19 :         }
    1101              : 
    1102              :         {
    1103              :             float fps;
    1104           14 :             if( parseFPS( fps, response ) < 0 )
    1105              :             {
    1106            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
    1107            1 :                     return -1;
    1108              : 
    1109            1 :                 std::cerr << "fps parse error. Response:\n" << response << "\n";
    1110              : 
    1111            1 :                 return log<software_error, 0>( { "fps parse error" } );
    1112              :             }
    1113           12 :             m_fps = fps;
    1114              : 
    1115           12 :             recordCamera();
    1116              : 
    1117           12 :             return 0;
    1118              :         }
    1119           19 :     }
    1120              :     else
    1121              :     {
    1122            1 :         m_fps = m_syncFreq;
    1123            1 :         recordCamera();
    1124              : 
    1125            1 :         return 0;
    1126              :     }
    1127              : }
    1128              : 
    1129           17 : inline int ocam2KCtrl::setFPS()
    1130              : {
    1131           17 :     std::string fpsStr = std::to_string( m_fpsSet );
    1132              : 
    1133           17 :     if( !m_synchro )
    1134              :     {
    1135           15 :         std::string response;
    1136              : 
    1137              :         { // mutex scope
    1138           15 :             std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1139           15 :             if( pdvSerialWriteRead( response, "fps " + fpsStr ) != 0 )
    1140              :             {
    1141            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
    1142            1 :                     return -1;
    1143            1 :                 return log<software_error, -1>( { "" } );
    1144              :             }
    1145           15 :         }
    1146              : 
    1147              :         {
    1148              :             ///\todo check response
    1149           13 :             std::cerr << "fps " << fpsStr << " response: " << response << "\n";
    1150           13 :             log<text_log>( { "set fps: " + fpsStr } );
    1151              : 
    1152              :             // We always want to reset the latency circular buffers
    1153              :             ///\todo verify that this works!!
    1154           13 :             m_nextMode = m_modeName;
    1155           13 :             m_reconfig = true;
    1156              :         }
    1157           15 :     }
    1158              :     else
    1159              :     {
    1160            2 :         std::cerr << "setting: " << fpsStr << "\n";
    1161              : 
    1162            2 :         pcf::IndiProperty ipFreq( pcf::IndiProperty::Number );
    1163              : 
    1164            2 :         ipFreq.setDevice( m_syncDevice );
    1165            2 :         ipFreq.setName( m_syncFreqProp );
    1166            4 :         ipFreq.add( pcf::IndiElement( "target" ) );
    1167              : 
    1168            2 :         ipFreq["target"] = fpsStr;
    1169              : 
    1170            2 :         sendNewProperty( ipFreq );
    1171            2 :     }
    1172              : 
    1173           15 :     return 0;
    1174           17 : }
    1175              : 
    1176           18 : inline int ocam2KCtrl::setSynchro()
    1177              : {
    1178           18 :     std::string response;
    1179              : 
    1180              :     { // mutex scope
    1181           18 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1182              : 
    1183              :         // First set the actual FPS to 0 to get to max
    1184           18 :         std::string fpsStr = std::to_string( 0 );
    1185           18 :         if( pdvSerialWriteRead( response, "fps " + fpsStr ) == 0 )
    1186              :         {
    1187              :             ///\todo check response
    1188           16 :             std::cerr << "fps " << fpsStr << " response: " << response << "\n";
    1189           16 :             log<text_log>( { "set fps: " + fpsStr } );
    1190              :         }
    1191              :         else
    1192              :         {
    1193            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1194            1 :                 return -1;
    1195            1 :             return log<software_error, -1>( { "" } );
    1196              :         }
    1197              : 
    1198              :         // Now actually turn synchro on
    1199           16 :         std::string sStr;
    1200           16 :         if( m_synchroSet )
    1201            3 :             sStr = "on";
    1202              :         else
    1203           13 :             sStr = "off";
    1204           16 :         if( pdvSerialWriteRead( response, "synchro " + sStr ) == 0 )
    1205              :         {
    1206              :             ///\todo check response
    1207           12 :             std::cerr << "synchro " << sStr << " resonse: " << response << "\n";
    1208           12 :             log<text_log>( { "set synchro: " + sStr } );
    1209              : 
    1210           12 :             m_synchro = m_synchroSet;
    1211              : 
    1212           12 :             if( m_synchro == false )
    1213              :             {
    1214           33 :                 updateSwitchIfChanged( m_indiP_synchro, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    1215              :             }
    1216              :             else
    1217              :             {
    1218            3 :                 updateSwitchIfChanged( m_indiP_synchro, "toggle", pcf::IndiElement::On, INDI_OK );
    1219              :             }
    1220              :         }
    1221              :         else
    1222              :         {
    1223            4 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1224            1 :                 return -1;
    1225            3 :             return log<software_error, -1>( { "" } );
    1226              :         }
    1227           28 :     }
    1228              : 
    1229              :     // Finally we set the FPS of the synchro device
    1230           12 :     return setFPS();
    1231           18 : }
    1232              : 
    1233            1 : inline int ocam2KCtrl::setExpTime()
    1234              : {
    1235            1 :     return 0;
    1236              : }
    1237              : 
    1238            1 : inline int ocam2KCtrl::setNextROI()
    1239              : {
    1240            1 :     return 0;
    1241              : }
    1242              : 
    1243            1 : inline int ocam2KCtrl::setShutter( int sh )
    1244              : {
    1245            1 :     return dssShutter<ocam2KCtrl>::setShutterState( sh );
    1246              : }
    1247              : 
    1248            1 : inline std::string ocam2KCtrl::stateString()
    1249              : {
    1250            1 :     std::string ss;
    1251              : 
    1252            1 :     ss += m_modeName + "_";
    1253            1 :     ss += std::to_string( m_fps ) + "_";
    1254            1 :     ss += std::to_string( m_emGain ) + "_";
    1255            1 :     ss += std::to_string( m_ccdTempSetpt );
    1256              : 
    1257            1 :     return ss;
    1258            0 : }
    1259              : 
    1260            3 : inline bool ocam2KCtrl::stateStringValid()
    1261              : {
    1262            3 :     if( state() == stateCodes::OPERATING && m_tempControlOnTarget )
    1263              :     {
    1264            1 :         return true;
    1265              :     }
    1266              :     else
    1267            2 :         return false;
    1268              : }
    1269              : 
    1270            4 : inline int ocam2KCtrl::resetEMProtection()
    1271              : {
    1272            4 :     std::string response;
    1273              : 
    1274              :     { // mutex scope
    1275            4 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1276           12 :         if( pdvSerialWriteRead( response, "protection reset" ) != 0 )
    1277              :         {
    1278            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1279            1 :                 return -1;
    1280            1 :             return log<software_error, -1>( { "" } );
    1281              :         }
    1282            4 :     }
    1283              : 
    1284              :     {
    1285            2 :         std::cerr << "\n******************************************\n";
    1286            2 :         std::cerr << "protection reset:\n";
    1287            2 :         std::cerr << response << "\n";
    1288            2 :         std::cerr << "\n******************************************\n";
    1289              :         ///\todo check response.
    1290              : 
    1291            8 :         updateIfChanged( m_indiP_emProt, "status", std::string( "RESET" ), INDI_OK );
    1292              : 
    1293            2 :         log<text_log>( "overillumination protection has been reset", logPrio::LOG_NOTICE );
    1294              : 
    1295            2 :         m_protectionResetConfirmed = 0;
    1296              : 
    1297            2 :         m_protectionReset = true;
    1298              : 
    1299            2 :         return 0;
    1300              :     }
    1301            4 : }
    1302              : 
    1303           10 : inline int ocam2KCtrl::getEMGain()
    1304              : {
    1305           10 :     std::string response;
    1306              : 
    1307              :     { // mutex scope
    1308           10 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1309           30 :         if( pdvSerialWriteRead( response, "gain" ) != 0 )
    1310              :         {
    1311            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1312            1 :                 return -1;
    1313            1 :             return log<software_error, -1>( { "" } );
    1314              :         }
    1315           10 :     }
    1316              : 
    1317              :     {
    1318              :         unsigned emGain;
    1319            8 :         if( parseEMGain( emGain, response ) < 0 )
    1320              :         {
    1321            3 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1322            1 :                 return -1;
    1323              : 
    1324            2 :             if( response.find( "HV" ) != std::string::npos )
    1325              :             {
    1326            1 :                 m_emGain = 1;
    1327            5 :                 updateIfChanged( m_indiP_emProt, "status", std::string( "TRIPPED" ), INDI_ALERT );
    1328            1 :                 return log<software_warning, -1>( { "EM Gain tripped!" } );
    1329              :             }
    1330              : 
    1331            1 :             std::cerr << "EM Gain parse error, response:\n" << response << "\n";
    1332              : 
    1333            1 :             return log<software_error, -1>( { "EM Gain parse error" } );
    1334              :         }
    1335              : 
    1336            5 :         m_emGain = emGain;
    1337              : 
    1338            5 :         return 0;
    1339              :     }
    1340           10 : }
    1341              : 
    1342            5 : inline int ocam2KCtrl::setEMGain()
    1343              : {
    1344            5 :     std::string response;
    1345              : 
    1346            5 :     if( m_protectionReset == false )
    1347              :     {
    1348            1 :         log<text_log>( "Attempt to set EM gain before protection reset", logPrio::LOG_NOTICE );
    1349              : 
    1350            1 :         if( m_emGainSet > 1 ) // we allow setting to 1 for safety
    1351              :         {
    1352            1 :             return 0;
    1353              :         }
    1354              :     }
    1355              : 
    1356            4 :     unsigned emg = m_emGainSet; // a float
    1357              : 
    1358            4 :     if( emg < 1 || emg > m_maxEMGain )
    1359              :     {
    1360            1 :         log<text_log>( "Attempt to set EM gain to " + std::to_string( emg ) + " outside limits refused",
    1361              :                        logPrio::LOG_WARNING );
    1362            1 :         return 0;
    1363              :     }
    1364              : 
    1365            3 :     std::string emgStr = std::to_string( emg );
    1366              :     { // mutex scope
    1367            3 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1368            3 :         if( pdvSerialWriteRead( response, "gain " + emgStr ) != 0 ) // m_pdv, "gain " + emgStr, m_readTimeout) == 0)
    1369              :         {
    1370            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
    1371            1 :                 return -1;
    1372            1 :             return log<software_error, -1>( { "" } );
    1373              :         }
    1374            3 :     }
    1375              : 
    1376              :     {
    1377              :         ///\todo check response
    1378            1 :         std::cerr << "gain " << emgStr << " response: " << emgStr << "\n";
    1379              : 
    1380            1 :         log<text_log>( { "set EM Gain: " + emgStr } );
    1381              : 
    1382            1 :         return 0;
    1383              :     }
    1384            5 : }
    1385              : 
    1386           11 : inline int ocam2KCtrl::configureAcquisition()
    1387              : {
    1388              :     // lock mutex
    1389           11 :     std::unique_lock<std::mutex>          lock( m_indiMutex );
    1390           11 :     std::lock_guard<std::recursive_mutex> cameraGuard( m_cameraMutex );
    1391              : 
    1392              :     // Send command to camera to place it in the correct mode
    1393           11 :     std::string response;
    1394           11 :     if( pdvSerialWriteRead( response, m_cameraModes[m_modeName].m_serialCommand ) != 0 )
    1395              :     {
    1396            2 :         if( powerState() != 1 || powerStateTarget() != 1 )
    1397            1 :             return -1;
    1398              : 
    1399            1 :         log<software_error>( { "Error sending command to set mode" } );
    1400            1 :         sleep( 1 );
    1401            1 :         return -1;
    1402              :     }
    1403              : 
    1404            9 :     m_currentROI.x     = 119.5;
    1405            9 :     m_currentROI.y     = 119.5;
    1406            9 :     m_currentROI.w     = 240;
    1407            9 :     m_currentROI.h     = 240;
    1408            9 :     m_currentROI.bin_x = m_cameraModes[m_modeName].m_binningX;
    1409            9 :     m_currentROI.bin_y = m_cameraModes[m_modeName].m_binningY;
    1410              : 
    1411            9 :     m_digitalBinX = m_cameraModes[m_modeName].m_digitalBinX;
    1412            9 :     m_digitalBinY = m_cameraModes[m_modeName].m_digitalBinY;
    1413              : 
    1414            9 :     if( m_digitalBinX > 1 )
    1415              :     {
    1416            2 :         m_digitalBin = true;
    1417            2 :         std::cerr << "digital binning!\n";
    1418              :     }
    1419              :     else
    1420              :     {
    1421            7 :         m_digitalBin = false;
    1422              :     }
    1423              : 
    1424            9 :     recordCamera();
    1425              : 
    1426              :     ///\todo check response of pdvSerialWriteRead
    1427            9 :     log<text_log>( "camera configured with: " + m_cameraModes[m_modeName].m_serialCommand );
    1428              : 
    1429            9 :     if( m_fpsSet > 0 )
    1430              :     {
    1431            1 :         setFPS();
    1432              :     }
    1433              : 
    1434            9 :     log<text_log>( "Send command to set mode: " + m_cameraModes[m_modeName].m_serialCommand );
    1435            9 :     log<text_log>( "Response was: " + response );
    1436              : 
    1437            9 :     if( setSynchro() < 0 )
    1438              :     {
    1439            1 :         log<software_error>( { "Error setting synchro during configureAcquisition" } );
    1440              :     }
    1441              : 
    1442              :     /* Initialize the OCAM2 SDK
    1443              :      */
    1444              : 
    1445            9 :     if( m_ocam2_id > 0 )
    1446              :     {
    1447            1 :         ocam2_exit( m_ocam2_id );
    1448            1 :         m_ocam2_id = 0;
    1449              :     }
    1450              :     ocam2_rc   rc;
    1451              :     ocam2_mode mode;
    1452              : 
    1453              :     int OCAM_SZw;
    1454              :     int OCAM_SZh;
    1455            9 :     if( m_raw_height == 121 )
    1456              :     {
    1457            5 :         mode     = OCAM2_NORMAL;
    1458            5 :         OCAM_SZw = 240;
    1459            5 :         OCAM_SZh = 240;
    1460              :     }
    1461            4 :     else if( m_raw_height == 62 )
    1462              :     {
    1463            1 :         mode     = OCAM2_BINNING;
    1464            1 :         OCAM_SZw = 120;
    1465            1 :         OCAM_SZh = 120;
    1466              :     }
    1467            3 :     else if( m_raw_height == 41 )
    1468              :     {
    1469            1 :         mode     = OCAM2_BINNING1x3;
    1470            1 :         OCAM_SZw = 240;
    1471            1 :         OCAM_SZh = 80;
    1472              :     }
    1473            2 :     else if( m_raw_height == 31 )
    1474              :     {
    1475            1 :         mode     = OCAM2_BINNING1x4;
    1476            1 :         OCAM_SZw = 240;
    1477            1 :         OCAM_SZh = 60;
    1478              :     }
    1479              :     else
    1480              :     {
    1481            1 :         log<text_log>( "Unrecognized OCAM2 mode.", logPrio::LOG_ERROR );
    1482            1 :         return -1;
    1483              :     }
    1484              : 
    1485            8 :     std::string ocamDescrambleFile = m_configDir + "/" + m_ocamDescrambleFile;
    1486              : 
    1487            8 :     std::cerr << "ocamDescrambleFile: " << ocamDescrambleFile << std::endl;
    1488              : 
    1489            8 :     ocam2_id nextOcam2Id = 0;
    1490              : 
    1491            8 :     rc = ocam2_init( mode, ocamDescrambleFile.c_str(), &nextOcam2Id );
    1492              : 
    1493            8 :     if( rc != OCAM2_OK )
    1494              :     {
    1495            2 :         if( powerState() != 1 || powerStateTarget() != 1 )
    1496            1 :             return -1;
    1497            1 :         log<text_log>( "ocam2_init error. Failed to initialize OCAM SDK with descramble file: " + ocamDescrambleFile,
    1498              :                        logPrio::LOG_ERROR );
    1499            1 :         return -1;
    1500              :     }
    1501              : 
    1502            6 :     m_ocam2_id = nextOcam2Id;
    1503              : 
    1504            6 :     log<text_log>( "OCAM2K initialized. id: " + std::to_string( m_ocam2_id ) );
    1505              : 
    1506           12 :     log<text_log>( std::string( "OCAM2K mode is:" ) + ocam2_modeStr( ocam2_getMode( m_ocam2_id ) ) );
    1507              : 
    1508            6 :     if( m_digitalBin )
    1509              :     {
    1510            1 :         std::cerr << "and digital binning!\n";
    1511            1 :         m_digitalBinWork.resize( OCAM_SZw, OCAM_SZh );
    1512              : 
    1513            1 :         m_width  = OCAM_SZw / m_digitalBinX;
    1514            1 :         m_height = OCAM_SZh / m_digitalBinY;
    1515              :     }
    1516              :     else
    1517              :     {
    1518            5 :         m_width  = OCAM_SZw;
    1519            5 :         m_height = OCAM_SZh;
    1520              :     }
    1521              : 
    1522            6 :     m_dataType = _DATATYPE_UINT16;
    1523              : 
    1524            6 :     if( ensureSyncStream() < 0 )
    1525              :     {
    1526            0 :         return log<software_error, -1>( { "Error preparing sync ImageStreamIO stream" } );
    1527              :     }
    1528              : 
    1529            6 :     state( stateCodes::OPERATING );
    1530              : 
    1531            6 :     return 0;
    1532           11 : }
    1533              : 
    1534            2 : inline float ocam2KCtrl::fps()
    1535              : {
    1536            2 :     return m_fps;
    1537              : }
    1538              : 
    1539            1 : inline int ocam2KCtrl::startAcquisition()
    1540              : {
    1541            1 :     m_lastImageNumber = -1;
    1542            1 :     return edtCamera<ocam2KCtrl>::pdvStartAcquisition();
    1543              : }
    1544              : 
    1545            4 : inline int ocam2KCtrl::acquireAndCheckValid()
    1546              : {
    1547            4 :     edtCamera<ocam2KCtrl>::pdvAcquire( m_currImageTimestamp );
    1548              : 
    1549              :     /* Removed all pdv timeout and overrun checking, since we can rely on frame number from the camera
    1550              :        to detect missed and corrupted frames.
    1551              : 
    1552              :        See ef0dd24 for last version with full checks in it.
    1553              :     */
    1554              : 
    1555              :     // Get the image number to see if this is valid.
    1556              :     // This is how it is in the ocam2_sdk:
    1557            4 :     unsigned currImageNumber = ( reinterpret_cast<int *>( m_image_p ) )[OCAM2_IMAGE_NB_OFFSET / 4]; /* int offset */
    1558            4 :     m_currImageNumber        = currImageNumber;
    1559              : 
    1560              :     // For the first loop after a restart
    1561            4 :     if( m_lastImageNumber == -1 )
    1562              :     {
    1563            1 :         m_lastImageNumber = m_currImageNumber - 1;
    1564              :     }
    1565              : 
    1566            4 :     if( m_currImageNumber - m_lastImageNumber != 1 )
    1567              :     {
    1568              :         // Detect exact condition of a wraparound on the unsigned int.
    1569              :         //  Yes, this can only happen once every 13.72 days at 3622 fps
    1570              :         //  But just in case . . .
    1571            3 :         if( m_lastImageNumber != std::numeric_limits<unsigned int>::max() && m_currImageNumber != 0 )
    1572              :         {
    1573              :             // The far more likely case is a problem...
    1574              : 
    1575              :             // If a reasonably small number of frames skipped, then we trust the image number
    1576            3 :             if( m_currImageNumber - m_lastImageNumber > 1 && m_currImageNumber - m_lastImageNumber < 100 )
    1577              :             {
    1578              :                 // This we handle as a non-timeout -- report how many frames were skipped
    1579            1 :                 long framesSkipped = m_currImageNumber - m_lastImageNumber - 1;
    1580              : 
    1581            1 :                 log<text_log>( "frames skipped: " + std::to_string( framesSkipped ), logPrio::LOG_ERROR );
    1582              : 
    1583            1 :                 m_lastImageNumber = -1;
    1584            1 :                 m_nextMode        = m_modeName;
    1585            1 :                 m_reconfig        = 1;
    1586            1 :                 return 1;
    1587              :             }
    1588              :             else // but if it's any bigger or < 0, it's probably garbage
    1589              :             {
    1590            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
    1591            1 :                     return -1;
    1592              : 
    1593              :                 ///\todo need frame corrupt log type
    1594            1 :                 log<text_log>( "frame number possibly corrupt: " + std::to_string( m_currImageNumber ) + " - " +
    1595            2 :                                    std::to_string( m_lastImageNumber ),
    1596              :                                logPrio::LOG_ERROR );
    1597              : 
    1598            1 :                 m_nextMode = m_modeName;
    1599            1 :                 m_reconfig = 1;
    1600              : 
    1601              :                 // Reset the counters.
    1602            1 :                 m_lastImageNumber = -1;
    1603            1 :                 return 1;
    1604              :             }
    1605              :         }
    1606              :     }
    1607            1 :     m_lastImageNumber = m_currImageNumber;
    1608            1 :     return 0;
    1609              : }
    1610              : 
    1611            2 : inline int ocam2KCtrl::loadImageIntoStream( void *dest )
    1612              : {
    1613            2 :     unsigned currImageNumber = 0;
    1614              : 
    1615            2 :     if( !m_digitalBin )
    1616              :     {
    1617            1 :         ocam2_descramble( m_ocam2_id,
    1618              :                           &currImageNumber,
    1619              :                           reinterpret_cast<int16_t *>( dest ),
    1620            1 :                           reinterpret_cast<int16_t *>( m_image_p ) );
    1621              :         // memcpy(dest, m_image_p, 120*120*2); //This is about 10 usec faster -- but we have to descramble.
    1622              :     }
    1623              :     else
    1624              :     {
    1625            1 :         ocam2_descramble(
    1626            1 :             m_ocam2_id, &currImageNumber, m_digitalBinWork.data(), reinterpret_cast<int16_t *>( m_image_p ) );
    1627              : 
    1628            1 :         mx::improc::eigenMap<int16_t> destMap( reinterpret_cast<int16_t *>( dest ), m_width, m_height );
    1629              : 
    1630            1 :         if( m_digitalBinX > 1 )
    1631              :         {
    1632            3 :             for( int cc = 0; cc < destMap.cols(); ++cc )
    1633              :             {
    1634            6 :                 for( int rr = 0; rr < destMap.rows(); ++rr )
    1635              :                 {
    1636            4 :                     destMap( rr, cc ) = m_digitalBinWork( rr * m_digitalBinX + 0, cc );
    1637              : 
    1638            8 :                     for( unsigned b = 1; b < m_digitalBinX; ++b )
    1639              :                     {
    1640            4 :                         destMap( rr, cc ) = m_digitalBinWork( rr * m_digitalBinX + b, cc );
    1641              :                     }
    1642              :                 }
    1643              :             }
    1644              :         }
    1645              :     }
    1646              : 
    1647            2 :     return 0;
    1648              : }
    1649              : 
    1650            1 : inline int ocam2KCtrl::reconfig()
    1651              : {
    1652            1 :     std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1653              : 
    1654            1 :     int rv = edtCamera<ocam2KCtrl>::pdvReconfig();
    1655              : 
    1656            1 :     if( rv < 0 )
    1657              :     {
    1658            0 :         return rv;
    1659              :     }
    1660              : 
    1661            1 :     state( stateCodes::READY );
    1662            1 :     return 0;
    1663            1 : }
    1664              : 
    1665            3 : inline int ocam2KCtrl::frameGrabberPostPublish( IMAGE *imageStream )
    1666              : {
    1667            3 :     if( imageStream == nullptr )
    1668              :     {
    1669            1 :         return 0;
    1670              :     }
    1671              : 
    1672            2 :     if( m_syncImageStream == nullptr )
    1673              :     {
    1674            1 :         return log<software_error, -1>( { "Sync ImageStreamIO stream unavailable during publication" } );
    1675              :     }
    1676              : 
    1677            1 :     m_syncImageStream->md[0].write       = 1;
    1678            1 :     m_syncImageStream->array.UI8[0]      = 0;
    1679            1 :     m_syncImageStream->md[0].writetime   = imageStream->md[0].writetime;
    1680            1 :     m_syncImageStream->md[0].atime       = imageStream->md[0].atime;
    1681            1 :     m_syncImageStream->md[0].cnt0        = imageStream->md[0].cnt0;
    1682            1 :     m_syncImageStream->md[0].cnt1        = 0;
    1683            1 :     m_syncImageStream->writetimearray[0] = imageStream->md[0].writetime;
    1684            1 :     m_syncImageStream->atimearray[0]     = imageStream->md[0].atime;
    1685            1 :     m_syncImageStream->cntarray[0]       = imageStream->md[0].cnt0;
    1686            1 :     m_syncImageStream->md[0].write       = 0;
    1687              : 
    1688            1 :     ImageStreamIO_sempost( m_syncImageStream, -1 );
    1689              : 
    1690            1 :     return 0;
    1691              : }
    1692              : 
    1693            7 : INDI_NEWCALLBACK_DEFN( ocam2KCtrl, m_indiP_emProtReset )( const pcf::IndiProperty &ipRecv )
    1694              : {
    1695            7 :     if( MagAOXAppT::m_powerState == 0 )
    1696            1 :         return 0;
    1697              : 
    1698            6 :     if( ipRecv.getName() != m_indiP_emProtReset.getName() )
    1699              :     {
    1700            1 :         log<software_error>( { "wrong INDI property received." } );
    1701            1 :         return -1;
    1702              :     }
    1703              : 
    1704           10 :     if( !ipRecv.find( "request" ) )
    1705              :     {
    1706            1 :         return 0;
    1707              :     }
    1708              : 
    1709            8 :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::Off )
    1710              :     {
    1711            1 :         return 0;
    1712              :     }
    1713              : 
    1714            3 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1715              : 
    1716            3 :     if( m_protectionResetConfirmed == 0 )
    1717              :     {
    1718            8 :         updateIfChanged( m_indiP_emProt, "status", std::string( "CONFIRM" ), INDI_BUSY );
    1719              : 
    1720            2 :         m_protectionResetConfirmed = 1;
    1721              : 
    1722            2 :         m_protectionResetReqTime = mx::sys::get_curr_time();
    1723              : 
    1724            2 :         log<text_log>( "protection reset requested", logPrio::LOG_NOTICE );
    1725              : 
    1726            2 :         return 0;
    1727              :     }
    1728              : 
    1729              :     // If here, this is a confirmation.
    1730            1 :     return resetEMProtection();
    1731            3 : }
    1732              : 
    1733            3 : INDI_SETCALLBACK_DEFN( ocam2KCtrl, m_indiP_syncFreq )( const pcf::IndiProperty &ipRecv )
    1734              : {
    1735            3 :     INDI_VALIDATE_CALLBACK_PROPS( ipRecv, m_indiP_syncFreq )
    1736              : 
    1737            6 :     if( !ipRecv.find( "current" ) )
    1738              :     {
    1739            1 :         return -1;
    1740              :     }
    1741              : 
    1742            2 :     m_syncFreq = ipRecv["current"].get<double>();
    1743              : 
    1744            2 :     if( m_synchro && m_syncFreq != m_fps )
    1745              :     {
    1746            2 :         recordCamera( true );
    1747            2 :         m_fps = m_syncFreq;
    1748              : 
    1749              :         // We always want to reset the latency circular buffers
    1750            2 :         m_nextMode = m_modeName;
    1751            2 :         m_reconfig = true;
    1752              :     }
    1753              : 
    1754            2 :     return 0;
    1755              : }
    1756              : 
    1757            1 : inline int ocam2KCtrl::checkRecordTimes()
    1758              : {
    1759            1 :     return telemeter<ocam2KCtrl>::checkRecordTimes( ocam_temps(), telem_stdcam(), telem_fgtimings() );
    1760              : }
    1761              : 
    1762            2 : inline int ocam2KCtrl::recordTelem( const ocam_temps * )
    1763              : {
    1764            2 :     return recordTemps( true );
    1765              : }
    1766              : 
    1767            2 : inline int ocam2KCtrl::recordTelem( const telem_stdcam * )
    1768              : {
    1769            2 :     return recordCamera( true );
    1770              : }
    1771              : 
    1772            2 : inline int ocam2KCtrl::recordTelem( const telem_fgtimings * )
    1773              : {
    1774            2 :     return recordFGTimings( true );
    1775              : }
    1776              : 
    1777           13 : inline int ocam2KCtrl::recordTemps( bool force )
    1778              : {
    1779              :     static ocamTemps lastTemps;
    1780              : 
    1781           13 :     if( !( lastTemps == m_temps ) || force )
    1782              :     {
    1783            7 :         telem<ocam_temps>( { m_temps.CCD,
    1784            7 :                              m_temps.CPU,
    1785            7 :                              m_temps.POWER,
    1786            7 :                              m_temps.BIAS,
    1787            7 :                              m_temps.WATER,
    1788            7 :                              m_temps.LEFT,
    1789            7 :                              m_temps.RIGHT,
    1790            7 :                              m_temps.COOLING_POWER } );
    1791            7 :         lastTemps = m_temps;
    1792              :     }
    1793              : 
    1794           13 :     return 0;
    1795              : }
    1796              : 
    1797              : } // namespace app
    1798              : } // namespace MagAOX
    1799              : 
    1800              : #endif
        

Generated by: LCOV version 2.0-1