LCOV - code coverage report
Current view: top level - apps/cred2Ctrl - cred2Ctrl.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 95.7 % 791 757
Test Date: 2026-04-15 19:34:29 Functions: 100.0 % 50 50

            Line data    Source code
       1              : /** \file cred2Ctrl.hpp
       2              :  * \brief The MagAO-X C-RED 2 camera controller.
       3              :  *
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  * \ingroup cred2Ctrl_files
       7              :  */
       8              : 
       9              : #ifndef cred2Ctrl_hpp
      10              : #define cred2Ctrl_hpp
      11              : 
      12              : #include <algorithm>
      13              : #include <cmath>
      14              : #include <dlfcn.h>
      15              : #include <fstream>
      16              : #include <mutex>
      17              : #include <sstream>
      18              : #include <string>
      19              : 
      20              : #include "../../libMagAOX/libMagAOX.hpp" // Note this is included on command line to trigger pch
      21              : #include "../../magaox_git_version.h"
      22              : 
      23              : #include "cred2Utils.hpp"
      24              : 
      25              : namespace MagAOX
      26              : {
      27              : namespace app
      28              : {
      29              : 
      30              : /** \defgroup cred2Ctrl C-RED 2 Camera
      31              :  * \brief Control of the First Light Imaging C-RED 2 camera.
      32              :  *
      33              :  * <a href="../handbook/operating/software/apps/cred2Ctrl.html">Application Documentation</a>
      34              :  *
      35              :  * \ingroup apps
      36              :  */
      37              : 
      38              : /** \defgroup cred2Ctrl_files C-RED 2 Camera Files
      39              :  * \ingroup cred2Ctrl
      40              :  */
      41              : 
      42              : /// MagAO-X application to control the C-RED 2 camera.
      43              : /**
      44              :  * \ingroup cred2Ctrl
      45              :  */
      46              : class cred2Ctrl : public MagAOXApp<>,
      47              :                   public dev::stdCamera<cred2Ctrl>,
      48              :                   public dev::edtCamera<cred2Ctrl>,
      49              :                   public dev::frameGrabber<cred2Ctrl>,
      50              :                   public dev::telemeter<cred2Ctrl>
      51              : {
      52              :     friend class dev::stdCamera<cred2Ctrl>;
      53              :     friend class dev::edtCamera<cred2Ctrl>;
      54              :     friend class dev::frameGrabber<cred2Ctrl>;
      55              :     friend class dev::telemeter<cred2Ctrl>;
      56              : 
      57              :     typedef MagAOXApp<>                  MagAOXAppT;
      58              :     typedef dev::stdCamera<cred2Ctrl>    stdCameraT;
      59              :     typedef dev::frameGrabber<cred2Ctrl> frameGrabberT;
      60              :     typedef dev::telemeter<cred2Ctrl>    telemeterT;
      61              : 
      62              :   public:
      63              :     /** \name app::dev Configurations
      64              :      * @{
      65              :      */
      66              :     static constexpr bool c_stdCamera_tempControl  = true;  ///< Expose temperature setpoint control.
      67              :     static constexpr bool c_stdCamera_temp         = true;  ///< Expose detector temperature status.
      68              :     static constexpr bool c_stdCamera_readoutSpeed = false; ///< Do not expose readout-speed controls.
      69              :     static constexpr bool c_stdCamera_vShiftSpeed  = false; ///< Do not expose vertical-shift controls.
      70              :     static constexpr bool c_stdCamera_emGain       = false; ///< Do not expose EM-gain controls.
      71              :     static constexpr bool c_stdCamera_exptimeCtrl  = false; ///< Do not expose exposure-time controls.
      72              :     static constexpr bool c_stdCamera_fpsCtrl      = true;  ///< Expose FPS controls.
      73              :     static constexpr bool c_stdCamera_fps          = true;  ///< Expose FPS status.
      74              :     static constexpr bool c_stdCamera_fan          = true;  ///< Expose fan-speed controls.
      75              :     static constexpr bool c_stdCamera_analogGain   = true;  ///< Expose discrete analog-gain controls.
      76              :     static constexpr bool c_stdCamera_led          = true;  ///< Expose status LED controls.
      77              :     static constexpr bool c_stdCamera_synchro      = false; ///< Do not expose synchro controls in the first pass.
      78              :     static constexpr bool c_stdCamera_usesModes    = false; ///< Use one synthetic runtime mode rather than INDI modes.
      79              :     static constexpr bool c_stdCamera_usesROI      = true;  ///< Expose ROI controls.
      80              :     static constexpr bool c_stdCamera_cropMode     = false; ///< Do not expose crop-mode controls separately.
      81              :     static constexpr bool c_stdCamera_hasShutter   = false; ///< Do not expose shutter controls.
      82              :     static constexpr bool c_stdCamera_usesStateString    = false; ///< Do not expose a dark-management state string.
      83              :     static constexpr bool c_edtCamera_relativeConfigPath = false; ///< Use an absolute temporary EDT config path.
      84              :     static constexpr bool c_frameGrabber_flippable = true; ///< Expose image flip controls through the framegrabber.
      85              : 
      86              :     ///@}
      87              : 
      88              :   protected:
      89              :     /** \name Configurable Parameters - Data
      90              :      * @{
      91              :      */
      92              :     std::string m_configFile; ///< Absolute path to the temporary EDT configuration file.
      93              : 
      94              :     int m_serialBaud{ 115200 }; ///< Camera Link serial baud rate used for C-RED 2 CLI access.
      95              :     ///@}
      96              : 
      97              :     /** \name C-RED 2 State - Data
      98              :      * @{
      99              :      */
     100              :     cred2Temps m_temps; ///< Cached camera temperature values used for INDI and telemetry updates.
     101              : 
     102              :     bool m_cameraCropEnabled{ false }; ///< Tracks whether this controller has enabled camera-side cropping.
     103              :     int  m_roiSettleCounter{ 0 };      ///< Number of main-loop cycles to skip serial status polling after ROI changes.
     104              : 
     105              :     std::recursive_mutex m_cameraMutex; ///< Protects serial command traffic and EDT reconfiguration.
     106              :     ///@}
     107              : 
     108              :     /** \name INDI - Data
     109              :      * @{
     110              :      */
     111              :     pcf::IndiProperty m_indiP_temps;     ///< Property reporting the detailed C-RED 2 temperature channels.
     112              :     pcf::IndiProperty m_indiP_fpsLimits; ///< Property reporting the current C-RED 2 minimum and maximum FPS.
     113              : 
     114              :     ///@}
     115              : 
     116              :   public:
     117              :     /// Default c'tor.
     118              :     cred2Ctrl();
     119              : 
     120              :     /// D'tor, declared and defined for noexcept.
     121              :     ~cred2Ctrl() noexcept;
     122              : 
     123              :     /// Setup the configuration system.
     124              :     virtual void setupConfig();
     125              : 
     126              :     /// Load the configuration system results.
     127              :     virtual void loadConfig();
     128              : 
     129              :     /// Implementation of loadConfig logic with standard helper-macro error handling.
     130              :     int loadConfigImpl( mx::app::appConfigurator &config /**< [in] application configurator with loaded values */ );
     131              : 
     132              :     /// Startup function.
     133              :     virtual int appStartup();
     134              : 
     135              :     /// Main FSM logic.
     136              :     virtual int appLogic();
     137              : 
     138              :     /// Actions required when the camera power turns off.
     139              :     virtual int onPowerOff();
     140              : 
     141              :     /// Actions required while the camera remains powered off.
     142              :     virtual int whilePowerOff();
     143              : 
     144              :     /// Shutdown function.
     145              :     virtual int appShutdown();
     146              : 
     147              :     /// Query and update the camera temperature channels.
     148              :     int getTemps();
     149              : 
     150              :     /// Query and update the current camera frame rate.
     151              :     int getFPS();
     152              : 
     153              :     /// Query and update the current camera FPS limits.
     154              :     int updateFPSLimits();
     155              : 
     156              :     /// Query and update the current fan-control state.
     157              :     int getFanSpeed();
     158              : 
     159              :     /// Query and update the current analog-gain state.
     160              :     int getAnalogGain();
     161              : 
     162              :     /// Query and update the current LED state.
     163              :     int getLEDState();
     164              : 
     165              :     /// Query the camera for its current ROI and synchronize local state.
     166              :     int syncROIFromCamera();
     167              : 
     168              :     /** \name stdCamera Interface
     169              :      * @{
     170              :      */
     171              : 
     172              :     /// Set defaults for a power-on state.
     173              :     int powerOnDefaults();
     174              : 
     175              :     /// Implement the C-RED 2 temperature-controller toggle semantics.
     176              :     int setTempControl();
     177              : 
     178              :     /// Send the current target detector temperature setpoint to the camera.
     179              :     int setTempSetPt();
     180              : 
     181              :     /// Send the requested frame rate to the camera.
     182              :     int setFPS();
     183              : 
     184              :     /// Send the requested fan-control mode to the camera.
     185              :     int setFanSpeed();
     186              : 
     187              :     /// Send the requested analog-gain mode to the camera.
     188              :     int setAnalogGain();
     189              : 
     190              :     /// Send the requested LED state to the camera.
     191              :     int setLED();
     192              : 
     193              :     /// Required by `stdCamera`, but unused for C-RED 2.
     194              :     int setExpTime();
     195              : 
     196              :     /// Validate and normalize the requested ROI.
     197              :     int checkNextROI();
     198              : 
     199              :     /// Request that the next valid ROI be applied through reconfiguration.
     200              :     int setNextROI();
     201              : 
     202              :     ///@}
     203              : 
     204              :     /** \name Framegrabber Interface
     205              :      * @{
     206              :      */
     207              : 
     208              :     /// Write the temporary EDT configuration file for the pending ROI.
     209              :     int writeConfig();
     210              : 
     211              :     /// Configure camera-side ROI settings before acquisition starts.
     212              :     int configureAcquisition();
     213              : 
     214              :     /// Return the currently measured frame rate.
     215              :     float fps();
     216              : 
     217              :     /// Start frame acquisition on the EDT board.
     218              :     int startAcquisition();
     219              : 
     220              :     /// Wait for and validate the next acquired image.
     221              :     int acquireAndCheckValid();
     222              : 
     223              :     /// Copy the current EDT image into the output stream.
     224              :     int loadImageIntoStream( void *dest /**< [in] destination frame buffer */ );
     225              : 
     226              :     /// Reconfigure the EDT board for the pending ROI.
     227              :     int reconfig();
     228              : 
     229              :     ///@}
     230              : 
     231              :     /** \name Telemeter Interface
     232              :      * @{
     233              :      */
     234              : 
     235              :     /// Check the telemetry record timers.
     236              :     int checkRecordTimes();
     237              : 
     238              :     /// Record the detailed C-RED 2 temperature telemetry.
     239              :     int recordTelem( const cred2_temps * /**< [in] type-dispatch tag */ );
     240              : 
     241              :     /// Record standard camera telemetry.
     242              :     int recordTelem( const telem_stdcam * /**< [in] type-dispatch tag */ );
     243              : 
     244              :     /// Record framegrabber timing telemetry.
     245              :     int recordTelem( const telem_fgtimings * /**< [in] type-dispatch tag */ );
     246              : 
     247              :     /// Record the detailed C-RED 2 temperature telemetry when values change.
     248              :     int recordTemps( bool force = false /**< [in] force a telemetry record even if the cached values match */ );
     249              : 
     250              :     ///@}
     251              : 
     252              :   protected:
     253              :     /// Apply and verify the configured Camera Link serial baud rate.
     254              :     int setSerialBaud();
     255              : 
     256              :     /// Send a command over Camera Link serial and clean the response.
     257              :     int sendCommand( std::string       &response,         ///< [out] cleaned command response
     258              :                      const std::string &command,          /**< [in] CLI command to send */
     259              :                      bool               logFailure = true /**< [in] log transport failures when true */
     260              :     );
     261              : 
     262              :     /// Send a command that should return a success acknowledgement.
     263              :     int issueCommand( const std::string &command,                /**< [in] CLI command to send */
     264              :                       bool               allowNoResponse = false /**< [in] treat a missing response as acceptable */
     265              :     );
     266              : };
     267              : 
     268              : namespace
     269              : {
     270              : 
     271              : /// Normalize a C-RED 2 text response for tolerant string parsing.
     272           45 : inline std::string cred2LowerResponse( const std::string &response /**< [in] raw or cleaned CLI response */ )
     273              : {
     274           45 :     std::string clean = cred2CleanResponse( response );
     275           45 :     std::transform( clean.begin(),
     276              :                     clean.end(),
     277              :                     clean.begin(),
     278          326 :                     []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
     279              : 
     280           45 :     return clean;
     281              : }
     282              : 
     283              : /// Convert a C-RED 2 fan percentage into the nearest exposed preset name.
     284            8 : inline std::string cred2FanPresetName( float fanPercent /**< [in] current or requested fan percentage */ )
     285              : {
     286            8 :     if( fanPercent <= 0.5f )
     287              :     {
     288            2 :         return "off";
     289              :     }
     290              : 
     291            7 :     if( fanPercent < 37.5f )
     292              :     {
     293            2 :         return "p25";
     294              :     }
     295              : 
     296            6 :     if( fanPercent < 62.5f )
     297              :     {
     298            4 :         return "p50";
     299              :     }
     300              : 
     301            4 :     if( fanPercent < 87.5f )
     302              :     {
     303            6 :         return "p75";
     304              :     }
     305              : 
     306            2 :     return "p100";
     307              : }
     308              : 
     309              : /// Convert an exposed fan preset name into the corresponding manual fan percentage.
     310           10 : inline int cred2FanPresetPercent( int               &fanPercent, ///< [out] mapped manual fan percentage
     311              :                                   const std::string &fanPreset   /**< [in] exposed fan preset name */
     312              : )
     313              : {
     314           10 :     if( fanPreset == "off" )
     315              :     {
     316            1 :         fanPercent = 0;
     317            1 :         return 0;
     318              :     }
     319              : 
     320            9 :     if( fanPreset == "p25" )
     321              :     {
     322            3 :         fanPercent = 25;
     323            3 :         return 0;
     324              :     }
     325              : 
     326            6 :     if( fanPreset == "p50" )
     327              :     {
     328            1 :         fanPercent = 50;
     329            1 :         return 0;
     330              :     }
     331              : 
     332            5 :     if( fanPreset == "p75" )
     333              :     {
     334            2 :         fanPercent = 75;
     335            2 :         return 0;
     336              :     }
     337              : 
     338            3 :     if( fanPreset == "p100" )
     339              :     {
     340            1 :         fanPercent = 100;
     341            1 :         return 0;
     342              :     }
     343              : 
     344            2 :     return -1;
     345              : }
     346              : 
     347              : /// Parse a C-RED 2 sensibility response into the exposed analog-gain preset name.
     348           13 : inline int cred2AnalogGainName( std::string       &gainName, ///< [out] exposed analog-gain preset name
     349              :                                 const std::string &response  /**< [in] raw or cleaned sensibility response */
     350              : )
     351              : {
     352           13 :     std::string clean = cred2LowerResponse( response );
     353              : 
     354           13 :     if( clean.find( "medium" ) != std::string::npos || clean == "med" )
     355              :     {
     356            8 :         gainName = "med";
     357            8 :         return 0;
     358              :     }
     359              : 
     360            5 :     if( clean.find( "high" ) != std::string::npos )
     361              :     {
     362            2 :         gainName = "high";
     363            2 :         return 0;
     364              :     }
     365              : 
     366            3 :     if( clean.find( "low" ) != std::string::npos )
     367              :     {
     368            1 :         gainName = "low";
     369            1 :         return 0;
     370              :     }
     371              : 
     372            2 :     return -1;
     373           13 : }
     374              : 
     375              : /// Convert an exposed analog-gain preset name into the C-RED 2 command argument.
     376            7 : inline int cred2AnalogGainCommand( std::string       &commandGain, ///< [out] C-RED 2 sensibility command argument
     377              :                                    const std::string &gainName     /**< [in] exposed analog-gain preset name */
     378              : )
     379              : {
     380            7 :     if( gainName == "low" )
     381              :     {
     382            1 :         commandGain = "low";
     383            1 :         return 0;
     384              :     }
     385              : 
     386            6 :     if( gainName == "med" )
     387              :     {
     388            2 :         commandGain = "medium";
     389            2 :         return 0;
     390              :     }
     391              : 
     392            4 :     if( gainName == "high" )
     393              :     {
     394            2 :         commandGain = "high";
     395            2 :         return 0;
     396              :     }
     397              : 
     398            2 :     return -1;
     399              : }
     400              : 
     401              : } // namespace
     402              : 
     403          351 : inline cred2Ctrl::cred2Ctrl() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     404              : {
     405          117 :     m_powerMgtEnabled = true;
     406              : 
     407          117 :     m_startupTemp = 20;
     408          117 :     m_minTemp     = -40;
     409          117 :     m_maxTemp     = 20;
     410          117 :     m_stepTemp    = 1;
     411              : 
     412          117 :     m_stepFPS = 0.001;
     413              : 
     414          117 :     m_full_x     = 319.5;
     415          117 :     m_full_y     = 255.5;
     416          117 :     m_full_w     = 640;
     417          117 :     m_full_h     = 512;
     418          117 :     m_full_bin_x = 1;
     419          117 :     m_full_bin_y = 1;
     420              : 
     421          117 :     m_default_x     = m_full_x;
     422          117 :     m_default_y     = m_full_y;
     423          117 :     m_default_w     = m_full_w;
     424          117 :     m_default_h     = m_full_h;
     425          117 :     m_default_bin_x = m_full_bin_x;
     426          117 :     m_default_bin_y = m_full_bin_y;
     427              : 
     428          117 :     m_full_currbin_x = m_full_x;
     429          117 :     m_full_currbin_y = m_full_y;
     430          117 :     m_full_currbin_w = m_full_w;
     431          117 :     m_full_currbin_h = m_full_h;
     432              : 
     433          117 :     m_minROIx  = 0;
     434          117 :     m_maxROIx  = 639;
     435          117 :     m_stepROIx = 0.5;
     436              : 
     437          117 :     m_minROIy  = 0;
     438          117 :     m_maxROIy  = 511;
     439          117 :     m_stepROIy = 0.5;
     440              : 
     441          117 :     m_minROIWidth  = 32;
     442          117 :     m_maxROIWidth  = 640;
     443          117 :     m_stepROIWidth = 32;
     444              : 
     445          117 :     m_minROIHeight  = 4;
     446          117 :     m_maxROIHeight  = 512;
     447          117 :     m_stepROIHeight = 4;
     448              : 
     449          117 :     m_minROIBinning_x  = 1;
     450          117 :     m_maxROIBinning_x  = 1;
     451          117 :     m_stepROIBinning_x = 1;
     452              : 
     453          117 :     m_minROIBinning_y  = 1;
     454          117 :     m_maxROIBinning_y  = 1;
     455          117 :     m_stepROIBinning_y = 1;
     456              : 
     457          819 :     m_fanSpeedNames      = { "off", "p25", "p50", "p75", "p100", "auto" };
     458          819 :     m_fanSpeedNameLabels = { "Off", "25", "50", "75", "100", "Auto" };
     459          117 :     m_fanSpeedNameSet    = "auto";
     460              : 
     461          468 :     m_analogGainNames      = { "low", "med", "high" };
     462          468 :     m_analogGainNameLabels = { "Low", "Med", "High" };
     463          117 :     m_analogGainNameSet    = "med";
     464              : 
     465          117 :     m_ledStateSet = true;
     466              : 
     467          117 :     m_temps.setInvalid();
     468         1170 : }
     469              : 
     470          118 : inline cred2Ctrl::~cred2Ctrl() noexcept
     471              : {
     472          118 : }
     473              : 
     474           12 : inline void cred2Ctrl::setupConfig()
     475              : {
     476           12 :     STDCAMERA_SETUP_CONFIG( config );
     477              : 
     478           12 :     dev::edtCamera<cred2Ctrl>::setupConfig( config );
     479              : 
     480           12 :     FRAMEGRABBER_SETUP_CONFIG( config );
     481              : 
     482           12 :     TELEMETER_SETUP_CONFIG( config );
     483              : 
     484          180 :     config.add( "camera.serialBaud",
     485              :                 "",
     486              :                 "camera.serialBaud",
     487              :                 argType::Required,
     488              :                 "camera",
     489              :                 "serialBaud",
     490              :                 false,
     491              :                 "int",
     492              :                 "The Camera Link serial baud rate for C-RED 2 CLI commands. Default is 115200." );
     493              : }
     494              : 
     495           12 : inline int cred2Ctrl::loadConfigImpl( mx::app::appConfigurator &config )
     496              : {
     497           12 :     STDCAMERA_LOAD_CONFIG( config );
     498              : 
     499           12 :     config( m_serialBaud, "camera.serialBaud" );
     500              : 
     501           12 :     m_configFile = "/tmp/cred2_" + configName() + ".cfg";
     502              : 
     503           60 :     m_cameraModes["runtime"] = dev::cameraConfig( { m_configFile,
     504              :                                                     "",
     505           12 :                                                     static_cast<unsigned>( m_nextROI.x ),
     506           12 :                                                     static_cast<unsigned>( m_nextROI.y ),
     507           12 :                                                     static_cast<unsigned>( m_nextROI.w ),
     508           12 :                                                     static_cast<unsigned>( m_nextROI.h ),
     509           12 :                                                     static_cast<unsigned>( m_nextROI.bin_x ),
     510           12 :                                                     static_cast<unsigned>( m_nextROI.bin_y ),
     511              :                                                     1,
     512              :                                                     1,
     513           12 :                                                     0 } );
     514           12 :     m_startupMode            = "runtime";
     515              : 
     516           12 :     if( writeConfig() < 0 )
     517              :     {
     518            2 :         return log<software_critical, -1>( { __FILE__, __LINE__, "could not write initial C-RED 2 EDT config" } );
     519              :     }
     520              : 
     521           11 :     dev::edtCamera<cred2Ctrl>::loadConfig( config );
     522              : 
     523           11 :     FRAMEGRABBER_LOAD_CONFIG( config );
     524              : 
     525           11 :     TELEMETER_LOAD_CONFIG( config );
     526              : 
     527           11 :     return 0;
     528              : }
     529              : 
     530           12 : inline void cred2Ctrl::loadConfig()
     531              : {
     532           12 :     if( loadConfigImpl( config ) < 0 )
     533              :     {
     534            1 :         log<software_critical>( { __FILE__, __LINE__, "error loading config" } );
     535            1 :         m_shutdown = true;
     536              :     }
     537           12 : }
     538              : 
     539            4 : inline int cred2Ctrl::appStartup()
     540              : {
     541           12 :     REG_INDI_NEWPROP_NOCB( m_indiP_temps, "temps", pcf::IndiProperty::Number );
     542            8 :     m_indiP_temps.add( pcf::IndiElement( "motherboard" ) );
     543            8 :     m_indiP_temps["motherboard"].set( 0 );
     544            8 :     m_indiP_temps.add( pcf::IndiElement( "frontend" ) );
     545            8 :     m_indiP_temps["frontend"].set( 0 );
     546            8 :     m_indiP_temps.add( pcf::IndiElement( "powerboard" ) );
     547            8 :     m_indiP_temps["powerboard"].set( 0 );
     548            8 :     m_indiP_temps.add( pcf::IndiElement( "snake" ) );
     549            8 :     m_indiP_temps["snake"].set( 0 );
     550            8 :     m_indiP_temps.add( pcf::IndiElement( "setpoint" ) );
     551            8 :     m_indiP_temps["setpoint"].set( 0 );
     552            8 :     m_indiP_temps.add( pcf::IndiElement( "peltier" ) );
     553            8 :     m_indiP_temps["peltier"].set( 0 );
     554            8 :     m_indiP_temps.add( pcf::IndiElement( "heatsink" ) );
     555            8 :     m_indiP_temps["heatsink"].set( 0 );
     556              : 
     557           24 :     createROIndiNumber( m_indiP_fpsLimits, "fps_limits" );
     558            8 :     m_indiP_fpsLimits.add( pcf::IndiElement( "min" ) );
     559            8 :     m_indiP_fpsLimits["min"].set( m_minFPS );
     560           16 :     m_indiP_fpsLimits["min"].setFormat( "%0.6f" );
     561            8 :     m_indiP_fpsLimits.add( pcf::IndiElement( "max" ) );
     562            8 :     m_indiP_fpsLimits["max"].set( m_maxFPS );
     563           16 :     m_indiP_fpsLimits["max"].setFormat( "%0.6f" );
     564              : 
     565            4 :     if( registerIndiPropertyReadOnly( m_indiP_fpsLimits ) < 0 )
     566              :     {
     567            1 :         return log<software_critical, -1>( { __FILE__, __LINE__ } );
     568              :     }
     569              : 
     570            3 :     STDCAMERA_APP_STARTUP;
     571              : 
     572            3 :     if( dev::edtCamera<cred2Ctrl>::appStartup() < 0 )
     573              :     {
     574            2 :         return log<software_critical, -1>( { __FILE__, __LINE__ } );
     575              :     }
     576              : 
     577            1 :     if( setSerialBaud() < 0 )
     578              :     {
     579            0 :         return log<software_critical, -1>( { __FILE__, __LINE__ } );
     580              :     }
     581              : 
     582            1 :     FRAMEGRABBER_APP_STARTUP;
     583              : 
     584            1 :     TELEMETER_APP_STARTUP;
     585              : 
     586            1 :     return 0;
     587              : }
     588              : 
     589           23 : inline int cred2Ctrl::appLogic()
     590              : {
     591           23 :     STDCAMERA_APP_LOGIC;
     592              : 
     593           23 :     if( dev::edtCamera<cred2Ctrl>::appLogic() < 0 )
     594              :     {
     595            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     596              :     }
     597              : 
     598           23 :     FRAMEGRABBER_APP_LOGIC;
     599              : 
     600           23 :     if( state() == stateCodes::POWERON )
     601              :     {
     602            1 :         return 0;
     603              :     }
     604              : 
     605           22 :     if( state() == stateCodes::NOTCONNECTED || state() == stateCodes::NODEVICE || state() == stateCodes::ERROR )
     606              :     {
     607            4 :         if( powerState() == 0 )
     608              :         {
     609            3 :             return 0;
     610              :         }
     611              : 
     612            3 :         std::string response;
     613            6 :         if( sendCommand( response, "fps raw" ) == 0 )
     614              :         {
     615            2 :             float fpsValue = 0;
     616            2 :             if( cred2ParseFloat( fpsValue, response ) == 0 )
     617              :             {
     618            1 :                 state( stateCodes::CONNECTED );
     619              :             }
     620              :             else
     621              :             {
     622            1 :                 state( stateCodes::NODEVICE );
     623            1 :                 sleep( 1 );
     624            1 :                 return 0;
     625              :             }
     626              :         }
     627              :         else
     628              :         {
     629            1 :             state( stateCodes::NODEVICE );
     630            1 :             sleep( 1 );
     631            1 :             return 0;
     632              :         }
     633            3 :     }
     634              : 
     635           19 :     if( state() == stateCodes::CONNECTED )
     636              :     {
     637            7 :         std::unique_lock<std::mutex> lock( m_indiMutex );
     638              : 
     639            7 :         if( syncROIFromCamera() < 0 )
     640              :         {
     641            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
     642              :             {
     643            1 :                 return 0;
     644              :             }
     645              : 
     646            1 :             state( stateCodes::ERROR );
     647            1 :             return 0;
     648              :         }
     649              : 
     650            8 :         if( updateFPSLimits() < 0 || getTemps() < 0 || getFPS() < 0 || getFanSpeed() < 0 || getAnalogGain() < 0 ||
     651            3 :             getLEDState() < 0 )
     652              :         {
     653            2 :             if( powerState() != 1 || powerStateTarget() != 1 )
     654              :             {
     655            1 :                 return 0;
     656              :             }
     657              : 
     658            1 :             state( stateCodes::ERROR );
     659            1 :             return 0;
     660              :         }
     661              : 
     662            3 :         state( stateCodes::READY );
     663              : 
     664            3 :         if( m_ccdTempSetpt > -999 )
     665              :         {
     666            3 :             if( setTempSetPt() < 0 )
     667              :             {
     668            3 :                 if( powerState() != 1 || powerStateTarget() != 1 )
     669              :                 {
     670            1 :                     return 0;
     671              :                 }
     672              : 
     673            2 :                 return log<software_error, 0>( { __FILE__, __LINE__ } );
     674              :             }
     675              :         }
     676            7 :     }
     677              : 
     678           12 :     if( state() == stateCodes::READY || state() == stateCodes::OPERATING )
     679              :     {
     680           12 :         std::unique_lock<std::mutex> lock( m_indiMutex, std::try_to_lock );
     681           12 :         if( !lock.owns_lock() )
     682              :         {
     683            1 :             return 0;
     684              :         }
     685              : 
     686           11 :         if( m_roiSettleCounter > 0 )
     687              :         {
     688            1 :             --m_roiSettleCounter;
     689              :         }
     690              :         else
     691              :         {
     692           10 :             if( updateFPSLimits() < 0 )
     693              :             {
     694            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
     695              :                 {
     696            1 :                     return 0;
     697              :                 }
     698              : 
     699            1 :                 state( stateCodes::ERROR );
     700            1 :                 return 0;
     701              :             }
     702              : 
     703            8 :             if( getTemps() < 0 )
     704              :             {
     705            0 :                 if( powerState() != 1 || powerStateTarget() != 1 )
     706              :                 {
     707            0 :                     return 0;
     708              :                 }
     709              : 
     710            0 :                 state( stateCodes::ERROR );
     711            0 :                 return 0;
     712              :             }
     713              : 
     714            8 :             if( getFPS() < 0 )
     715              :             {
     716            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
     717              :                 {
     718            1 :                     return 0;
     719              :                 }
     720              : 
     721            1 :                 state( stateCodes::ERROR );
     722            1 :                 return 0;
     723              :             }
     724              : 
     725            6 :             if( getFanSpeed() < 0 )
     726              :             {
     727            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
     728              :                 {
     729            1 :                     return 0;
     730              :                 }
     731              : 
     732            1 :                 state( stateCodes::ERROR );
     733            1 :                 return 0;
     734              :             }
     735              : 
     736            4 :             if( getAnalogGain() < 0 )
     737              :             {
     738            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
     739              :                 {
     740            1 :                     return 0;
     741              :                 }
     742              : 
     743            1 :                 state( stateCodes::ERROR );
     744            1 :                 return 0;
     745              :             }
     746              : 
     747            2 :             if( getLEDState() < 0 )
     748              :             {
     749            2 :                 if( powerState() != 1 || powerStateTarget() != 1 )
     750              :                 {
     751            1 :                     return 0;
     752              :                 }
     753              : 
     754            1 :                 state( stateCodes::ERROR );
     755            1 :                 return 0;
     756              :             }
     757              :         }
     758              : 
     759            1 :         if( frameGrabber<cred2Ctrl>::updateINDI() < 0 )
     760              :         {
     761            0 :             log<software_error>( { __FILE__, __LINE__ } );
     762            0 :             state( stateCodes::ERROR );
     763            0 :             return 0;
     764              :         }
     765              : 
     766            1 :         if( stdCamera<cred2Ctrl>::updateINDI() < 0 )
     767              :         {
     768            0 :             log<software_error>( { __FILE__, __LINE__ } );
     769            0 :             state( stateCodes::ERROR );
     770            0 :             return 0;
     771              :         }
     772              : 
     773            1 :         if( edtCamera<cred2Ctrl>::updateINDI() < 0 )
     774              :         {
     775            0 :             log<software_error>( { __FILE__, __LINE__ } );
     776            0 :             state( stateCodes::ERROR );
     777            0 :             return 0;
     778              :         }
     779              : 
     780            1 :         if( telemeter<cred2Ctrl>::appLogic() < 0 )
     781              :         {
     782            1 :             log<software_error>( { __FILE__, __LINE__ } );
     783            1 :             return 0;
     784              :         }
     785           12 :     }
     786              : 
     787            0 :     return 0;
     788              : }
     789              : 
     790            1 : inline int cred2Ctrl::onPowerOff()
     791              : {
     792            1 :     m_powerOnCounter = 0;
     793              : 
     794            1 :     std::lock_guard<std::mutex> lock( m_indiMutex );
     795              : 
     796            1 :     m_temps.setInvalid();
     797            1 :     m_ccdTemp              = -999;
     798            1 :     m_tempControlStatus    = false;
     799            1 :     m_tempControlOnTarget  = false;
     800            1 :     m_tempControlStatusStr = "UNKNOWN";
     801              : 
     802            2 :     updateIfChanged( m_indiP_temps, "motherboard", m_temps.motherboard );
     803            2 :     updateIfChanged( m_indiP_temps, "frontend", m_temps.frontend );
     804            2 :     updateIfChanged( m_indiP_temps, "powerboard", m_temps.powerboard );
     805            2 :     updateIfChanged( m_indiP_temps, "snake", m_temps.snake );
     806            2 :     updateIfChanged( m_indiP_temps, "setpoint", m_temps.setpoint );
     807            2 :     updateIfChanged( m_indiP_temps, "peltier", m_temps.peltier );
     808            2 :     updateIfChanged( m_indiP_temps, "heatsink", m_temps.heatsink );
     809              : 
     810            1 :     if( stdCamera<cred2Ctrl>::onPowerOff() < 0 )
     811              :     {
     812            0 :         log<software_error>( { __FILE__, __LINE__ } );
     813              :     }
     814              : 
     815            1 :     if( edtCamera<cred2Ctrl>::onPowerOff() < 0 )
     816              :     {
     817            0 :         log<software_error>( { __FILE__, __LINE__ } );
     818              :     }
     819              : 
     820            1 :     if( frameGrabber<cred2Ctrl>::onPowerOff() < 0 )
     821              :     {
     822            0 :         log<software_error>( { __FILE__, __LINE__ } );
     823              :     }
     824              : 
     825            1 :     return 0;
     826            1 : }
     827              : 
     828            1 : inline int cred2Ctrl::whilePowerOff()
     829              : {
     830            1 :     std::lock_guard<std::mutex> lock( m_indiMutex );
     831              : 
     832            1 :     if( stdCamera<cred2Ctrl>::whilePowerOff() < 0 )
     833              :     {
     834            0 :         log<software_error>( { __FILE__, __LINE__ } );
     835              :     }
     836              : 
     837            1 :     if( edtCamera<cred2Ctrl>::whilePowerOff() < 0 )
     838              :     {
     839            0 :         log<software_error>( { __FILE__, __LINE__ } );
     840              :     }
     841              : 
     842            1 :     return 0;
     843            1 : }
     844              : 
     845            2 : inline int cred2Ctrl::appShutdown()
     846              : {
     847            2 :     STDCAMERA_APP_SHUTDOWN;
     848              : 
     849            2 :     dev::edtCamera<cred2Ctrl>::appShutdown();
     850              : 
     851            2 :     FRAMEGRABBER_APP_SHUTDOWN;
     852              : 
     853            2 :     TELEMETER_APP_SHUTDOWN;
     854              : 
     855            2 :     return 0;
     856              : }
     857              : 
     858          198 : inline int cred2Ctrl::sendCommand( std::string &response, const std::string &command, bool logFailure )
     859              : {
     860          198 :     std::string rawResponse;
     861              : 
     862          198 :     response.clear();
     863              : 
     864              :     { // mutex scope
     865          198 :         std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
     866          198 :         if( pdvSerialWriteRead( rawResponse, command, logFailure ) != 0 )
     867              :         {
     868           26 :             if( powerState() != 1 || powerStateTarget() != 1 )
     869              :             {
     870            6 :                 return -1;
     871              :             }
     872              : 
     873           20 :             if( !logFailure )
     874              :             {
     875            7 :                 return -1;
     876              :             }
     877              : 
     878           13 :             return log<software_error, -1>( { __FILE__, __LINE__, "error sending C-RED 2 command: " + command } );
     879              :         }
     880          198 :     }
     881              : 
     882          172 :     response = cred2CleanResponse( rawResponse );
     883              : 
     884          172 :     return 0;
     885          198 : }
     886              : 
     887           30 : inline int cred2Ctrl::issueCommand( const std::string &command, bool allowNoResponse )
     888              : {
     889           30 :     std::string response;
     890           30 :     if( sendCommand( response, command, !allowNoResponse ) < 0 )
     891              :     {
     892            7 :         if( allowNoResponse )
     893              :         {
     894            5 :             return 0;
     895              :         }
     896              : 
     897            2 :         return -1;
     898              :     }
     899              : 
     900           23 :     if( !cred2ResponseOK( response ) )
     901              :     {
     902           22 :         return log<text_log, -1>( "C-RED 2 rejected command '" + command + "' with response: " + response,
     903           11 :                                   logPrio::LOG_ERROR );
     904              :     }
     905              : 
     906           12 :     return 0;
     907           30 : }
     908              : 
     909           17 : inline int cred2Ctrl::syncROIFromCamera()
     910              : {
     911           17 :     std::string response;
     912           17 :     bool        cropEnabled = false;
     913           17 :     int         startColumn = 0;
     914           17 :     int         endColumn   = 0;
     915           17 :     int         startRow    = 0;
     916           17 :     int         endRow      = 0;
     917              : 
     918           68 :     if( sendCommand( response, "cropping raw", false ) < 0 ||
     919           17 :         cred2ParseCropState( cropEnabled, startColumn, endColumn, startRow, endRow, response ) < 0 )
     920              :     {
     921            3 :         return log<software_error, -1>( { __FILE__, __LINE__, "failed to query current cropping mode: " + response } );
     922              :     }
     923              : 
     924           14 :     if( !cropEnabled )
     925              :     {
     926            9 :         m_cameraCropEnabled = false;
     927            9 :         m_currentROI.x      = m_full_x;
     928            9 :         m_currentROI.y      = m_full_y;
     929            9 :         m_currentROI.w      = m_full_w;
     930            9 :         m_currentROI.h      = m_full_h;
     931            9 :         m_currentROI.bin_x  = m_full_bin_x;
     932            9 :         m_currentROI.bin_y  = m_full_bin_y;
     933              :     }
     934              :     else
     935              :     {
     936            5 :         cred2Roi cameraROI;
     937              : 
     938            5 :         if( startColumn == 0 && endColumn == 0 && startRow == 0 && endRow == 0 )
     939              :         {
     940           12 :             if( sendCommand( response, "cropping columns raw", false ) < 0 ||
     941            3 :                 cred2ParseRange( cameraROI.startColumn, cameraROI.endColumn, response ) < 0 )
     942              :             {
     943            2 :                 return log<software_error, -1>(
     944            3 :                     { __FILE__, __LINE__, "failed to query current cropping columns: " + response } );
     945              :             }
     946              : 
     947            8 :             if( sendCommand( response, "cropping rows raw", false ) < 0 ||
     948            2 :                 cred2ParseRange( cameraROI.startRow, cameraROI.endRow, response ) < 0 )
     949              :             {
     950            2 :                 return log<software_error, -1>(
     951            3 :                     { __FILE__, __LINE__, "failed to query current cropping rows: " + response } );
     952              :             }
     953              :         }
     954              :         else
     955              :         {
     956            2 :             cameraROI.startColumn = startColumn;
     957            2 :             cameraROI.endColumn   = endColumn;
     958            2 :             cameraROI.startRow    = startRow;
     959            2 :             cameraROI.endRow      = endRow;
     960              :         }
     961              : 
     962            3 :         cameraROI.fullFrame = false;
     963              : 
     964            6 :         if( cred2RoiToCenter(
     965            3 :                 m_currentROI.x, m_currentROI.y, m_currentROI.w, m_currentROI.h, cameraROI, m_full_w, m_full_h ) < 0 )
     966              :         {
     967            2 :             return log<software_error, -1>( { __FILE__, __LINE__, "camera reported an invalid ROI" } );
     968              :         }
     969              : 
     970            2 :         m_currentROI.bin_x  = 1;
     971            2 :         m_currentROI.bin_y  = 1;
     972            2 :         m_cameraCropEnabled = true;
     973              :     }
     974              : 
     975           11 :     m_nextROI  = m_currentROI;
     976           11 :     m_width    = m_currentROI.w;
     977           11 :     m_height   = m_currentROI.h;
     978           11 :     m_dataType = _DATATYPE_INT16;
     979              : 
     980           22 :     updateIfChanged( m_indiP_roi_x, "current", m_currentROI.x, INDI_OK );
     981           22 :     updateIfChanged( m_indiP_roi_y, "current", m_currentROI.y, INDI_OK );
     982           22 :     updateIfChanged( m_indiP_roi_w, "current", m_currentROI.w, INDI_OK );
     983           22 :     updateIfChanged( m_indiP_roi_h, "current", m_currentROI.h, INDI_OK );
     984           22 :     updateIfChanged( m_indiP_roi_bin_x, "current", m_currentROI.bin_x, INDI_OK );
     985           22 :     updateIfChanged( m_indiP_roi_bin_y, "current", m_currentROI.bin_y, INDI_OK );
     986              : 
     987           22 :     updateIfChanged( m_indiP_roi_x, "target", m_nextROI.x, INDI_OK );
     988           22 :     updateIfChanged( m_indiP_roi_y, "target", m_nextROI.y, INDI_OK );
     989           22 :     updateIfChanged( m_indiP_roi_w, "target", m_nextROI.w, INDI_OK );
     990           22 :     updateIfChanged( m_indiP_roi_h, "target", m_nextROI.h, INDI_OK );
     991           22 :     updateIfChanged( m_indiP_roi_bin_x, "target", m_nextROI.bin_x, INDI_OK );
     992           22 :     updateIfChanged( m_indiP_roi_bin_y, "target", m_nextROI.bin_y, INDI_OK );
     993              : 
     994           11 :     if( m_currentROI.w != m_raw_width || m_currentROI.h != m_raw_height )
     995              :     {
     996            3 :         if( writeConfig() < 0 )
     997              :         {
     998            1 :             return log<software_error, -1>( { __FILE__, __LINE__ } );
     999              :         }
    1000              : 
    1001            2 :         m_nextMode = m_modeName.empty() ? m_startupMode : m_modeName;
    1002              : 
    1003            2 :         if( dev::edtCamera<cred2Ctrl>::pdvReconfig() < 0 )
    1004              :         {
    1005            0 :             return log<software_error, -1>( { __FILE__, __LINE__ } );
    1006              :         }
    1007              : 
    1008            2 :         if( setSerialBaud() < 0 )
    1009              :         {
    1010            1 :             return log<software_error, -1>( { __FILE__, __LINE__ } );
    1011              :         }
    1012              :     }
    1013              : 
    1014            9 :     return 0;
    1015           17 : }
    1016              : 
    1017            5 : inline int cred2Ctrl::setSerialBaud()
    1018              : {
    1019              :     typedef int ( *setBaudFnT )( PdvDev *, int );
    1020              :     typedef int ( *getBaudFnT )( PdvDev * );
    1021              : 
    1022            5 :     if( m_pdv == nullptr )
    1023              :     {
    1024            4 :         return log<software_error, -1>( { __FILE__, __LINE__, "cannot set serial baud with null PDV handle" } );
    1025              :     }
    1026              : 
    1027            3 :     static setBaudFnT setBaudFn = reinterpret_cast<setBaudFnT>( dlsym( RTLD_DEFAULT, "pdv_serial_set_baud" ) );
    1028            3 :     static getBaudFnT getBaudFn = reinterpret_cast<getBaudFnT>( dlsym( RTLD_DEFAULT, "pdv_serial_get_baud" ) );
    1029              : 
    1030            3 :     if( setBaudFn == nullptr || getBaudFn == nullptr )
    1031              :     {
    1032            3 :         return 0;
    1033              :     }
    1034              : 
    1035            0 :     if( setBaudFn( m_pdv, m_serialBaud ) < 0 )
    1036              :     {
    1037            0 :         return log<software_error, -1>(
    1038            0 :             { __FILE__, __LINE__, "failed to set C-RED 2 serial baud to " + std::to_string( m_serialBaud ) } );
    1039              :     }
    1040              : 
    1041            0 :     int actualBaud = getBaudFn( m_pdv );
    1042            0 :     if( actualBaud != m_serialBaud )
    1043              :     {
    1044            0 :         return log<software_error, -1>( { __FILE__,
    1045              :                                           __LINE__,
    1046            0 :                                           "EDT serial baud verification failed: expected " +
    1047            0 :                                               std::to_string( m_serialBaud ) + ", got " +
    1048            0 :                                               std::to_string( actualBaud ) } );
    1049              :     }
    1050              : 
    1051            0 :     return 0;
    1052              : }
    1053              : 
    1054           18 : inline int cred2Ctrl::getTemps()
    1055              : {
    1056           18 :     cred2Temps         temps;
    1057           18 :     std::string        response;
    1058           18 :     std::vector<float> bundledTemps;
    1059           18 :     const double       diffLimit = 1.0;
    1060           36 :     const std::string  bundledCommand( "temperatures raw" );
    1061           18 :     const std::string  setpointCommand( "temperatures snake setpoint raw" );
    1062            5 :     const auto         keepLastTemps = [this]( const std::string &detail )
    1063              :     {
    1064           10 :         return log<text_log, 0>( "transient C-RED 2 temperature refresh failure; keeping previous cached values: " +
    1065              :                                      detail,
    1066           10 :                                  logPrio::LOG_WARNING );
    1067           18 :     };
    1068              : 
    1069           18 :     if( sendCommand( response, bundledCommand, false ) < 0 )
    1070              :     {
    1071            2 :         return keepLastTemps( bundledCommand );
    1072              :     }
    1073              : 
    1074           16 :     if( cred2ParseFloatVector( bundledTemps, response, 6 ) < 0 )
    1075              :     {
    1076            1 :         return keepLastTemps( bundledCommand + " -> " + response );
    1077              :     }
    1078              : 
    1079           15 :     temps.motherboard = bundledTemps[0];
    1080           15 :     temps.frontend    = bundledTemps[1];
    1081           15 :     temps.powerboard  = bundledTemps[2];
    1082           15 :     temps.snake       = bundledTemps[3];
    1083           15 :     temps.peltier     = bundledTemps[4];
    1084           15 :     temps.heatsink    = bundledTemps[5];
    1085              : 
    1086           15 :     if( sendCommand( response, setpointCommand, false ) < 0 )
    1087              :     {
    1088            1 :         return keepLastTemps( setpointCommand );
    1089              :     }
    1090              : 
    1091           14 :     if( cred2ParseFloat( temps.setpoint, response ) < 0 )
    1092              :     {
    1093            1 :         return keepLastTemps( setpointCommand + " -> " + response );
    1094              :     }
    1095              : 
    1096           13 :     m_temps        = temps;
    1097           13 :     m_ccdTemp      = temps.snake;
    1098           13 :     m_ccdTempSetpt = temps.setpoint;
    1099              : 
    1100           13 :     if( m_ccdTempSetpt < 19.5 )
    1101              :     {
    1102           11 :         m_tempControlStatus = true;
    1103           11 :         if( std::fabs( m_ccdTemp - m_ccdTempSetpt ) < diffLimit )
    1104              :         {
    1105           10 :             m_tempControlStatusStr = "ON TARGET";
    1106           10 :             m_tempControlOnTarget  = true;
    1107              :         }
    1108              :         else
    1109              :         {
    1110            1 :             m_tempControlStatusStr = "OFF TARGET";
    1111            1 :             m_tempControlOnTarget  = false;
    1112              :         }
    1113              :     }
    1114              :     else
    1115              :     {
    1116            2 :         m_tempControlStatus   = false;
    1117            2 :         m_tempControlOnTarget = false;
    1118            2 :         if( std::fabs( m_ccdTemp - m_ccdTempSetpt ) < diffLimit )
    1119              :         {
    1120            1 :             m_tempControlStatusStr = "TEMP OFF";
    1121              :         }
    1122              :         else
    1123              :         {
    1124            1 :             m_tempControlStatusStr = "WARMING";
    1125              :         }
    1126              :     }
    1127              : 
    1128           26 :     updateIfChanged( m_indiP_temps, "motherboard", m_temps.motherboard );
    1129           26 :     updateIfChanged( m_indiP_temps, "frontend", m_temps.frontend );
    1130           26 :     updateIfChanged( m_indiP_temps, "powerboard", m_temps.powerboard );
    1131           26 :     updateIfChanged( m_indiP_temps, "snake", m_temps.snake );
    1132           26 :     updateIfChanged( m_indiP_temps, "setpoint", m_temps.setpoint );
    1133           26 :     updateIfChanged( m_indiP_temps, "peltier", m_temps.peltier );
    1134           26 :     updateIfChanged( m_indiP_temps, "heatsink", m_temps.heatsink );
    1135              : 
    1136           13 :     recordTemps();
    1137           13 :     recordCamera();
    1138              : 
    1139           13 :     return 0;
    1140           18 : }
    1141              : 
    1142           14 : inline int cred2Ctrl::getFPS()
    1143              : {
    1144           14 :     std::string response;
    1145           14 :     float       fpsValue = 0;
    1146              : 
    1147           28 :     if( sendCommand( response, "fps raw" ) < 0 )
    1148              :     {
    1149            2 :         return -1;
    1150              :     }
    1151              : 
    1152           12 :     if( cred2ParseFloat( fpsValue, response ) < 0 )
    1153              :     {
    1154            1 :         return log<software_error, -1>( { __FILE__, __LINE__, "failed to parse fps response: " + response } );
    1155              :     }
    1156              : 
    1157           11 :     m_fps = fpsValue;
    1158           11 :     recordCamera();
    1159              : 
    1160           11 :     return 0;
    1161           14 : }
    1162              : 
    1163           19 : inline int cred2Ctrl::getFanSpeed()
    1164              : {
    1165           19 :     std::string response;
    1166           19 :     std::string fanMode;
    1167           19 :     float       fanPercent = 0;
    1168              : 
    1169           38 :     if( sendCommand( response, "fan mode raw" ) < 0 )
    1170              :     {
    1171            3 :         return -1;
    1172              :     }
    1173              : 
    1174           16 :     fanMode = cred2LowerResponse( response );
    1175           16 :     if( fanMode.find( "auto" ) == std::string::npos && fanMode.find( "manual" ) == std::string::npos )
    1176              :     {
    1177            6 :         if( sendCommand( response, "fan mode" ) < 0 )
    1178              :         {
    1179            1 :             return -1;
    1180              :         }
    1181              : 
    1182            2 :         fanMode = cred2LowerResponse( response );
    1183              :     }
    1184              : 
    1185           15 :     if( fanMode.find( "auto" ) != std::string::npos )
    1186              :     {
    1187            9 :         m_fanSpeedName    = "auto";
    1188            9 :         m_fanSpeedNameSet = m_fanSpeedName;
    1189            9 :         m_fanSpeedValid   = true;
    1190            9 :         return 0;
    1191              :     }
    1192              : 
    1193            6 :     if( fanMode.find( "manual" ) == std::string::npos )
    1194              :     {
    1195            1 :         return log<software_error, -1>( { __FILE__, __LINE__, "failed to parse fan mode response: " + response } );
    1196              :     }
    1197              : 
    1198           10 :     if( sendCommand( response, "fan speed raw" ) < 0 )
    1199              :     {
    1200            1 :         return -1;
    1201              :     }
    1202              : 
    1203            4 :     if( cred2ParseFloat( fanPercent, response ) < 0 )
    1204              :     {
    1205            6 :         if( sendCommand( response, "fan speed" ) < 0 || cred2ParseFloat( fanPercent, response ) < 0 )
    1206              :         {
    1207            1 :             return log<software_error, -1>( { __FILE__, __LINE__, "failed to parse fan speed response: " + response } );
    1208              :         }
    1209              :     }
    1210              : 
    1211            3 :     m_fanSpeedName    = cred2FanPresetName( fanPercent );
    1212            3 :     m_fanSpeedNameSet = m_fanSpeedName;
    1213            3 :     m_fanSpeedValid   = true;
    1214            3 :     recordCamera();
    1215              : 
    1216            3 :     return 0;
    1217           19 : }
    1218              : 
    1219           11 : inline int cred2Ctrl::getAnalogGain()
    1220              : {
    1221           11 :     std::string response;
    1222           11 :     std::string analogGain;
    1223              : 
    1224           22 :     if( sendCommand( response, "sensibility" ) < 0 )
    1225              :     {
    1226            3 :         return -1;
    1227              :     }
    1228              : 
    1229            8 :     if( cred2AnalogGainName( analogGain, response ) < 0 )
    1230              :     {
    1231            1 :         return log<software_error, -1>( { __FILE__, __LINE__, "failed to parse sensibility response: " + response } );
    1232              :     }
    1233              : 
    1234            7 :     m_analogGainName    = analogGain;
    1235            7 :     m_analogGainNameSet = m_analogGainName;
    1236            7 :     m_analogGainValid   = true;
    1237            7 :     recordCamera();
    1238              : 
    1239            7 :     return 0;
    1240           11 : }
    1241              : 
    1242           15 : inline int cred2Ctrl::getLEDState()
    1243              : {
    1244           15 :     std::string response;
    1245           15 :     bool        ledState = false;
    1246              : 
    1247           30 :     if( sendCommand( response, "led raw" ) < 0 )
    1248              :     {
    1249            4 :         if( sendCommand( response, "led" ) < 0 )
    1250              :         {
    1251            1 :             return -1;
    1252              :         }
    1253              :     }
    1254              : 
    1255           14 :     if( cred2ParseBool( ledState, response ) < 0 )
    1256              :     {
    1257            8 :         std::string clean = cred2LowerResponse( response );
    1258              : 
    1259            8 :         if( clean.find( "off" ) != std::string::npos )
    1260              :         {
    1261            1 :             ledState = false;
    1262              :         }
    1263            7 :         else if( clean.find( "on" ) != std::string::npos )
    1264              :         {
    1265            1 :             ledState = true;
    1266              :         }
    1267              :         else
    1268              :         {
    1269           12 :             if( sendCommand( response, "led" ) < 0 )
    1270              :             {
    1271            1 :                 return -1;
    1272              :             }
    1273              : 
    1274            5 :             clean = cred2LowerResponse( response );
    1275            5 :             if( clean.find( "off" ) != std::string::npos )
    1276              :             {
    1277            1 :                 ledState = false;
    1278              :             }
    1279            4 :             else if( clean.find( "on" ) != std::string::npos )
    1280              :             {
    1281            1 :                 ledState = true;
    1282              :             }
    1283              :             else
    1284              :             {
    1285            3 :                 return log<software_error, -1>( { __FILE__, __LINE__, "failed to parse led response: " + response } );
    1286              :             }
    1287              :         }
    1288            8 :     }
    1289              : 
    1290           10 :     m_ledState      = ledState;
    1291           10 :     m_ledStateSet   = ledState;
    1292           10 :     m_ledStateValid = true;
    1293           10 :     recordCamera();
    1294              : 
    1295           10 :     return 0;
    1296           15 : }
    1297              : 
    1298           18 : inline int cred2Ctrl::updateFPSLimits()
    1299              : {
    1300           18 :     std::string response;
    1301           18 :     float       minFPS = 0;
    1302           18 :     float       maxFPS = 0;
    1303              : 
    1304           54 :     if( sendCommand( response, "minfps raw" ) < 0 || cred2ParseFloat( minFPS, response ) < 0 )
    1305              :     {
    1306            5 :         return log<software_error, -1>( { __FILE__, __LINE__, "failed to parse minfps response: " + response } );
    1307              :     }
    1308              : 
    1309           39 :     if( sendCommand( response, "maxfps raw" ) < 0 || cred2ParseFloat( maxFPS, response ) < 0 )
    1310              :     {
    1311            1 :         return log<software_error, -1>( { __FILE__, __LINE__, "failed to parse maxfps response: " + response } );
    1312              :     }
    1313              : 
    1314           12 :     m_minFPS = minFPS;
    1315           12 :     m_maxFPS = maxFPS;
    1316           12 :     m_fpsSet = std::clamp( m_fpsSet, m_minFPS, m_maxFPS );
    1317              : 
    1318           24 :     updateIfChanged( m_indiP_fpsLimits, "min", m_minFPS, INDI_IDLE );
    1319           24 :     updateIfChanged( m_indiP_fpsLimits, "max", m_maxFPS, INDI_IDLE );
    1320              : 
    1321           12 :     recordCamera();
    1322              : 
    1323           12 :     return 0;
    1324           18 : }
    1325              : 
    1326            1 : inline int cred2Ctrl::powerOnDefaults()
    1327              : {
    1328            1 :     m_tempControlStatus    = false;
    1329            1 :     m_tempControlStatusSet = false;
    1330            1 :     m_tempControlStatusStr = "TEMP OFF";
    1331            1 :     m_tempControlOnTarget  = false;
    1332            1 :     m_cameraCropEnabled    = false;
    1333            1 :     m_currentROI.x         = m_default_x;
    1334            1 :     m_currentROI.y         = m_default_y;
    1335            1 :     m_currentROI.w         = m_default_w;
    1336            1 :     m_currentROI.h         = m_default_h;
    1337            1 :     m_currentROI.bin_x     = m_default_bin_x;
    1338            1 :     m_currentROI.bin_y     = m_default_bin_y;
    1339              : 
    1340            1 :     m_fanSpeedValid     = false;
    1341            1 :     m_analogGainValid   = false;
    1342            1 :     m_ledStateValid     = false;
    1343            1 :     m_fanSpeedNameSet   = "auto";
    1344            1 :     m_analogGainNameSet = "med";
    1345            1 :     m_ledStateSet       = true;
    1346              : 
    1347            1 :     m_nextROI = m_currentROI;
    1348              : 
    1349            1 :     return 0;
    1350              : }
    1351              : 
    1352            3 : inline int cred2Ctrl::setTempControl()
    1353              : {
    1354            3 :     if( m_tempControlStatusSet )
    1355              :     {
    1356            2 :         if( m_ccdTempSetpt >= 19.5 )
    1357              :         {
    1358            2 :             return log<text_log, 0>(
    1359              :                 "temperature control is setpoint-driven for C-RED 2; choose a target below 20 C to cool",
    1360            1 :                 logPrio::LOG_NOTICE );
    1361              :         }
    1362              : 
    1363            1 :         return setTempSetPt();
    1364              :     }
    1365              : 
    1366            1 :     m_ccdTempSetpt = 20;
    1367            1 :     return setTempSetPt();
    1368              : }
    1369              : 
    1370            6 : inline int cred2Ctrl::setTempSetPt()
    1371              : {
    1372            6 :     if( m_ccdTempSetpt < m_minTemp || m_ccdTempSetpt > m_maxTemp )
    1373              :     {
    1374            2 :         return log<text_log, -1>( "attempt to set temperature outside valid range: " + std::to_string( m_ccdTempSetpt ),
    1375            1 :                                   logPrio::LOG_ERROR );
    1376              :     }
    1377              : 
    1378            5 :     std::ostringstream command;
    1379            5 :     command << "set temperatures snake " << m_ccdTempSetpt;
    1380              : 
    1381            5 :     if( issueCommand( command.str() ) < 0 )
    1382              :     {
    1383            3 :         return -1;
    1384              :     }
    1385              : 
    1386            2 :     m_tempControlStatusSet = ( m_ccdTempSetpt < 19.5 );
    1387            2 :     m_tempControlStatus    = m_tempControlStatusSet;
    1388            2 :     m_tempControlOnTarget  = false;
    1389            2 :     m_tempControlStatusStr = m_tempControlStatusSet ? "OFF TARGET" : "WARMING";
    1390              : 
    1391            2 :     recordCamera();
    1392              : 
    1393            2 :     return 0;
    1394            5 : }
    1395              : 
    1396            3 : inline int cred2Ctrl::setFPS()
    1397              : {
    1398            3 :     if( m_fpsSet < m_minFPS || m_fpsSet > m_maxFPS )
    1399              :     {
    1400            2 :         return log<text_log, -1>( "attempt to set fps outside valid range: " + std::to_string( m_fpsSet ),
    1401            1 :                                   logPrio::LOG_ERROR );
    1402              :     }
    1403              : 
    1404            2 :     std::ostringstream command;
    1405            2 :     command << "set fps " << m_fpsSet;
    1406              : 
    1407            2 :     if( issueCommand( command.str() ) < 0 )
    1408              :     {
    1409            1 :         return -1;
    1410              :     }
    1411              : 
    1412            1 :     log<text_log>( "set fps: " + std::to_string( m_fpsSet ) );
    1413              : 
    1414            1 :     return getFPS();
    1415            2 : }
    1416              : 
    1417            6 : inline int cred2Ctrl::setFanSpeed()
    1418              : {
    1419            6 :     if( m_fanSpeedNameSet == "auto" )
    1420              :     {
    1421            4 :         if( issueCommand( "set fan mode automatic" ) < 0 )
    1422              :         {
    1423            1 :             return -1;
    1424              :         }
    1425              :     }
    1426              :     else
    1427              :     {
    1428            4 :         int fanPercent = 0;
    1429            4 :         if( cred2FanPresetPercent( fanPercent, m_fanSpeedNameSet ) < 0 )
    1430              :         {
    1431            1 :             return log<software_error, -1>( { __FILE__, __LINE__, "unknown fan speed preset: " + m_fanSpeedNameSet } );
    1432              :         }
    1433              : 
    1434            6 :         if( issueCommand( "set fan mode manual" ) < 0 )
    1435              :         {
    1436            1 :             return -1;
    1437              :         }
    1438              : 
    1439            2 :         if( issueCommand( "set fan speed " + std::to_string( fanPercent ) ) < 0 )
    1440              :         {
    1441            1 :             return -1;
    1442              :         }
    1443              :     }
    1444              : 
    1445            2 :     return getFanSpeed();
    1446              : }
    1447              : 
    1448            3 : inline int cred2Ctrl::setAnalogGain()
    1449              : {
    1450            3 :     std::string commandGain;
    1451              : 
    1452            3 :     if( cred2AnalogGainCommand( commandGain, m_analogGainNameSet ) < 0 )
    1453              :     {
    1454            1 :         return log<software_error, -1>( { __FILE__, __LINE__, "unknown analog gain preset: " + m_analogGainNameSet } );
    1455              :     }
    1456              : 
    1457            2 :     if( issueCommand( "set sensibility " + commandGain ) < 0 )
    1458              :     {
    1459            1 :         return -1;
    1460              :     }
    1461              : 
    1462            1 :     return getAnalogGain();
    1463            3 : }
    1464              : 
    1465            4 : inline int cred2Ctrl::setLED()
    1466              : {
    1467            4 :     if( m_ledStateSet )
    1468              :     {
    1469            4 :         if( issueCommand( "set led on" ) < 0 )
    1470              :         {
    1471            1 :             return -1;
    1472              :         }
    1473              :     }
    1474              :     else
    1475              :     {
    1476            4 :         if( issueCommand( "set led off" ) < 0 )
    1477              :         {
    1478            1 :             return -1;
    1479              :         }
    1480              :     }
    1481              : 
    1482            2 :     return getLEDState();
    1483              : }
    1484              : 
    1485            1 : inline int cred2Ctrl::setExpTime()
    1486              : {
    1487            1 :     return 0;
    1488              : }
    1489              : 
    1490            2 : inline int cred2Ctrl::checkNextROI()
    1491              : {
    1492            8 :     auto roundToStep = []( int value, int step )
    1493            8 :     { return static_cast<int>( std::lround( static_cast<double>( value ) / static_cast<double>( step ) ) ) * step; };
    1494              : 
    1495            2 :     m_nextROI.bin_x = 1;
    1496            2 :     m_nextROI.bin_y = 1;
    1497              : 
    1498            2 :     int width = roundToStep( m_nextROI.w, 32 );
    1499            2 :     width     = std::clamp( width, 32, m_full_w );
    1500              : 
    1501            2 :     int height = roundToStep( m_nextROI.h, 4 );
    1502            2 :     height     = std::clamp( height, 4, m_full_h );
    1503              : 
    1504            2 :     int startColumn = static_cast<int>( std::lround( m_nextROI.x - 0.5f * ( static_cast<float>( width ) - 1.0f ) ) );
    1505            2 :     int startRow    = static_cast<int>( std::lround( m_nextROI.y - 0.5f * ( static_cast<float>( height ) - 1.0f ) ) );
    1506              : 
    1507            2 :     startColumn = roundToStep( startColumn, 32 );
    1508            2 :     startRow    = roundToStep( startRow, 4 );
    1509              : 
    1510            2 :     startColumn = std::clamp( startColumn, 0, m_full_w - width );
    1511            2 :     startRow    = std::clamp( startRow, 0, m_full_h - height );
    1512              : 
    1513            2 :     m_nextROI.w = width;
    1514            2 :     m_nextROI.h = height;
    1515            2 :     m_nextROI.x = startColumn + 0.5f * ( static_cast<float>( width ) - 1.0f );
    1516            2 :     m_nextROI.y = startRow + 0.5f * ( static_cast<float>( height ) - 1.0f );
    1517              : 
    1518            4 :     updateIfChanged( m_indiP_roi_x, "target", m_nextROI.x, INDI_OK );
    1519            4 :     updateIfChanged( m_indiP_roi_y, "target", m_nextROI.y, INDI_OK );
    1520            4 :     updateIfChanged( m_indiP_roi_w, "target", m_nextROI.w, INDI_OK );
    1521            4 :     updateIfChanged( m_indiP_roi_h, "target", m_nextROI.h, INDI_OK );
    1522            4 :     updateIfChanged( m_indiP_roi_bin_x, "target", m_nextROI.bin_x, INDI_OK );
    1523            4 :     updateIfChanged( m_indiP_roi_bin_y, "target", m_nextROI.bin_y, INDI_OK );
    1524              : 
    1525            2 :     return 0;
    1526              : }
    1527              : 
    1528            1 : inline int cred2Ctrl::setNextROI()
    1529              : {
    1530            1 :     if( checkNextROI() < 0 )
    1531              :     {
    1532            0 :         return -1;
    1533              :     }
    1534              : 
    1535            1 :     recordCamera( true );
    1536            1 :     state( stateCodes::CONFIGURING );
    1537              : 
    1538            1 :     m_nextMode = m_modeName.empty() ? m_startupMode : m_modeName;
    1539            1 :     m_reconfig = true;
    1540              : 
    1541            2 :     updateSwitchIfChanged( m_indiP_roi_set, "request", pcf::IndiElement::Off, INDI_IDLE );
    1542            2 :     updateSwitchIfChanged( m_indiP_roi_full, "request", pcf::IndiElement::Off, INDI_IDLE );
    1543            2 :     updateSwitchIfChanged( m_indiP_roi_last, "request", pcf::IndiElement::Off, INDI_IDLE );
    1544            2 :     updateSwitchIfChanged( m_indiP_roi_default, "request", pcf::IndiElement::Off, INDI_IDLE );
    1545              : 
    1546            1 :     return 0;
    1547              : }
    1548              : 
    1549           19 : inline int cred2Ctrl::writeConfig()
    1550              : {
    1551           19 :     std::ofstream fout( m_configFile );
    1552           19 :     if( fout.fail() )
    1553              :     {
    1554            8 :         return log<software_error, -1>( { __FILE__, __LINE__, "error opening C-RED 2 config file for writing" } );
    1555              :     }
    1556              : 
    1557           15 :     const int width  = m_nextROI.w / m_nextROI.bin_x;
    1558           15 :     const int height = m_nextROI.h / m_nextROI.bin_y;
    1559              : 
    1560           15 :     fout << "camera_class:                  \"FirstLightImaging\"\n";
    1561           15 :     fout << "camera_model:                  \"C-RED 2\"\n";
    1562           15 :     fout << "camera_info:                   \"" << width << "x" << height << " (4-tap, freerun)\"\n";
    1563           15 :     fout << "width:                         " << width << "\n";
    1564           15 :     fout << "height:                        " << height << "\n";
    1565           15 :     fout << "depth:                         16\n";
    1566           15 :     fout << "extdepth:                      16\n";
    1567           15 :     fout << "rbtfile:                       aiagcl.bit\n";
    1568           15 :     fout << "CL_DATA_PATH_NORM:             3f       # four tap\n";
    1569           15 :     fout << "CL_CFG_NORM:                   02\n";
    1570           15 :     fout << "CL_CFG2_NORM:                  40\n";
    1571           15 :     fout << "method_framesync:              EMULATE_TIMEOUT\n";
    1572           15 :     fout << "htaps:                         4\n";
    1573           15 :     fout << "serial_baud:                  " << m_serialBaud << "\n";
    1574           15 :     fout << "serial_term:                   <0A>\n";
    1575           15 :     fout << "serial_waitc:                  0D\n";
    1576              : 
    1577           15 :     fout.close();
    1578              : 
    1579           15 :     return 0;
    1580           19 : }
    1581              : 
    1582            5 : inline int cred2Ctrl::configureAcquisition()
    1583              : {
    1584            5 :     std::unique_lock<std::mutex>          lock( m_indiMutex );
    1585            5 :     std::lock_guard<std::recursive_mutex> cameraGuard( m_cameraMutex );
    1586              : 
    1587            5 :     cred2Roi roi;
    1588            5 :     if( cred2RoiFromCenter( roi, m_nextROI.x, m_nextROI.y, m_nextROI.w, m_nextROI.h, m_full_w, m_full_h ) < 0 )
    1589              :     {
    1590            1 :         state( stateCodes::ERROR );
    1591            2 :         return log<software_error, -1>( { __FILE__, __LINE__, "invalid ROI specified for C-RED 2 configure" } );
    1592              :     }
    1593              : 
    1594            4 :     if( roi.fullFrame )
    1595              :     {
    1596            6 :         if( m_cameraCropEnabled && issueCommand( "set cropping off", true ) < 0 )
    1597              :         {
    1598            1 :             state( stateCodes::ERROR );
    1599            1 :             return -1;
    1600              :         }
    1601              : 
    1602            1 :         m_cameraCropEnabled = false;
    1603              :     }
    1604              :     else
    1605              :     {
    1606            4 :         if( issueCommand( "set cropping columns " + cred2ColumnsSpec( roi ), true ) < 0 ||
    1607            7 :             issueCommand( "set cropping rows " + cred2RowsSpec( roi ), true ) < 0 ||
    1608            4 :             issueCommand( "set cropping on", true ) < 0 )
    1609              :         {
    1610            1 :             state( stateCodes::ERROR );
    1611            1 :             return -1;
    1612              :         }
    1613              : 
    1614            1 :         m_cameraCropEnabled = true;
    1615              :     }
    1616              : 
    1617            2 :     m_currentROI = m_nextROI;
    1618              : 
    1619            4 :     updateIfChanged( m_indiP_roi_x, "current", m_currentROI.x, INDI_OK );
    1620            4 :     updateIfChanged( m_indiP_roi_y, "current", m_currentROI.y, INDI_OK );
    1621            4 :     updateIfChanged( m_indiP_roi_w, "current", m_currentROI.w, INDI_OK );
    1622            4 :     updateIfChanged( m_indiP_roi_h, "current", m_currentROI.h, INDI_OK );
    1623            4 :     updateIfChanged( m_indiP_roi_bin_x, "current", m_currentROI.bin_x, INDI_OK );
    1624            4 :     updateIfChanged( m_indiP_roi_bin_y, "current", m_currentROI.bin_y, INDI_OK );
    1625              : 
    1626            2 :     m_nextROI = m_currentROI;
    1627              : 
    1628            4 :     updateIfChanged( m_indiP_roi_x, "target", m_nextROI.x, INDI_OK );
    1629            4 :     updateIfChanged( m_indiP_roi_y, "target", m_nextROI.y, INDI_OK );
    1630            4 :     updateIfChanged( m_indiP_roi_w, "target", m_nextROI.w, INDI_OK );
    1631            4 :     updateIfChanged( m_indiP_roi_h, "target", m_nextROI.h, INDI_OK );
    1632            4 :     updateIfChanged( m_indiP_roi_bin_x, "target", m_nextROI.bin_x, INDI_OK );
    1633            4 :     updateIfChanged( m_indiP_roi_bin_y, "target", m_nextROI.bin_y, INDI_OK );
    1634              : 
    1635            2 :     m_width    = m_currentROI.w;
    1636            2 :     m_height   = m_currentROI.h;
    1637            2 :     m_dataType = _DATATYPE_INT16;
    1638              : 
    1639              :     // Use the current FPS target while the camera settles so the framegrabber
    1640              :     // does not keep reconfiguring latency buffers on a stale pre-ROI value.
    1641            2 :     if( m_fpsSet > 0 )
    1642              :     {
    1643            2 :         m_fps = m_fpsSet;
    1644              :     }
    1645              : 
    1646              :     // Give the camera a few app-logic cycles to settle after crop changes
    1647              :     // before resuming serial status polls such as fps, temperatures, and
    1648              :     // refreshed FPS limits.
    1649            2 :     m_roiSettleCounter = 5;
    1650              : 
    1651            2 :     recordCamera( true );
    1652            2 :     state( stateCodes::READY );
    1653              : 
    1654            2 :     return 0;
    1655            5 : }
    1656              : 
    1657            1 : inline float cred2Ctrl::fps()
    1658              : {
    1659            1 :     return m_fps;
    1660              : }
    1661              : 
    1662            1 : inline int cred2Ctrl::startAcquisition()
    1663              : {
    1664            1 :     state( stateCodes::OPERATING );
    1665            1 :     recordCamera();
    1666            1 :     return edtCamera<cred2Ctrl>::pdvStartAcquisition();
    1667              : }
    1668              : 
    1669            1 : inline int cred2Ctrl::acquireAndCheckValid()
    1670              : {
    1671            1 :     return edtCamera<cred2Ctrl>::pdvAcquire( m_currImageTimestamp );
    1672              : }
    1673              : 
    1674            2 : inline int cred2Ctrl::loadImageIntoStream( void *dest )
    1675              : {
    1676            2 :     if( frameGrabber<cred2Ctrl>::loadImageIntoStreamCopy( dest, m_image_p, m_width, m_height, m_typeSize ) == nullptr )
    1677              :     {
    1678            1 :         return -1;
    1679              :     }
    1680              : 
    1681            1 :     return 0;
    1682              : }
    1683              : 
    1684            3 : inline int cred2Ctrl::reconfig()
    1685              : {
    1686            3 :     recordCamera( true );
    1687            3 :     state( stateCodes::CONFIGURING );
    1688              : 
    1689            3 :     if( writeConfig() < 0 )
    1690              :     {
    1691            1 :         return -1;
    1692              :     }
    1693              : 
    1694            2 :     std::lock_guard<std::recursive_mutex> guard( m_cameraMutex );
    1695            2 :     int                                   rv = edtCamera<cred2Ctrl>::pdvReconfig();
    1696            2 :     if( rv < 0 )
    1697              :     {
    1698            0 :         return rv;
    1699              :     }
    1700              : 
    1701            2 :     if( setSerialBaud() < 0 )
    1702              :     {
    1703            1 :         return -1;
    1704              :     }
    1705              : 
    1706            1 :     state( stateCodes::READY );
    1707            1 :     m_nextMode = m_modeName;
    1708              : 
    1709            1 :     return 0;
    1710            2 : }
    1711              : 
    1712            1 : inline int cred2Ctrl::checkRecordTimes()
    1713              : {
    1714            1 :     return telemeter<cred2Ctrl>::checkRecordTimes( cred2_temps(), telem_stdcam(), telem_fgtimings() );
    1715              : }
    1716              : 
    1717            2 : inline int cred2Ctrl::recordTelem( const cred2_temps * )
    1718              : {
    1719            2 :     return recordTemps( true );
    1720              : }
    1721              : 
    1722            2 : inline int cred2Ctrl::recordTelem( const telem_stdcam * )
    1723              : {
    1724            2 :     return recordCamera( true );
    1725              : }
    1726              : 
    1727            2 : inline int cred2Ctrl::recordTelem( const telem_fgtimings * )
    1728              : {
    1729            2 :     return recordFGTimings( true );
    1730              : }
    1731              : 
    1732           18 : inline int cred2Ctrl::recordTemps( bool force )
    1733              : {
    1734              :     static cred2Temps lastTemps;
    1735              : 
    1736           18 :     if( !( lastTemps == m_temps ) || force )
    1737              :     {
    1738            9 :         telem<cred2_temps>( { m_temps.motherboard,
    1739            9 :                               m_temps.frontend,
    1740            9 :                               m_temps.powerboard,
    1741            9 :                               m_temps.snake,
    1742            9 :                               m_temps.setpoint,
    1743            9 :                               m_temps.peltier,
    1744            9 :                               m_temps.heatsink } );
    1745            9 :         lastTemps = m_temps;
    1746              :     }
    1747              : 
    1748           18 :     return 0;
    1749              : }
    1750              : 
    1751              : } // namespace app
    1752              : } // namespace MagAOX
    1753              : 
    1754              : #endif // cred2Ctrl_hpp
        

Generated by: LCOV version 2.0-1