LCOV - code coverage report
Current view: top level - libMagAOX/app/dev - stdCamera.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 31.1 % 874 272
Test Date: 2026-04-15 19:34:29 Functions: 22.0 % 50 11

            Line data    Source code
       1              : /** \file stdCamera.hpp
       2              :  * \brief Standard camera interface
       3              :  *
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  * \ingroup app_files
       7              :  */
       8              : 
       9              : #ifndef stdCamera_hpp
      10              : #define stdCamera_hpp
      11              : 
      12              : #include <string>
      13              : #include <type_traits>
      14              : #include <unordered_map>
      15              : #include <vector>
      16              : 
      17              : #include <mx/app/application.hpp>
      18              : 
      19              : #include "../MagAOXApp.hpp"
      20              : 
      21              : namespace MagAOX
      22              : {
      23              : namespace app
      24              : {
      25              : namespace dev
      26              : {
      27              : 
      28              : #define CAMCTRL_E_NOCONFIGS ( -10 )
      29              : 
      30              : /// A camera configuration
      31              : /** a.k.a. a mode
      32              :  */
      33              : struct cameraConfig
      34              : {
      35              :     std::string m_configFile;    ///< The file to use for this mode, e.g. an EDT configuration file.
      36              :     std::string m_serialCommand; ///< The command to send to the camera to place it in this mode.
      37              :     unsigned    m_centerX{ 0 };
      38              :     unsigned    m_centerY{ 0 };
      39              :     unsigned    m_sizeX{ 0 };
      40              :     unsigned    m_sizeY{ 0 };
      41              :     unsigned    m_binningX{ 0 };
      42              :     unsigned    m_binningY{ 0 };
      43              : 
      44              :     unsigned m_digitalBinX{ 0 };
      45              :     unsigned m_digitalBinY{ 0 };
      46              : 
      47              :     float m_maxFPS{ 0 };
      48              : };
      49              : 
      50              : typedef std::unordered_map<std::string, cameraConfig> cameraConfigMap;
      51              : 
      52              : /// Load the camera configurations contained in the app configuration into a map
      53              : int loadCameraConfig( cameraConfigMap &ccmap, ///< [out] the map in which to place the configurations found in config
      54              :                       mx::app::appConfigurator &config ///< [in] the application configuration structure
      55              : );
      56              : 
      57              : /// Detect whether a derived camera exposes stdCamera fan control support.
      58              : template <class derivedT, class = void>
      59              : struct stdCameraHasFan : std::false_type
      60              : {
      61              : };
      62              : 
      63              : /// Specialization for cameras that define `c_stdCamera_fan`.
      64              : template <class derivedT>
      65              : struct stdCameraHasFan<derivedT, std::void_t<decltype( derivedT::c_stdCamera_fan )>>
      66              :     : std::bool_constant<derivedT::c_stdCamera_fan>
      67              : {
      68              : };
      69              : 
      70              : /// Detect whether a derived camera exposes legacy stdCamera fan-speed control support.
      71              : template <class derivedT, class = void>
      72              : struct stdCameraHasLegacyFanSpeed : std::false_type
      73              : {
      74              : };
      75              : 
      76              : /// Specialization for cameras that define `c_stdCamera_fanSpeed`.
      77              : template <class derivedT>
      78              : struct stdCameraHasLegacyFanSpeed<derivedT, std::void_t<decltype( derivedT::c_stdCamera_fanSpeed )>>
      79              :     : std::bool_constant<derivedT::c_stdCamera_fanSpeed>
      80              : {
      81              : };
      82              : 
      83              : /// Detect whether a derived camera exposes stdCamera LED control support.
      84              : template <class derivedT, class = void>
      85              : struct stdCameraHasLED : std::false_type
      86              : {
      87              : };
      88              : 
      89              : /// Specialization for cameras that define `c_stdCamera_led`.
      90              : template <class derivedT>
      91              : struct stdCameraHasLED<derivedT, std::void_t<decltype( derivedT::c_stdCamera_led )>>
      92              :     : std::bool_constant<derivedT::c_stdCamera_led>
      93              : {
      94              : };
      95              : 
      96              : /// Detect whether a derived camera exposes stdCamera analog-gain control support.
      97              : template <class derivedT, class = void>
      98              : struct stdCameraHasAnalogGain : std::false_type
      99              : {
     100              : };
     101              : 
     102              : /// Specialization for cameras that define `c_stdCamera_analogGain`.
     103              : template <class derivedT>
     104              : struct stdCameraHasAnalogGain<derivedT, std::void_t<decltype( derivedT::c_stdCamera_analogGain )>>
     105              :     : std::bool_constant<derivedT::c_stdCamera_analogGain>
     106              : {
     107              : };
     108              : 
     109              : /// MagAO-X standard camera interface
     110              : /** Implements the standard interface to a MagAO-X camera.  The derived class `derivedT` must
     111              :  * meet the following requirements:
     112              :  *
     113              :  * - The derived class `derivedT` must be a `MagAOXApp\<true\>`
     114              :  *
     115              :  * - Must declare this class a friend like so:
     116              :  *   \code
     117              :  *       friend class dev::stdCamera<DERIVEDNAME>;  //replace DERIVEDNAME with derivedT class name
     118              :  *   \endcode
     119              :  *
     120              :  * - Must declare the following typedef:
     121              :  *   \code
     122              :  *       typedef dev::stdCamera<DERIVEDNAME> stdCameraT; //replace DERIVEDNAME with derivedT class name
     123              :  *   \endcode
     124              :  *
     125              :  * - Must declare a series of `static constexpr` flags to manage static compile-time configuration.  Each of these
     126              :  *   flags must be defined in derivedT to be either true or false.
     127              :  *
     128              :  *     - Temperature Control and Status:
     129              :  *
     130              :  *         - A static configuration variable must be defined in derivedT as
     131              :  *           \code
     132              :  *               static constexpr bool c_stdCamera_tempControl = true; //or: false
     133              :  *           \endcode
     134              :  *           which determines whether or not temperature controls are exposed.
     135              :  *
     136              :  *         - If `(c_stdCamera_tempControl == true)` then the derived class must implement the following interfaces
     137              :  *           \code
     138              :  *               int setTempControl(); // set temp control status according to m_tempControlStatusSet
     139              :  *               int setTempSetPt(); // set the temperature set point accordin to m_ccdTempSetpt
     140              :  *           \endcode
     141              :  *
     142              :  *         - A static configuration variable must be defined in derivedT as
     143              :  *           \code
     144              :  *               static constexpr bool c_stdCamera_temp = true; //or: false
     145              :  *           \endcode
     146              :  *           which determines whether or not temperature reporting is exposed.  Note that if
     147              :  *           `(c_stdCamera_tempControl == true)`, then the behavior is as if `c_stdCamera_temp == true`,
     148              :  *           but thus constexpr must still be defined.
     149              :  *
     150              :  *         - If either `(c_stdCamera_tempControl == true)` or `c_stdCamera_temp == true` then the INDI property
     151              :  * "temp_ccd" will be updated from the value of \ref m_ccdTemp.
     152              :  *
     153              :  *     - Readout Speed:
     154              :  *
     155              :  *       - A static configuration variable must be defined in derivedT as
     156              :  *         \code
     157              :  *             static constexpr bool c_stdCamera_readoutSpeed = true; //or: false
     158              :  *         \endcode
     159              :  *         which determines whether or not readout speed controls are exposed.  If true, then the implementation should
     160              :  * populate
     161              :  *         \ref m_readoutSpeedNames and \ref m_readoutSpeedNameLabels (vectors of strings) on construction to the
     162              :  * allowed values.  This facility is normally used to control both amplifier and readout/adc speed with names like
     163              :  * "ccd_1MHz" and "emccd_17MHz".
     164              :  *
     165              :  *       - If used (and true) then the following interface must be implemented:
     166              :  *         \code
     167              :  *             int setReadoutSpeed(); // configures camera using m_readoutSpeedNameSet
     168              :  *         \endcode
     169              :  *         This configures the camera according to \ref m_readoutSpeedNameSet.
     170              :  *         The implementation must also manage \ref m_readoutSpeedName, keeping it up to date with the current setting.
     171              :  *
     172              :  *       - If true, the configuration setting "camera.defaultReadoutSpeed"
     173              :  *         is also exposed, and \ref m_defaultReadoutSpeed will be set according to it.  The implementation can
     174              :  *         set a sensible default on construction.
     175              :  *
     176              :  *     - Vertical Shift Speed:
     177              :  *
     178              :  *        - A static configuration variable must be defined in derivedT as
     179              :  *          \code
     180              :  *              static constexpr bool c_stdCamera_vShiftSpeed = true; //or: false
     181              :  *          \endcode
     182              :  *          which determines whether or not vertical shift speed controls are exposed.
     183              :  *
     184              :  *        - If true, then the implementation should populate \ref m_vShiftSpeedNames and \ref m_vShiftSpeedLabels
     185              :  *          (vectors of strings) on construction to the allowed values.  This
     186              :  *          facility is normally used with names like "0_3us" and "1_3us".
     187              :  *
     188              :  *        - If true then the following interface must be defined:
     189              :  *          \code
     190              :  *              int setVShiftSpeed(); // configures camera according to m_vShiftSpeedNameSet
     191              :  *          \endcode
     192              :  *          function must be defined which sets the camera according to \ref m_vShiftSpeedNameSet.
     193              :  *          The implementation must also manage m_vShiftSpeedName, keeping it up to date.
     194              :  *
     195              :  *        - The configuration setting "camera.defaultVShiftSpeed"
     196              :  *          is also exposed, and \ref m_defaultVShiftSpeed will be set accordingly.  derivedT can set a sensible default
     197              :  *          on constuction.
     198              :  *
     199              :  *     - Fan Speed:
     200              :  *
     201              :  *        - A static configuration variable must be defined in derivedT as
     202              :  *          \code
     203              :  *              static constexpr bool c_stdCamera_fanSpeed = true; //or: false
     204              :  *          \endcode
     205              :  *          which determines whether or not fan-speed controls are supported by the app.
     206              :  *
     207              :  *        - If true, then the implementation should populate \ref m_fanSpeedNames and
     208              :  *          \ref m_fanSpeedNameLabels (vectors of strings) on construction to the allowed values.
     209              :  *
     210              :  *        - If true then the following interface must be defined:
     211              :  *          \code
     212              :  *              int setFanSpeed(); // configures camera according to m_fanSpeedNameSet
     213              :  *          \endcode
     214              :  *          function must be defined which sets the camera according to \ref m_fanSpeedNameSet.
     215              :  *          The implementation must also manage \ref m_fanSpeedName, keeping it up to date.
     216              :  *
     217              :  *        - The configuration settings "camera.fanSpeedControl" and "camera.defaultFanSpeed"
     218              :  *          are also exposed. The former determines whether the INDI control is published, and the latter
     219              :  *          sets the default fan speed applied after power-on. The value of \ref m_defaultFanSpeed must
     220              :  *          be one of `high`, `medium`, `low`, or `off`.
     221              :  *
     222              :  *     - Exposure Time:
     223              :  *        - A static configuration variable must be defined in derivedT as
     224              :  *          \code
     225              :  *              static constexpr bool c_stdCamera_exptimeCtrl = true; //or: false
     226              :  *          \endcode
     227              :  *        - If true, the following interface must be implemented:
     228              :  *          \code
     229              :  *              int setExpTime(); // set camera exposure time according to m_expTimeSet.
     230              :  *          \endcode
     231              :  *          to configure the camera according to \ref m_expTimeSet.  derivedT must also keep \ref m_expTime up to date.
     232              :  *
     233              :  *     - Frames per Second (FPS) Control and Status:
     234              :  *         - A static configuration variable must be defined in derivedT as
     235              :  *           \code
     236              :  *               static constexpr bool c_stdCamera_fpsCtrl = true; //or: false
     237              :  *           \endcode
     238              :  *
     239              :  *         - If that is set to true the derivedT must implement
     240              :  *           \code
     241              :  *               int setFPS(); // set camera FS according to m_fps
     242              :  *           \endcode
     243              :  *           to configure the camera according to \ref m_fpsSet.
     244              :  *
     245              :  *         - A static configuration variable must be defined in derivedT as
     246              :  *           \code
     247              :  *              static constexpr bool c_stdCamera_fps = true; //or: false
     248              :  *           \endcode
     249              :  *           Note that the value of c_stdCamera_fps does not matter if c_stdCamera_fpsCtrl == true.
     250              :  *
     251              :  *         - If either `c_stdCamera_fpsCtrl == true` or `c_stdCamera_fps == true` then derivedT must also
     252              :  *           keep \ref m_fps up to date.
     253              :  *
     254              :  *     - Fan Control:
     255              :  *
     256              :  *       - A static configuration variable may be defined in derivedT as
     257              :  *         \code
     258              :  *             static constexpr bool c_stdCamera_fan = true; //or: false
     259              :  *         \endcode
     260              :  *         which determines whether or not discrete fan controls are exposed. If omitted, fan controls default to off.
     261              :  *
     262              :  *       - If that is set to true the derivedT must implement
     263              :  *         \code
     264              :  *             int setFanSpeed(); // configure the camera based on m_fanSpeedNameSet
     265              :  *         \endcode
     266              :  *         and should populate \ref m_fanSpeedNames (and optionally \ref m_fanSpeedNameLabels) before
     267              :  *         stdCamera::appStartup().
     268              :  *
     269              :  *     - Analog Gain:
     270              :  *
     271              :  *       - A static configuration variable may be defined in derivedT as
     272              :  *         \code
     273              :  *             static constexpr bool c_stdCamera_analogGain = true; //or: false
     274              :  *         \endcode
     275              :  *         which determines whether or not discrete analog-gain controls are exposed. If omitted, analog-gain
     276              :  *         controls default to off.
     277              :  *
     278              :  *       - If that is set to true the derivedT must implement
     279              :  *         \code
     280              :  *             int setAnalogGain(); // configure the camera based on m_analogGainNameSet
     281              :  *         \endcode
     282              :  *         and should populate \ref m_analogGainNames (and optionally \ref m_analogGainNameLabels) before
     283              :  *         stdCamera::appStartup().
     284              :  *
     285              :  *     - LED Control:
     286              :  *
     287              :  *       - A static configuration variable may be defined in derivedT as
     288              :  *         \code
     289              :  *             static constexpr bool c_stdCamera_led = true; //or: false
     290              :  *         \endcode
     291              :  *         which determines whether or not status LED controls are exposed. If omitted, LED controls default to off.
     292              :  *
     293              :  *       - If that is set to true the derivedT must implement
     294              :  *         \code
     295              :  *             int setLED(); // configure the camera according to m_ledStateSet
     296              :  *         \endcode
     297              :  *         and should keep \ref m_ledState up to date.
     298              :  *
     299              :  *     - Synchro Control:
     300              :  *
     301              :  *       - A static configuration variable must be defined in derivedT as
     302              :  *         \code
     303              :  *             static constexpr bool c_stdCamera_synchro = true; //or: false
     304              :  *         \endcode
     305              :  *       - If that is set to true the derivedT must implement
     306              :  *         \code
     307              :  *             int setSynchro(); // configure the camera based m_synchroSet.
     308              :  *         \endcode
     309              :  *         to configure the camera based on \ref m_synchroSet.  The implementation should also keep
     310              :  *         \ref m_synchro up to date.
     311              :  *
     312              :  *     - EM Gain:
     313              :  *        - A static configuration variable must be defined in derivedT as
     314              :  *          \code
     315              :  *              static constexpr bool c_stdCamera_emGain = true; //or: false
     316              :  *          \endcode
     317              :  *          which determines whether or not EM gain controls are exposed.
     318              :  *
     319              :  *        - If the camera uses EM Gain, then a function
     320              :  *          \code
     321              :  *              int setEMGain(); // set EM gain based on m_emGainSet.
     322              :  *          \endcode
     323              :  *          must be defined which sets the camera EM Gain to \ref m_emGainSet.
     324              :  *
     325              :  *        - If true the implementation must keep \ref m_emGain up to date.
     326              :  *
     327              :  *        - If true the value of \ref m_maxEMGain should be set by the implementation and managed
     328              :  *          as needed. Additionally the configuration setting "camera.maxEMGain" is exposed.
     329              :  *
     330              :  *     - Camera Modes:
     331              :  *
     332              :  *       - A static configuration variable must be defined in derivedT as
     333              :  *         \code
     334              :  *             static constexpr bool c_stdCamera_usesModes= true; //or: false
     335              :  *         \endcode
     336              :  *
     337              :  *       - If true, then modes are read from the configuration file.  See \ref loadCameraConfig()
     338              :  *
     339              :  *       - If true, then the configuration setting "camera.startupMode" is exposed, which sets the mode at startup by
     340              :  * its name.
     341              :  *
     342              :  *     - Regions of Interest
     343              :  *
     344              :  *       - A static configuration variable must be defined in derivedT as
     345              :  *         \code
     346              :  *             static constexpr bool c_stdCamera_usesROI = true; //or: false
     347              :  *         \endcode
     348              :  *
     349              :  *       - The default values of m_full_x/y/w/h must be set before calling stdCamera::appStartup(). These
     350              :  *         are configured by stdCamera::loadConfig(), but only if set in the config file.
     351              :  *
     352              :  *       - The derived class must implement:
     353              :  *         \code
     354              :  *             int checkNextROI(); // verifies m_nextROI values and modifies to closest valid values if needed
     355              :  *             int setNextROI(); // sets the ROI to the new target values.
     356              :  *         \endcode
     357              :  *
     358              :  *     - Crop Mode ROIs:
     359              :  *
     360              :  *        - A static configuration variable must be defined in derivedT as
     361              :  *          \code
     362              :  *              static constexpr bool c_stdCamera_cropMode = true; //or: false
     363              :  *          \endcode
     364              :  *
     365              :  *        - If true the derived class must implement
     366              :  *          \code
     367              :  *              int setCropMode(); // set crop mode according to m_cropModeSet
     368              :  *          \endcode
     369              :  *          which changes the crop mode according to \ref m_cropModeSet.
     370              :  *
     371              :  *        - `derivedT` must also maintain the value of \ref m_cropMode.
     372              :  *
     373              :  *     - Shutters:
     374              :  *
     375              :  *       - A static configuration variable must be defined in derivedT as
     376              :  *         \code
     377              :  *             static constexpr bool c_stdCamera_hasShutter = true; //or: false
     378              :  *         \endcode
     379              :  *
     380              :  *       - If true the following interface must be implemented:
     381              :  *         \code
     382              :  *             int setShutter(int); // shut the shutter if 0, open the shutter otherwise.
     383              :  *         \endcode
     384              :  *         which shuts the shutter if the argument is 0, opens it otherwise.
     385              :  *
     386              :  *     - State:
     387              :  *
     388              :  *       - A static configuration variable must be defined in derivedT as
     389              :  *         \code
     390              :  *             static constexpr bool c_stdCamera_usesStateString = true; //or: false
     391              :  *         \endcode
     392              :  *         which determines whether the class provides a state string for dark management.
     393              :  *
     394              :  *       - If true, the following functions must be defined in derivedT:
     395              :  *         \code
     396              :  *             std::string stateString(); //String capturing the current state.  Must not include "__T".
     397              :  *             bool stateStringValid(); //Whether or not the current state string is valid, i.e. not changing.
     398              :  *         \endcode
     399              :  *
     400              :  *
     401              :  * - The derived class must implement:
     402              :  *   \code
     403              :  *   int powerOnDefaults(); // called on power-on after powerOnWaitElapsed has occurred.
     404              :  *   \endcode
     405              :  *
     406              :  * - Calls to this class's setupConfig(), loadConfig(), appStartup(), appLogic(), appShutdown()
     407              :  *   onPowerOff(), and whilePowerOff(),  must be placed in the derived class's functions of the same name.
     408              :  *
     409              :  * \ingroup appdev
     410              :  */
     411              : template <class derivedT>
     412              : class stdCamera
     413              : {
     414              :   protected:
     415              :     static constexpr bool c_hasFan =
     416              :         stdCameraHasFan<derivedT>::value ||
     417              :         stdCameraHasLegacyFanSpeed<derivedT>::value; ///< True when the derived camera exposes fan control.
     418              :     static constexpr bool c_hasLegacyFanSpeed =
     419              :         stdCameraHasLegacyFanSpeed<derivedT>::value; ///< True when the derived camera uses the legacy fan config path.
     420              :     static constexpr bool c_hasLED =
     421              :         stdCameraHasLED<derivedT>::value; ///< True when the derived camera exposes LED control.
     422              :     static constexpr bool c_hasAnalogGain =
     423              :         stdCameraHasAnalogGain<derivedT>::value; ///< True when the derived camera exposes analog-gain control.
     424              : 
     425              :     /** \name Configurable Parameters
     426              :      * @{
     427              :      */
     428              : 
     429              :     cameraConfigMap m_cameraModes; ///< Map holding the possible camera mode configurations
     430              : 
     431              :     std::string m_startupMode; ///< The camera mode to load during first init after a power-on.
     432              : 
     433              :     float m_startupTemp{ -999 }; ///< The temperature to set after a power-on.  Set to <= -999 to not use [default].
     434              : 
     435              :     std::string m_defaultReadoutSpeed;             ///< The default readout speed of the camera.
     436              :     std::string m_defaultVShiftSpeed;              ///< The default readout speed of the camera.
     437              :     bool        m_fanSpeedControlEnabled{ false }; ///< Whether or not fan-speed control is published through INDI.
     438              :     std::string m_defaultFanSpeed;                 ///< The default fan speed to apply after power on.
     439              : 
     440              :     ///@}
     441              : 
     442              :     /** \name Temperature Control Interface
     443              :      * @{
     444              :      */
     445              : 
     446              :     float m_minTemp{ -60 };
     447              :     float m_maxTemp{ 30 };
     448              :     float m_stepTemp{ 0 };
     449              : 
     450              :     float m_ccdTemp{ -999 }; ///< The current temperature, in C
     451              : 
     452              :     float m_ccdTempSetpt{ -999 }; ///< The desired temperature, in C
     453              : 
     454              :     bool m_tempControlStatus{ false };    ///< Whether or not temperature control is active
     455              :     bool m_tempControlStatusSet{ false }; ///< Desired state of temperature control
     456              : 
     457              :     bool m_tempControlOnTarget{ false }; ///< Whether or not the temperature control system is on its target temperature
     458              : 
     459              :     std::string m_tempControlStatusStr; ///< Camera specific description of temperature control status.
     460              : 
     461              :     pcf::IndiProperty m_indiP_temp;
     462              :     pcf::IndiProperty m_indiP_tempcont;
     463              :     pcf::IndiProperty m_indiP_tempstat;
     464              : 
     465              :     ///@}
     466              : 
     467              :     /** \name Readout Control
     468              :      * @{
     469              :      */
     470              : 
     471              :     std::vector<std::string> m_readoutSpeedNames;
     472              :     std::vector<std::string> m_readoutSpeedNameLabels;
     473              : 
     474              :     std::string m_readoutSpeedName;    ///< The current readout speed name
     475              :     std::string m_readoutSpeedNameSet; ///< The user requested readout speed name, to be set by derived()
     476              : 
     477              :     std::vector<std::string> m_vShiftSpeedNames;
     478              :     std::vector<std::string> m_vShiftSpeedNameLabels;
     479              : 
     480              :     std::string m_vShiftSpeedName;    ///< The current vshift speed name
     481              :     std::string m_vShiftSpeedNameSet; ///< The user requested vshift speed name, to be set by derived()
     482              : 
     483              :     float m_adcSpeed{ 0 };
     484              :     float m_vshiftSpeed{ 0 };
     485              : 
     486              :     float m_emGain{ 1 };    ///< The camera's current EM gain (if available).
     487              :     float m_emGainSet{ 1 }; ///< The camera's EM gain, as set by the user.
     488              :     float m_maxEMGain{ 1 }; ///< The configurable maximum EM gain.  To be enforced in derivedT.
     489              : 
     490              :     pcf::IndiProperty m_indiP_readoutSpeed;
     491              :     pcf::IndiProperty m_indiP_vShiftSpeed;
     492              : 
     493              :     pcf::IndiProperty m_indiP_emGain;
     494              : 
     495              :     ///@}
     496              : 
     497              :     /** \name Exposure Control
     498              :      * @{
     499              :      */
     500              :     float m_minExpTime{ 0 };                                 ///< The minimum exposure time, used for INDI attributes
     501              :     float m_maxExpTime{ std::numeric_limits<float>::max() }; ///< The maximum exposure time, used for INDI attributes
     502              :     float m_stepExpTime{ 0 }; ///< The maximum exposure time stepsize, used for INDI attributes
     503              : 
     504              :     float m_expTime{ 0 };    ///< The current exposure time, in seconds.
     505              :     float m_expTimeSet{ 0 }; ///< The exposure time, in seconds, as set by user.
     506              : 
     507              :     float m_minFPS{ 0 };                                 ///< The minimum FPS, used for INDI attributes
     508              :     float m_maxFPS{ std::numeric_limits<float>::max() }; ///< The maximum FPS, used for INDI attributes
     509              :     float m_stepFPS{ 0 };                                ///< The FPS step size, used for INDI attributes
     510              : 
     511              :     float m_fps{ 0 };    ///< The current FPS.
     512              :     float m_fpsSet{ 0 }; ///< The commanded fps, as set by user.
     513              : 
     514              :     pcf::IndiProperty m_indiP_exptime;
     515              : 
     516              :     pcf::IndiProperty m_indiP_fps;
     517              : 
     518              :     ///@}
     519              : 
     520              :     /** \name Fan Control
     521              :      * @{
     522              :      */
     523              :     std::vector<std::string> m_fanSpeedNames;         ///< Valid fan-control option names for the INDI selection switch.
     524              :     std::vector<std::string> m_fanSpeedNameLabels;    ///< Optional GUI labels for the fan-control options.
     525              :     std::string              m_fanSpeedName{ "" };    ///< Current fan-control option name.
     526              :     std::string              m_fanSpeedNameSet{ "" }; ///< Requested fan-control option name.
     527              :     bool                     m_fanSpeedValid{ false }; ///< True once the current fan-control state is known.
     528              : 
     529              :     pcf::IndiProperty m_indiP_fanSpeed; ///< Property used to select the fan-control mode.
     530              : 
     531              :     ///@}
     532              : 
     533              :     /** \name Analog Gain
     534              :      * @{
     535              :      */
     536              :     std::vector<std::string> m_analogGainNames;      ///< Valid analog-gain option names for the INDI selection switch.
     537              :     std::vector<std::string> m_analogGainNameLabels; ///< Optional GUI labels for the analog-gain options.
     538              :     std::string              m_analogGainName{ "" }; ///< Current analog-gain option name.
     539              :     std::string              m_analogGainNameSet{ "" };  ///< Requested analog-gain option name.
     540              :     bool                     m_analogGainValid{ false }; ///< True once the current analog-gain state is known.
     541              : 
     542              :     pcf::IndiProperty m_indiP_analogGain; ///< Property used to select the analog-gain mode.
     543              : 
     544              :     ///@}
     545              : 
     546              :     /** \name LED Control
     547              :      * @{
     548              :      */
     549              :     bool m_ledState{ false };      ///< Current status LED state.
     550              :     bool m_ledStateSet{ false };   ///< Requested status LED state.
     551              :     bool m_ledStateValid{ false }; ///< True once the current LED state is known.
     552              : 
     553              :     pcf::IndiProperty m_indiP_led; ///< Property used to control the status LED.
     554              : 
     555              :     ///@}
     556              : 
     557              :     /** \name External Synchronization
     558              :      * @{
     559              :      */
     560              :     bool m_synchroSet{ false }; ///< Target status of m_synchro
     561              : 
     562              :     bool m_synchro{ false }; ///< Status of synchronization, true is on, false is off.
     563              : 
     564              :     pcf::IndiProperty m_indiP_synchro;
     565              : 
     566              :     ///@}
     567              : 
     568              :     /** \name Modes
     569              :      *
     570              :      * @{
     571              :      */
     572              :     std::string m_modeName; ///< The current mode name
     573              : 
     574              :     std::string m_nextMode; ///< The mode to be set by the next reconfiguration
     575              : 
     576              :     pcf::IndiProperty m_indiP_mode; ///< Property used to report the current mode
     577              : 
     578              :     pcf::IndiProperty
     579              :         m_indiP_reconfig; ///< Request switch which forces the framegrabber to go through the reconfigure process.
     580              : 
     581              :     ///@}
     582              : 
     583              :     /** \name ROIs
     584              :      * ROI controls are exposed if derivedT::c_stdCamera_usesROI==true
     585              :      * @{
     586              :      */
     587              :     struct roi
     588              :     {
     589              :         float x{ 0 };
     590              :         float y{ 0 };
     591              :         int   w{ 0 };
     592              :         int   h{ 0 };
     593              :         int   bin_x{ 0 };
     594              :         int   bin_y{ 0 };
     595              :     };
     596              : 
     597              :     roi m_currentROI;
     598              :     roi m_nextROI;
     599              :     roi m_lastROI;
     600              : 
     601              :     float m_minROIx{ 0 };
     602              :     float m_maxROIx{ 1023 };
     603              :     float m_stepROIx{ 0 };
     604              : 
     605              :     float m_minROIy{ 0 };
     606              :     float m_maxROIy{ 1023 };
     607              :     float m_stepROIy{ 0 };
     608              : 
     609              :     int m_minROIWidth{ 1 };
     610              :     int m_maxROIWidth{ 1024 };
     611              :     int m_stepROIWidth{ 1 };
     612              : 
     613              :     int m_minROIHeight{ 1 };
     614              :     int m_maxROIHeight{ 1024 };
     615              :     int m_stepROIHeight{ 1 };
     616              : 
     617              :     int m_minROIBinning_x{ 1 };
     618              :     int m_maxROIBinning_x{ 4 };
     619              :     int m_stepROIBinning_x{ 1 };
     620              : 
     621              :     int m_minROIBinning_y{ 1 };
     622              :     int m_maxROIBinning_y{ 4 };
     623              :     int m_stepROIBinning_y{ 1 };
     624              : 
     625              :     float m_default_x{ 0 };     ///< Power-on ROI center x coordinate.
     626              :     float m_default_y{ 0 };     ///< Power-on ROI center y coordinate.
     627              :     int   m_default_w{ 0 };     ///< Power-on ROI width.
     628              :     int   m_default_h{ 0 };     ///< Power-on ROI height.
     629              :     int   m_default_bin_x{ 1 }; ///< Power-on ROI x binning.
     630              :     int   m_default_bin_y{ 1 }; ///< Power-on ROI y binning.
     631              : 
     632              :     float m_full_x{ 0 };     ///< The full ROI center x coordinate.
     633              :     float m_full_y{ 0 };     ///< The full ROI center y coordinate.
     634              :     int   m_full_w{ 0 };     ///< The full ROI width.
     635              :     int   m_full_h{ 0 };     ///< The full ROI height.
     636              :     int   m_full_bin_x{ 1 }; ///< The x-binning in the full ROI.
     637              :     int   m_full_bin_y{ 1 }; ///< The y-binning in the full ROI.
     638              : 
     639              :     float m_full_currbin_x{ 0 }; ///< The current-binning full ROI center x coordinate.
     640              :     float m_full_currbin_y{ 0 }; ///< The current-binning full ROI center y coordinate.
     641              :     int   m_full_currbin_w{ 0 }; ///< The current-binning full ROI width.
     642              :     int   m_full_currbin_h{ 0 }; ///< The current-binning full ROI height.
     643              : 
     644              :     pcf::IndiProperty m_indiP_roi_x;     ///< Property used to set the ROI x center coordinate
     645              :     pcf::IndiProperty m_indiP_roi_y;     ///< Property used to set the ROI x center coordinate
     646              :     pcf::IndiProperty m_indiP_roi_w;     ///< Property used to set the ROI width
     647              :     pcf::IndiProperty m_indiP_roi_h;     ///< Property used to set the ROI height
     648              :     pcf::IndiProperty m_indiP_roi_bin_x; ///< Property used to set the ROI x binning
     649              :     pcf::IndiProperty m_indiP_roi_bin_y; ///< Property used to set the ROI y binning
     650              : 
     651              :     pcf::IndiProperty m_indiP_fullROI; ///< Property used to preset the full ROI dimensions.
     652              : 
     653              :     pcf::IndiProperty m_indiP_roi_check; ///< Property used to trigger checking the target ROI
     654              : 
     655              :     pcf::IndiProperty m_indiP_roi_set; ///< Property used to trigger setting the ROI
     656              : 
     657              :     pcf::IndiProperty m_indiP_roi_full;     ///< Property used to trigger setting the full ROI.
     658              :     pcf::IndiProperty m_indiP_roi_fullbin;  ///< Property used to trigger setting the full in current binning ROI.
     659              :     pcf::IndiProperty m_indiP_roi_loadlast; ///< Property used to trigger loading the last ROI as the target.
     660              :     pcf::IndiProperty m_indiP_roi_last;     ///< Property used to trigger setting the last ROI.
     661              :     pcf::IndiProperty m_indiP_roi_default;  ///< Property used to trigger setting the default and startup ROI.
     662              : 
     663              :     ///@}
     664              : 
     665              :     /** \name Crop Mode
     666              :      * Crop mode controls are exposed if derivedT::c_stdCamera_cropMode==true
     667              :      * @{
     668              :      */
     669              :     bool m_cropMode{ false };    ///< Status of crop mode ROIs, if enabled for this camera.
     670              :     bool m_cropModeSet{ false }; ///< Desired status of crop mode ROIs, if enabled for this camera.
     671              : 
     672              :     pcf::IndiProperty m_indiP_cropMode; ///< Property used to toggle crop mode on and off.
     673              :     ///@}
     674              : 
     675              :     /** \name Shutter Control
     676              :      * Shutter controls are exposed if derivedT::c_stdCamera_hasShutter == true.
     677              :      * @{
     678              :      */
     679              :     std::string m_shutterStatus{ "UNKNOWN" };
     680              :     int         m_shutterState{ -1 }; /// State of the shutter.  0 = shut, 1 = open, -1 = unknown.
     681              : 
     682              :     pcf::IndiProperty m_indiP_shutterStatus; ///< Property to report shutter status
     683              :     pcf::IndiProperty m_indiP_shutter;       ///< Property used to control the shutter, a switch.
     684              : 
     685              :     ///@}
     686              : 
     687              :     /** \name State String
     688              :      * The State string is exposed if derivedT::c_stdCamera_usesStateString is true.
     689              :      * @{
     690              :      */
     691              :     pcf::IndiProperty m_indiP_stateString;
     692              :     ///@}
     693              : 
     694              :   public:
     695              :     /// Destructor.
     696              :     ~stdCamera() noexcept;
     697              : 
     698              :     /// Setup the configuration system
     699              :     /**
     700              :       * This should be called in `derivedT::setupConfig` as
     701              :       * \code
     702              :         stdCamera<derivedT>::setupConfig(config);
     703              :         \endcode
     704              :       * with appropriate error checking.
     705              :       */
     706              :     int setupConfig( mx::app::appConfigurator &config /**< [out] the derived classes configurator*/ );
     707              : 
     708              :     /// load the configuration system results
     709              :     /**
     710              :       * This should be called in `derivedT::loadConfig` as
     711              :       * \code
     712              :         stdCamera<derivedT>::loadConfig(config);
     713              :         \endcode
     714              :       * with appropriate error checking.
     715              :       */
     716              :     int loadConfig( mx::app::appConfigurator &config /**< [in] the derived classes configurator*/ );
     717              : 
     718              :   protected:
     719              :     // workers to create indi variables if needed
     720              :     int createReadoutSpeed( const mx::meta::trueFalseT<true> &t );
     721              : 
     722              :     int createReadoutSpeed( const mx::meta::trueFalseT<false> &f );
     723              : 
     724              :     int createVShiftSpeed( const mx::meta::trueFalseT<true> &t );
     725              : 
     726              :     int createVShiftSpeed( const mx::meta::trueFalseT<false> &f );
     727              : 
     728              :     int createFanSpeed( const mx::meta::trueFalseT<true> &t );
     729              : 
     730              :     int createFanSpeed( const mx::meta::trueFalseT<false> &f );
     731              : 
     732              :   public:
     733              :     /// Startup function
     734              :     /**
     735              :       * This should be called in `derivedT::appStartup` as
     736              :       * \code
     737              :         stdCamera<derivedT>::appStartup();
     738              :         \endcode
     739              :       * with appropriate error checking.
     740              :       *
     741              :       * You should set the default/startup values of m_currentROI as well as the min/max/step values for the ROI
     742              :       parameters
     743              :       * before calling this function.
     744              :       *
     745              :       * \returns 0 on success
     746              :       * \returns -1 on error, which is logged.
     747              :       */
     748              :     int appStartup();
     749              : 
     750              :     /// Application logic
     751              :     /** Checks the stdCamera thread
     752              :       *
     753              :       * This should be called from the derived's appLogic() as in
     754              :       * \code
     755              :         stdCamera<derivedT>::appLogic();
     756              :         \endcode
     757              :       * with appropriate error checking.
     758              :       *
     759              :       * \returns 0 on success
     760              :       * \returns -1 on error, which is logged.
     761              :       */
     762              :     int appLogic();
     763              : 
     764              :     /// Actions on power off
     765              :     /**
     766              :       * This should be called from the derived's onPowerOff() as in
     767              :       * \code
     768              :         stdCamera<derivedT>::onPowerOff();
     769              :         \endcode
     770              :       * with appropriate error checking.
     771              :       *
     772              :       * The INDI mutex should be locked before calling.
     773              :       *
     774              :       * \returns 0 on success
     775              :       * \returns -1 on error, which is logged.
     776              :       */
     777              :     int onPowerOff();
     778              : 
     779              :     /// Actions while powered off
     780              :     /**
     781              :       * This should be called from the derived's whilePowerOff() as in
     782              :       * \code
     783              :         stdCamera<derivedT>::whilePowerOff();
     784              :         \endcode
     785              :       * with appropriate error checking.
     786              :       *
     787              :       * \returns 0 on success
     788              :       * \returns -1 on error, which is logged.
     789              :       */
     790              :     int whilePowerOff();
     791              : 
     792              :     /// Application shutdown
     793              :     /** Shuts down the stdCamera thread
     794              :       *
     795              :       * \code
     796              :         stdCamera<derivedT>::appShutdown();
     797              :         \endcode
     798              :       * with appropriate error checking.
     799              :       *
     800              :       * \returns 0 on success
     801              :       * \returns -1 on error, which is logged.
     802              :       */
     803              :     int appShutdown();
     804              : 
     805              :   protected:
     806              :     /** \name INDI
     807              :      *
     808              :      *@{
     809              :      */
     810              :   public:
     811              :     /// The static callback function to be registered for stdCamera properties
     812              :     /** Calls newCallback_stdCamera
     813              :      *
     814              :      * \returns 0 on success.
     815              :      * \returns -1 on error.
     816              :      */
     817              :     static int st_newCallBack_stdCamera(
     818              :         void                    *app,   ///< [in] a pointer to this, will be static_cast-ed to derivedT.
     819              :         const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
     820              :     );
     821              : 
     822              :     /// The callback function for stdCamera properties
     823              :     /** Dispatches to the relevant handler
     824              :      *
     825              :      * \returns 0 on success.
     826              :      * \returns -1 on error.
     827              :      */
     828              :     int newCallBack_stdCamera(
     829              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     830              : 
     831              :     /// Interface to setTempSetPt when the derivedT has temperature control
     832              :     /** Tag-dispatch resolution of c_stdCamera_tempControl==true will call this function.
     833              :      * Calls derivedT::setTempSetPt.
     834              :      */
     835              :     int setTempSetPt( const mx::meta::trueFalseT<true> &t );
     836              : 
     837              :     /// Interface to setTempSetPt when the derivedT does not have temperature control
     838              :     /** Tag-dispatch resolution of c_stdCamera_tempControl==false will call this function.
     839              :      * Prevents requiring derivedT::setTempSetPt.
     840              :      */
     841              :     int setTempSetPt( const mx::meta::trueFalseT<false> &f );
     842              : 
     843              :     /// Callback to process a NEW CCD temp request
     844              :     /**
     845              :      * \returns 0 on success.
     846              :      * \returns -1 on error.
     847              :      */
     848              :     int newCallBack_temp(
     849              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     850              : 
     851              :     /// Interface to setTempControl when the derivedT has temperature control
     852              :     /** Tag-dispatch resolution of c_stdCamera_tempControl==true will call this function.
     853              :      * Calls derivedT::setTempControl.
     854              :      */
     855              :     int setTempControl( const mx::meta::trueFalseT<true> &t );
     856              : 
     857              :     /// Interface to setTempControl when the derivedT does not have temperature control
     858              :     /** Tag-dispatch resolution of c_stdCamera_tempControl==false will call this function.
     859              :      * Prevents requiring derivedT::setTempControl.
     860              :      */
     861              :     int setTempControl( const mx::meta::trueFalseT<false> &f );
     862              : 
     863              :     /// Callback to process a NEW CCD temp control request
     864              :     /**
     865              :      * \returns 0 on success.
     866              :      * \returns -1 on error.
     867              :      */
     868              :     int newCallBack_temp_controller(
     869              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     870              : 
     871              :     /// Interface to setReadoutSpeed when the derivedT has readout speed control
     872              :     /** Tag-dispatch resolution of c_stdCamera_readoutSpeed==true will call this function.
     873              :      * Calls derivedT::setReadoutSpeed.
     874              :      */
     875              :     int setReadoutSpeed( const mx::meta::trueFalseT<true> &t );
     876              : 
     877              :     /// Interface to setReadoutSpeed when the derivedT does not have readout speed control
     878              :     /** Tag-dispatch resolution of c_stdCamera_readoutSpeed==false will call this function.
     879              :      * Just returns 0.
     880              :      */
     881              :     int setReadoutSpeed( const mx::meta::trueFalseT<false> &f );
     882              : 
     883              :     /// Callback to process a NEW readout speed  request
     884              :     /**
     885              :      * \returns 0 on success.
     886              :      * \returns -1 on error.
     887              :      */
     888              :     int newCallBack_readoutSpeed(
     889              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     890              : 
     891              :     /// Interface to setVShiftSpeed when the derivedT has vshift speed control
     892              :     /** Tag-dispatch resolution of c_stdCamera_vShiftSpeed==true will call this function.
     893              :      * Calls derivedT::setVShiftSpeed.
     894              :      */
     895              :     int setVShiftSpeed( const mx::meta::trueFalseT<true> &t );
     896              : 
     897              :     /// Interface to setVShiftSpeed when the derivedT does not have vshift speed control
     898              :     /** Tag-dispatch resolution of c_stdCamera_vShiftSpeed==false will call this function.
     899              :      * Just returns 0.
     900              :      */
     901              :     int setVShiftSpeed( const mx::meta::trueFalseT<false> &f );
     902              : 
     903              :     /// Callback to process a NEW vshift speed  request
     904              :     /**
     905              :      * \returns 0 on success.
     906              :      * \returns -1 on error.
     907              :      */
     908              :     int newCallBack_vShiftSpeed(
     909              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     910              : 
     911              :     /// Interface to setEMGain when the derivedT has EM Gain
     912              :     /** Tag-dispatch resolution of c_stdCamera_emGain==true will call this function.
     913              :      * Calls derivedT::setEMGain.
     914              :      */
     915              :     int setEMGain( const mx::meta::trueFalseT<true> &t );
     916              : 
     917              :     /// Interface to setEMGain when the derivedT does not have EM Gain
     918              :     /** Tag-dispatch resolution of c_stdCamera_emGain==false will call this function.
     919              :      * This prevents requiring derivedT to have its own setEMGain().
     920              :      */
     921              :     int setEMGain( const mx::meta::trueFalseT<false> &f );
     922              : 
     923              :     /// Callback to process a NEW EM gain request
     924              :     /**
     925              :      * \returns 0 on success.
     926              :      * \returns -1 on error.
     927              :      */
     928              :     int newCallBack_emgain(
     929              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     930              : 
     931              :     /// Interface to setExpTime when the derivedT uses exposure time controls
     932              :     /** Tag-dispatch resolution of c_stdCamera_exptimeCtrl==true will call this function.
     933              :      * Calls derivedT::setExpTime.
     934              :      */
     935              :     int setExpTime( const mx::meta::trueFalseT<true> &t );
     936              : 
     937              :     /// Interface to setExptime when the derivedT does not use exposure time controls.
     938              :     /** Tag-dispatch resolution of c_stdCamera_exptimeCtrl==false will call this function.
     939              :      * This prevents requiring derivedT to have its own setExpTime().
     940              :      */
     941              :     int setExpTime( const mx::meta::trueFalseT<false> &f );
     942              : 
     943              :     /// Callback to process a NEW exposure time request
     944              :     /**
     945              :      * \returns 0 on success.
     946              :      * \returns -1 on error.
     947              :      */
     948              :     int newCallBack_exptime(
     949              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     950              : 
     951              :     /// Interface to setFPS when the derivedT uses FPS controls
     952              :     /** Tag-dispatch resolution of c_stdCamera_fpsCtrl==true will call this function.
     953              :      * Calls derivedT::setFPS.
     954              :      */
     955              :     int setFPS( const mx::meta::trueFalseT<true> &t );
     956              : 
     957              :     /// Interface to setFPS when the derivedT does not use FPS controls.
     958              :     /** Tag-dispatch resolution of c_stdCamera_hasFPS==false will call this function.
     959              :      * This prevents requiring derivedT to have its own setFPS().
     960              :      */
     961              :     int setFPS( const mx::meta::trueFalseT<false> &f );
     962              : 
     963              :     /// Callback to process a NEW fps request
     964              :     /**
     965              :      * \returns 0 on success.
     966              :      * \returns -1 on error.
     967              :      */
     968              :     int newCallBack_fps(
     969              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     970              : 
     971              :     /// Interface to setFanSpeed when the derivedT exposes fan controls.
     972              :     /** Tag-dispatch resolution of fan control availability will call this function.
     973              :      * Calls derivedT::setFanSpeed.
     974              :      */
     975              :     int setFanSpeed( const mx::meta::trueFalseT<true> &t );
     976              : 
     977              :     /// Interface to setFanSpeed when the derivedT does not expose fan controls.
     978              :     /** Tag-dispatch resolution of fan control availability will call this function.
     979              :      * This prevents requiring derivedT to have its own setFanSpeed().
     980              :      */
     981              :     int setFanSpeed( const mx::meta::trueFalseT<false> &f );
     982              : 
     983              :     /// Callback to process a NEW fan speed request.
     984              :     /**
     985              :      * \returns 0 on success.
     986              :      * \returns -1 on error.
     987              :      */
     988              :     int newCallBack_fanSpeed(
     989              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
     990              : 
     991              :     /// Interface to setAnalogGain when the derivedT exposes analog-gain controls.
     992              :     /** Tag-dispatch resolution of analog-gain control availability will call this function.
     993              :      * Calls derivedT::setAnalogGain.
     994              :      */
     995              :     int setAnalogGain( const mx::meta::trueFalseT<true> &t );
     996              : 
     997              :     /// Interface to setAnalogGain when the derivedT does not expose analog-gain controls.
     998              :     /** Tag-dispatch resolution of analog-gain control availability will call this function.
     999              :      * This prevents requiring derivedT to have its own setAnalogGain().
    1000              :      */
    1001              :     int setAnalogGain( const mx::meta::trueFalseT<false> &f );
    1002              : 
    1003              :     /// Callback to process a NEW analog-gain request.
    1004              :     /**
    1005              :      * \returns 0 on success.
    1006              :      * \returns -1 on error.
    1007              :      */
    1008              :     int newCallBack_analogGain(
    1009              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1010              : 
    1011              :     /// Interface to setLED when the derivedT exposes LED controls.
    1012              :     /** Tag-dispatch resolution of LED control availability will call this function.
    1013              :      * Calls derivedT::setLED.
    1014              :      */
    1015              :     int setLED( const mx::meta::trueFalseT<true> &t );
    1016              : 
    1017              :     /// Interface to setLED when the derivedT does not expose LED controls.
    1018              :     /** Tag-dispatch resolution of LED control availability will call this function.
    1019              :      * This prevents requiring derivedT to have its own setLED().
    1020              :      */
    1021              :     int setLED( const mx::meta::trueFalseT<false> &f );
    1022              : 
    1023              :     /// Callback to process a NEW LED request.
    1024              :     /**
    1025              :      * \returns 0 on success.
    1026              :      * \returns -1 on error.
    1027              :      */
    1028              :     int newCallBack_led(
    1029              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1030              : 
    1031              :     /// Interface to setSynchro when the derivedT has synchronization
    1032              :     /** Tag-dispatch resolution of c_stdCamera_synchro==true will call this function.
    1033              :      * Calls derivedT::setSynchro.
    1034              :      */
    1035              :     int setSynchro( const mx::meta::trueFalseT<true> &t );
    1036              : 
    1037              :     /// Interface to setSynchro when the derivedT does not have synchronization
    1038              :     /** Tag-dispatch resolution of c_stdCamera_ynchro==false will call this function.
    1039              :      * This prevents requiring derivedT to have its own setSynchro().
    1040              :      */
    1041              :     int setSynchro( const mx::meta::trueFalseT<false> &f );
    1042              : 
    1043              :     /// Callback to process a NEW synchro request
    1044              :     /**
    1045              :      * \returns 0 on success.
    1046              :      * \returns -1 on error.
    1047              :      */
    1048              :     int newCallBack_synchro(
    1049              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1050              : 
    1051              :     /// Callback to process a NEW mode request
    1052              :     /**
    1053              :      * \returns 0 on success.
    1054              :      * \returns -1 on error.
    1055              :      */
    1056              :     int newCallBack_mode(
    1057              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1058              : 
    1059              :     /// Callback to process a NEW reconfigure request
    1060              :     /**
    1061              :      * \returns 0 on success.
    1062              :      * \returns -1 on error.
    1063              :      */
    1064              :     int newCallBack_reconfigure(
    1065              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1066              : 
    1067              :     /// Interface to setCropMode when the derivedT has crop mode
    1068              :     /** Tag-dispatch resolution of c_stdCamera_cropMode==true will call this function.
    1069              :      * Calls derivedT::setCropMode.
    1070              :      */
    1071              :     int setCropMode( const mx::meta::trueFalseT<true> &t );
    1072              : 
    1073              :     /// Interface to setCropMode when the derivedT does not have crop mode
    1074              :     /** Tag-dispatch resolution of c_stdCamera_cropMode==false will call this function.
    1075              :      * This prevents requiring derivedT to have its own setCropMode().
    1076              :      */
    1077              :     int setCropMode( const mx::meta::trueFalseT<false> &f );
    1078              : 
    1079              :     /// Callback to process a NEW cropMode request
    1080              :     /**
    1081              :      * \returns 0 on success.
    1082              :      * \returns -1 on error.
    1083              :      */
    1084              :     int newCallBack_cropMode(
    1085              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1086              : 
    1087              :     /// Callback to process a NEW roi_x request
    1088              :     /**
    1089              :      * \returns 0 on success.
    1090              :      * \returns -1 on error.
    1091              :      */
    1092              :     int newCallBack_roi_x(
    1093              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1094              : 
    1095              :     /// Callback to process a NEW roi_y request
    1096              :     /**
    1097              :      * \returns 0 on success.
    1098              :      * \returns -1 on error.
    1099              :      */
    1100              :     int newCallBack_roi_y(
    1101              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1102              : 
    1103              :     /// Callback to process a NEW roi_w request
    1104              :     /**
    1105              :      * \returns 0 on success.
    1106              :      * \returns -1 on error.
    1107              :      */
    1108              :     int newCallBack_roi_w(
    1109              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1110              : 
    1111              :     /// Callback to process a NEW roi_h request
    1112              :     /**
    1113              :      * \returns 0 on success.
    1114              :      * \returns -1 on error.
    1115              :      */
    1116              :     int newCallBack_roi_h(
    1117              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1118              : 
    1119              :     /// Callback to process a NEW bin_x request
    1120              :     /**
    1121              :      * \returns 0 on success.
    1122              :      * \returns -1 on error.
    1123              :      */
    1124              :     int newCallBack_roi_bin_x(
    1125              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1126              : 
    1127              :     /// Callback to process a NEW bin_y request
    1128              :     /**
    1129              :      * \returns 0 on success.
    1130              :      * \returns -1 on error.
    1131              :      */
    1132              :     int newCallBack_roi_bin_y(
    1133              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1134              : 
    1135              :     /// Interface to checkNextROI when the derivedT uses ROIs
    1136              :     /** Tag-dispatch resolution of c_stdCamera_usesROI==true will call this function.
    1137              :      * Calls derivedT::checkNextROI.
    1138              :      */
    1139              :     int checkNextROI( const mx::meta::trueFalseT<true> &t );
    1140              : 
    1141              :     /// Interface to checkNextROI when the derivedT does not use ROIs.
    1142              :     /** Tag-dispatch resolution of c_stdCamera_usesROI==false will call this function.
    1143              :      * This prevents requiring derivedT to have its own checkNextROI().
    1144              :      */
    1145              :     int checkNextROI( const mx::meta::trueFalseT<false> &f );
    1146              : 
    1147              :     /// Callback to process a NEW roi_check request
    1148              :     /**
    1149              :      * \returns 0 on success.
    1150              :      * \returns -1 on error.
    1151              :      */
    1152              :     int newCallBack_roi_check(
    1153              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1154              : 
    1155              :     /// Interface to setNextROI when the derivedT uses ROIs
    1156              :     /** Tag-dispatch resolution of c_stdCamera_usesROI==true will call this function.
    1157              :      * Calls derivedT::setNextROI.
    1158              :      */
    1159              :     int setNextROI( const mx::meta::trueFalseT<true> &t );
    1160              : 
    1161              :     /// Interface to setNextROI when the derivedT does not use ROIs.
    1162              :     /** Tag-dispatch resolution of c_stdCamera_usesROI==false will call this function.
    1163              :      * This prevents requiring derivedT to have its own setNextROI().
    1164              :      */
    1165              :     int setNextROI( const mx::meta::trueFalseT<false> &f );
    1166              : 
    1167              :     /// Callback to process a NEW roi_set request
    1168              :     /**
    1169              :      * \returns 0 on success.
    1170              :      * \returns -1 on error.
    1171              :      */
    1172              :     int newCallBack_roi_set(
    1173              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1174              : 
    1175              :     /// Callback to process a NEW roi_full request
    1176              :     /**
    1177              :      * \returns 0 on success.
    1178              :      * \returns -1 on error.
    1179              :      */
    1180              :     int newCallBack_roi_full(
    1181              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1182              : 
    1183              :     /// Callback to process a NEW roi_fullbin request
    1184              :     /**
    1185              :      * \returns 0 on success.
    1186              :      * \returns -1 on error.
    1187              :      */
    1188              :     int newCallBack_roi_fullbin(
    1189              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1190              : 
    1191              :     /// Callback to process a NEW roi_loadlast request
    1192              :     /**
    1193              :      * \returns 0 on success.
    1194              :      * \returns -1 on error.
    1195              :      */
    1196              :     int newCallBack_roi_loadlast(
    1197              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1198              : 
    1199              :     /// Callback to process a NEW roi_last request
    1200              :     /**
    1201              :      * \returns 0 on success.
    1202              :      * \returns -1 on error.
    1203              :      */
    1204              :     int newCallBack_roi_last(
    1205              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1206              : 
    1207              :     /// Callback to process a NEW roi_default request
    1208              :     /**
    1209              :      * \returns 0 on success.
    1210              :      * \returns -1 on error.
    1211              :      */
    1212              :     int newCallBack_roi_default(
    1213              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1214              : 
    1215              :     /// Interface to setShutter when the derivedT has a shutter
    1216              :     /** Tag-dispatch resolution of c_stdCamera_hasShutter==true will call this function.
    1217              :      * Calls derivedT::setShutter.
    1218              :      */
    1219              :     int setShutter( int ss, const mx::meta::trueFalseT<true> &t );
    1220              : 
    1221              :     /// Interface to setShutter when the derivedT does not have a shutter.
    1222              :     /** Tag-dispatch resolution of c_stdCamera_hasShutter==false will call this function.
    1223              :      * This prevents requiring derivedT to have its own setShutter().
    1224              :      */
    1225              :     int setShutter( int ss, const mx::meta::trueFalseT<false> &f );
    1226              : 
    1227              :     /// Callback to process a NEW shutter request
    1228              :     /**
    1229              :      * \returns 0 on success.
    1230              :      * \returns -1 on error.
    1231              :      */
    1232              :     int newCallBack_shutter(
    1233              :         const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
    1234              : 
    1235              :     /// Interface to stateString when the derivedT provides it
    1236              :     /** Tag-dispatch resolution of c_stdCamera_usesStateString==true will call this function.
    1237              :      * Calls derivedT::stateString.
    1238              :      */
    1239              :     std::string stateString( const mx::meta::trueFalseT<true> &t );
    1240              : 
    1241              :     /// Interface to stateString when the derivedT does not provide it
    1242              :     /** Tag-dispatch resolution of c_stdCamera_usesStateString==false will call this function.
    1243              :      * returns "".
    1244              :      */
    1245              :     std::string stateString( const mx::meta::trueFalseT<false> &f );
    1246              : 
    1247              :     /// Interface to stateStringValid when the derivedT provides it
    1248              :     /** Tag-dispatch resolution of c_stdCamera_usesStateString==true will call this function.
    1249              :      * Calls derivedT::stateStringValid.
    1250              :      */
    1251              :     bool stateStringValid( const mx::meta::trueFalseT<true> &t );
    1252              : 
    1253              :     /// Interface to stateStringValid when the derivedT does not provide it
    1254              :     /** Tag-dispatch resolution of c_stdCamera_usesStateString==false will call this function.
    1255              :      * returns false.
    1256              :      */
    1257              :     bool stateStringValid( const mx::meta::trueFalseT<false> &f );
    1258              : 
    1259              :     /// Update the INDI properties for this device controller
    1260              :     /** You should call this once per main loop.
    1261              :      * It is not called automatically.
    1262              :      *
    1263              :      * \returns 0 on success.
    1264              :      * \returns -1 on error.
    1265              :      */
    1266              :     int updateINDI();
    1267              : 
    1268              :     ///@}
    1269              : 
    1270              :     /** \name Telemeter Interface
    1271              :      * @{
    1272              :      */
    1273              : 
    1274              :     int recordCamera( bool force = false );
    1275              : 
    1276              :     ///@}
    1277              : 
    1278              :   private:
    1279          543 :     derivedT &derived()
    1280              :     {
    1281          543 :         return *static_cast<derivedT *>( this );
    1282              :     }
    1283              : };
    1284              : 
    1285              : template <class derivedT>
    1286          227 : stdCamera<derivedT>::~stdCamera() noexcept
    1287              : {
    1288          227 :     return;
    1289          227 : }
    1290              : 
    1291              : template <class derivedT>
    1292           16 : int stdCamera<derivedT>::setupConfig( mx::app::appConfigurator &config )
    1293              : {
    1294              :     if( derivedT::c_stdCamera_tempControl )
    1295              :     {
    1296          224 :         config.add( "camera.startupTemp",
    1297              :                     "",
    1298              :                     "camera.startupTemp",
    1299              :                     argType::Required,
    1300              :                     "camera",
    1301              :                     "startupTemp",
    1302              :                     false,
    1303              :                     "float",
    1304              :                     "The temperature setpoint to set after a power-on [C].  Default is 20 C." );
    1305              :     }
    1306              : 
    1307              :     if( derivedT::c_stdCamera_readoutSpeed )
    1308              :     {
    1309              :         config.add( "camera.defaultReadoutSpeed",
    1310              :                     "",
    1311              :                     "camera.defaultReadoutSpeed",
    1312              :                     argType::Required,
    1313              :                     "camera",
    1314              :                     "defaultReadoutSpeed",
    1315              :                     false,
    1316              :                     "string",
    1317              :                     "The default amplifier and readout speed." );
    1318              :     }
    1319              : 
    1320              :     if( derivedT::c_stdCamera_vShiftSpeed )
    1321              :     {
    1322              :         config.add( "camera.defaultVShiftSpeed",
    1323              :                     "",
    1324              :                     "camera.defaultVShiftSpeed",
    1325              :                     argType::Required,
    1326              :                     "camera",
    1327              :                     "defaultVShiftSpeed",
    1328              :                     false,
    1329              :                     "string",
    1330              :                     "The default vertical shift speed." );
    1331              :     }
    1332              : 
    1333              :     if( c_hasLegacyFanSpeed )
    1334              :     {
    1335              :         config.add( "camera.fanSpeedControl",
    1336              :                     "",
    1337              :                     "camera.fanSpeedControl",
    1338              :                     argType::Required,
    1339              :                     "camera",
    1340              :                     "fanSpeedControl",
    1341              :                     false,
    1342              :                     "bool",
    1343              :                     "Whether or not fan-speed control is exposed." );
    1344              : 
    1345              :         config.add( "camera.defaultFanSpeed",
    1346              :                     "",
    1347              :                     "camera.defaultFanSpeed",
    1348              :                     argType::Required,
    1349              :                     "camera",
    1350              :                     "defaultFanSpeed",
    1351              :                     false,
    1352              :                     "string",
    1353              :                     "The default fan speed. Must be one of high, medium, low, or off." );
    1354              :     }
    1355              : 
    1356              :     if( derivedT::c_stdCamera_emGain )
    1357              :     {
    1358           56 :         config.add( "camera.maxEMGain",
    1359              :                     "",
    1360              :                     "camera.maxEMGain",
    1361              :                     argType::Required,
    1362              :                     "camera",
    1363              :                     "maxEMGain",
    1364              :                     false,
    1365              :                     "unsigned",
    1366              :                     "The maximum EM gain which can be set by the user." );
    1367              :     }
    1368              : 
    1369              :     if( derivedT::c_stdCamera_usesModes )
    1370              :     {
    1371           56 :         config.add( "camera.startupMode",
    1372              :                     "",
    1373              :                     "camera.startupMode",
    1374              :                     argType::Required,
    1375              :                     "camera",
    1376              :                     "startupMode",
    1377              :                     false,
    1378              :                     "string",
    1379              :                     "The mode to set upon power on or application startup." );
    1380              :     }
    1381              : 
    1382              :     if( derivedT::c_stdCamera_usesROI )
    1383              :     {
    1384          168 :         config.add( "camera.default_x",
    1385              :                     "",
    1386              :                     "camera.default_x",
    1387              :                     argType::Required,
    1388              :                     "camera",
    1389              :                     "default_x",
    1390              :                     false,
    1391              :                     "float",
    1392              :                     "The default ROI x position." );
    1393              : 
    1394          168 :         config.add( "camera.default_y",
    1395              :                     "",
    1396              :                     "camera.default_y",
    1397              :                     argType::Required,
    1398              :                     "camera",
    1399              :                     "default_y",
    1400              :                     false,
    1401              :                     "float",
    1402              :                     "The default ROI y position." );
    1403              : 
    1404          168 :         config.add( "camera.default_w",
    1405              :                     "",
    1406              :                     "camera.default_w",
    1407              :                     argType::Required,
    1408              :                     "camera",
    1409              :                     "default_w",
    1410              :                     false,
    1411              :                     "int",
    1412              :                     "The default ROI width." );
    1413              : 
    1414          168 :         config.add( "camera.default_h",
    1415              :                     "",
    1416              :                     "camera.default_h",
    1417              :                     argType::Required,
    1418              :                     "camera",
    1419              :                     "default_h",
    1420              :                     false,
    1421              :                     "int",
    1422              :                     "The default ROI height." );
    1423              : 
    1424          168 :         config.add( "camera.default_bin_x",
    1425              :                     "",
    1426              :                     "camera.default_bin_x",
    1427              :                     argType::Required,
    1428              :                     "camera",
    1429              :                     "default_bin_x",
    1430              :                     false,
    1431              :                     "int",
    1432              :                     "The default ROI x binning." );
    1433              : 
    1434          156 :         config.add( "camera.default_bin_y",
    1435              :                     "",
    1436              :                     "camera.default_bin_y",
    1437              :                     argType::Required,
    1438              :                     "camera",
    1439              :                     "default_bin_y",
    1440              :                     false,
    1441              :                     "int",
    1442              :                     "The default ROI y binning." );
    1443              :     }
    1444              : 
    1445           16 :     return 0;
    1446              : }
    1447              : 
    1448              : template <class derivedT>
    1449           16 : int stdCamera<derivedT>::loadConfig( mx::app::appConfigurator &config )
    1450              : {
    1451              :     if( derivedT::c_stdCamera_tempControl )
    1452              :     {
    1453           32 :         config( m_startupTemp, "camera.startupTemp" );
    1454              :     }
    1455              : 
    1456              :     if( derivedT::c_stdCamera_readoutSpeed )
    1457              :     {
    1458              :         config( m_defaultReadoutSpeed, "camera.defaultReadoutSpeed" );
    1459              :     }
    1460              : 
    1461              :     if( derivedT::c_stdCamera_vShiftSpeed )
    1462              :     {
    1463              :         config( m_defaultVShiftSpeed, "camera.defaultVShiftSpeed" );
    1464              :     }
    1465              : 
    1466              :     if( c_hasLegacyFanSpeed )
    1467              :     {
    1468              :         config( m_fanSpeedControlEnabled, "camera.fanSpeedControl" );
    1469              :         config( m_defaultFanSpeed, "camera.defaultFanSpeed" );
    1470              : 
    1471              :         if( m_defaultFanSpeed != "high" && m_defaultFanSpeed != "medium" && m_defaultFanSpeed != "low" &&
    1472              :             m_defaultFanSpeed != "off" )
    1473              :         {
    1474              :             return derivedT::template log<software_critical, -1>(
    1475              :                 { __FILE__,
    1476              :                   __LINE__,
    1477              :                   "invalid camera.defaultFanSpeed: '" + m_defaultFanSpeed +
    1478              :                       "'. Must be one of high, medium, low, or off." } );
    1479              :         }
    1480              :     }
    1481              : 
    1482              :     if( derivedT::c_stdCamera_emGain )
    1483              :     {
    1484            4 :         config( m_maxEMGain, "camera.maxEMGain" );
    1485              :     }
    1486              : 
    1487              :     if( derivedT::c_stdCamera_usesModes )
    1488              :     {
    1489            4 :         int rv = loadCameraConfig( m_cameraModes, config );
    1490              : 
    1491            4 :         if( rv < 0 )
    1492              :         {
    1493            3 :             if( rv == CAMCTRL_E_NOCONFIGS )
    1494              :             {
    1495            3 :                 derivedT::template log<text_log>( "No camera configurations found.", logPrio::LOG_CRITICAL );
    1496              :             }
    1497              :         }
    1498              : 
    1499            8 :         config( m_startupMode, "camera.startupMode" );
    1500              :     }
    1501              : 
    1502              :     if( derivedT::c_stdCamera_usesROI )
    1503              :     {
    1504           24 :         config( m_full_x, "camera.full_x" );
    1505           24 :         config( m_full_y, "camera.full_y" );
    1506           24 :         config( m_full_w, "camera.full_w" );
    1507           24 :         config( m_full_h, "camera.full_h" );
    1508           24 :         config( m_full_bin_x, "camera.full_bin_x" );
    1509           12 :         config( m_full_bin_y, "camera.full_bin_y" );
    1510              : 
    1511           12 :         if( m_full_x == 0 )
    1512              :         {
    1513            0 :             return derivedT::template log<software_critical, -1>(
    1514            0 :                 { __FILE__, __LINE__, "full ROI x (camera.full_x) not set" } );
    1515              :         }
    1516              : 
    1517           12 :         if( m_full_y == 0 )
    1518              :         {
    1519            0 :             return derivedT::template log<software_critical, -1>(
    1520            0 :                 { __FILE__, __LINE__, "full ROI y (camera.full_y) not set" } );
    1521              :         }
    1522              : 
    1523           12 :         if( m_full_w == 0 )
    1524              :         {
    1525            0 :             return derivedT::template log<software_critical, -1>(
    1526            0 :                 { __FILE__, __LINE__, "full ROI w (camera.full_w) not set" } );
    1527              :         }
    1528              : 
    1529           12 :         if( m_full_h == 0 )
    1530              :         {
    1531            0 :             return derivedT::template log<software_critical, -1>(
    1532            0 :                 { __FILE__, __LINE__, "full ROI h (camera.full_h) not set" } );
    1533              :         }
    1534              : 
    1535           12 :         if( m_full_bin_x < 1 )
    1536              :         {
    1537            0 :             return derivedT::template log<software_critical, -1>(
    1538            0 :                 { __FILE__, __LINE__, "full ROI bin-x (camera.full_bin_x) not set" } );
    1539              :         }
    1540              : 
    1541           12 :         if( m_full_bin_y < 1 )
    1542              :         {
    1543            0 :             return derivedT::template log<software_critical, -1>(
    1544            0 :                 { __FILE__, __LINE__, "full ROI bin-y (camera.full_bin_y) not set" } );
    1545              :         }
    1546              : 
    1547           24 :         config( m_default_x, "camera.default_x" );
    1548           24 :         config( m_default_y, "camera.default_y" );
    1549           24 :         config( m_default_w, "camera.default_w" );
    1550           24 :         config( m_default_h, "camera.default_h" );
    1551           24 :         config( m_default_bin_x, "camera.default_bin_x" );
    1552           12 :         config( m_default_bin_y, "camera.default_bin_y" );
    1553              : 
    1554              :         // If default is not setup properly, it defaults to full
    1555           12 :         if( m_default_x == 0 || m_default_y == 0 || m_default_w == 0 || m_default_h == 0 || m_default_bin_x < 1 ||
    1556           12 :             m_default_bin_y < 1 )
    1557              :         {
    1558            0 :             m_default_x     = m_full_x;
    1559            0 :             m_default_y     = m_full_y;
    1560            0 :             m_default_w     = m_full_w;
    1561            0 :             m_default_h     = m_full_h;
    1562            0 :             m_default_bin_x = m_default_bin_x;
    1563            0 :             m_default_bin_y = m_default_bin_y;
    1564              :         }
    1565              : 
    1566              :         // now always start with current and next set to default
    1567              : 
    1568           12 :         m_currentROI.x     = m_default_x;
    1569           12 :         m_currentROI.y     = m_default_y;
    1570           12 :         m_currentROI.w     = m_default_w;
    1571           12 :         m_currentROI.h     = m_default_h;
    1572           12 :         m_currentROI.bin_x = m_default_bin_x;
    1573           12 :         m_currentROI.bin_y = m_default_bin_y;
    1574              : 
    1575           12 :         m_nextROI.x     = m_default_x;
    1576           12 :         m_nextROI.y     = m_default_y;
    1577           12 :         m_nextROI.w     = m_default_w;
    1578           12 :         m_nextROI.h     = m_default_h;
    1579           12 :         m_nextROI.bin_x = m_default_bin_x;
    1580           12 :         m_nextROI.bin_y = m_default_bin_y;
    1581              :     }
    1582              : 
    1583           16 :     return 0;
    1584              : }
    1585              : 
    1586              : template <class derivedT>
    1587              : int stdCamera<derivedT>::createReadoutSpeed( const mx::meta::trueFalseT<true> &t )
    1588              : {
    1589              :     static_cast<void>( t );
    1590              : 
    1591              :     derived().createStandardIndiSelectionSw(
    1592              :         m_indiP_readoutSpeed, "readout_speed", m_readoutSpeedNames, "Readout Speed" );
    1593              : 
    1594              :     // Set the labes if provided
    1595              :     if( m_readoutSpeedNameLabels.size() == m_readoutSpeedNames.size() )
    1596              :     {
    1597              :         for( size_t n = 0; n < m_readoutSpeedNames.size(); ++n )
    1598              :             m_indiP_readoutSpeed[m_readoutSpeedNames[n]].setLabel( m_readoutSpeedNameLabels[n] );
    1599              :     }
    1600              : 
    1601              :     derived().registerIndiPropertyNew( m_indiP_readoutSpeed, st_newCallBack_stdCamera );
    1602              : 
    1603              :     return 0;
    1604              : }
    1605              : 
    1606              : template <class derivedT>
    1607              : int stdCamera<derivedT>::createReadoutSpeed( const mx::meta::trueFalseT<0> &f )
    1608              : {
    1609              :     static_cast<void>( f );
    1610              : 
    1611              :     return 0;
    1612              : }
    1613              : 
    1614              : template <class derivedT>
    1615              : int stdCamera<derivedT>::createVShiftSpeed( const mx::meta::trueFalseT<true> &t )
    1616              : {
    1617              :     static_cast<void>( t );
    1618              : 
    1619              :     derived().createStandardIndiSelectionSw(
    1620              :         m_indiP_vShiftSpeed, "vshift_speed", m_vShiftSpeedNames, "Vert. Shift Speed" );
    1621              : 
    1622              :     if( m_vShiftSpeedNameLabels.size() == m_vShiftSpeedNames.size() )
    1623              :     {
    1624              :         for( size_t n = 0; n < m_vShiftSpeedNames.size(); ++n )
    1625              :             m_indiP_vShiftSpeed[m_vShiftSpeedNames[n]].setLabel( m_vShiftSpeedNameLabels[n] );
    1626              :     }
    1627              : 
    1628              :     derived().registerIndiPropertyNew( m_indiP_vShiftSpeed, st_newCallBack_stdCamera );
    1629              : 
    1630              :     return 0;
    1631              : }
    1632              : 
    1633              : template <class derivedT>
    1634              : int stdCamera<derivedT>::createVShiftSpeed( const mx::meta::trueFalseT<0> &f )
    1635              : {
    1636              :     static_cast<void>( f );
    1637              : 
    1638              :     return 0;
    1639              : }
    1640              : 
    1641              : template <class derivedT>
    1642              : int stdCamera<derivedT>::createFanSpeed( const mx::meta::trueFalseT<true> &t )
    1643              : {
    1644              :     static_cast<void>( t );
    1645              : 
    1646              :     derived().createStandardIndiSelectionSw( m_indiP_fanSpeed, "fan_speed", m_fanSpeedNames, "Fan Speed" );
    1647              : 
    1648              :     if( m_fanSpeedNameLabels.size() == m_fanSpeedNames.size() )
    1649              :     {
    1650              :         for( size_t n = 0; n < m_fanSpeedNames.size(); ++n )
    1651              :         {
    1652              :             m_indiP_fanSpeed[m_fanSpeedNames[n]].setLabel( m_fanSpeedNameLabels[n] );
    1653              :         }
    1654              :     }
    1655              : 
    1656              :     derived().registerIndiPropertyNew( m_indiP_fanSpeed, st_newCallBack_stdCamera );
    1657              : 
    1658              :     return 0;
    1659              : }
    1660              : 
    1661              : template <class derivedT>
    1662              : int stdCamera<derivedT>::createFanSpeed( const mx::meta::trueFalseT<0> &f )
    1663              : {
    1664              :     static_cast<void>( f );
    1665              : 
    1666              :     return 0;
    1667              : }
    1668              : 
    1669              : template <class derivedT>
    1670            6 : int stdCamera<derivedT>::appStartup()
    1671              : {
    1672              : 
    1673              :     if( derivedT::c_stdCamera_tempControl )
    1674              :     {
    1675              :         // The min/max/step values should be set in derivedT before this is called.
    1676           54 :         derived().createStandardIndiNumber(
    1677           12 :             m_indiP_temp, "temp_ccd", m_minTemp, m_maxTemp, m_stepTemp, "%0.1f", "CCD Temperature", "CCD Temperature" );
    1678           12 :         m_indiP_temp["current"].set( m_ccdTemp );
    1679           12 :         m_indiP_temp["target"].set( m_ccdTempSetpt );
    1680            6 :         if( derived().registerIndiPropertyNew( m_indiP_temp, st_newCallBack_stdCamera ) < 0 )
    1681              :         {
    1682              : #ifndef STDCAMERA_TEST_NOLOG
    1683            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1684              : #endif
    1685            0 :             return -1;
    1686              :         }
    1687              : 
    1688           42 :         derived().createStandardIndiToggleSw(
    1689            6 :             m_indiP_tempcont, "temp_controller", "CCD Temperature", "Control On/Off" );
    1690           12 :         m_indiP_tempcont["toggle"].set( pcf::IndiElement::Off );
    1691            6 :         if( derived().registerIndiPropertyNew( m_indiP_tempcont, st_newCallBack_stdCamera ) < 0 )
    1692              :         {
    1693              : #ifndef STDCAMERA_TEST_NOLOG
    1694            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1695              : #endif
    1696            0 :             return -1;
    1697              :         }
    1698              : 
    1699           66 :         derived().createROIndiText(
    1700            6 :             m_indiP_tempstat, "temp_control", "status", "CCD Temperature", "", "CCD Temperature" );
    1701            6 :         if( derived().registerIndiPropertyReadOnly( m_indiP_tempstat ) < 0 )
    1702              :         {
    1703              : #ifndef STDCAMERA_TEST_NOLOG
    1704            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1705              : #endif
    1706            0 :             return -1;
    1707              :         }
    1708              :     }
    1709              :     else if( derivedT::c_stdCamera_temp )
    1710              :     {
    1711              :         derived().createROIndiNumber( m_indiP_temp, "temp_ccd", "CCD Temperature", "CCD Temperature" );
    1712              :         m_indiP_temp.add( pcf::IndiElement( "current" ) );
    1713              :         m_indiP_temp["current"].set( m_ccdTemp );
    1714              :         if( derived().registerIndiPropertyReadOnly( m_indiP_temp ) < 0 )
    1715              :         {
    1716              : #ifndef STDCAMERA_TEST_NOLOG
    1717              :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1718              : #endif
    1719              :             return -1;
    1720              :         }
    1721              :     }
    1722              : 
    1723              :     if( derivedT::c_stdCamera_readoutSpeed )
    1724              :     {
    1725              :         mx::meta::trueFalseT<derivedT::c_stdCamera_readoutSpeed> tf;
    1726              :         if( createReadoutSpeed( tf ) < 0 )
    1727              :         {
    1728              : #ifndef STDCAMERA_TEST_NOLOG
    1729              :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1730              : #endif
    1731              :             return -1;
    1732              :         }
    1733              :     }
    1734              : 
    1735              :     if( derivedT::c_stdCamera_vShiftSpeed )
    1736              :     {
    1737              :         mx::meta::trueFalseT<derivedT::c_stdCamera_vShiftSpeed> tf;
    1738              :         if( createVShiftSpeed( tf ) < 0 )
    1739              :         {
    1740              : #ifndef STDCAMERA_TEST_NOLOG
    1741              :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1742              : #endif
    1743              :             return -1;
    1744              :         }
    1745              :     }
    1746              : 
    1747              :     if( derivedT::c_stdCamera_emGain )
    1748              :     {
    1749           24 :         derived().createStandardIndiNumber( m_indiP_emGain, "emgain", 0, 1000, 1, "%0.3f" );
    1750            3 :         if( derived().registerIndiPropertyNew( m_indiP_emGain, st_newCallBack_stdCamera ) < 0 )
    1751              :         {
    1752              : #ifndef STDCAMERA_TEST_NOLOG
    1753            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1754              : #endif
    1755            0 :             return -1;
    1756              :         }
    1757              :     }
    1758              : 
    1759              :     if( derivedT::c_stdCamera_exptimeCtrl )
    1760              :     {
    1761              :         derived().createStandardIndiNumber(
    1762              :             m_indiP_exptime, "exptime", m_minExpTime, m_maxExpTime, m_stepExpTime, "%0.3f" );
    1763              :         if( derived().registerIndiPropertyNew( m_indiP_exptime, st_newCallBack_stdCamera ) < 0 )
    1764              :         {
    1765              : #ifndef STDCAMERA_TEST_NOLOG
    1766              :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1767              : #endif
    1768              :             return -1;
    1769              :         }
    1770              :     }
    1771              : 
    1772              :     if( derivedT::c_stdCamera_fpsCtrl )
    1773              :     {
    1774           48 :         derived().createStandardIndiNumber( m_indiP_fps, "fps", m_minFPS, m_maxFPS, m_stepFPS, "%0.2f" );
    1775            6 :         if( derived().registerIndiPropertyNew( m_indiP_fps, st_newCallBack_stdCamera ) < 0 )
    1776              :         {
    1777              : #ifndef STDCAMERA_TEST_NOLOG
    1778            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1779              : #endif
    1780            0 :             return -1;
    1781              :         }
    1782              :     }
    1783              :     else if( derivedT::c_stdCamera_fps )
    1784              :     {
    1785              :         derived().createROIndiNumber( m_indiP_fps, "fps" );
    1786              :         m_indiP_fps.add( pcf::IndiElement( "current" ) );
    1787              :         m_indiP_fps["current"].setMin( m_minFPS );
    1788              :         m_indiP_fps["current"].setMax( m_maxFPS );
    1789              :         m_indiP_fps["current"].setStep( m_stepFPS );
    1790              :         m_indiP_fps["current"].setFormat( "%0.2f" );
    1791              : 
    1792              :         if( derived().registerIndiPropertyReadOnly( m_indiP_fps ) < 0 )
    1793              :         {
    1794              : #ifndef STDCAMERA_TEST_NOLOG
    1795              :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1796              : #endif
    1797              :             return -1;
    1798              :         }
    1799              :     }
    1800              : 
    1801              :     if( c_hasFan && ( !c_hasLegacyFanSpeed || m_fanSpeedControlEnabled ) )
    1802              :     {
    1803            3 :         if( m_fanSpeedNames.empty() )
    1804              :         {
    1805              : #ifndef STDCAMERA_TEST_NOLOG
    1806            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__, "no fan control options configured" } );
    1807              : #endif
    1808            0 :             return -1;
    1809              :         }
    1810              : 
    1811            3 :         if( m_fanSpeedNameLabels.size() == m_fanSpeedNames.size() )
    1812              :         {
    1813           24 :             derived().createStandardIndiSelectionSw(
    1814            6 :                 m_indiP_fanSpeed, "fan_speed", m_fanSpeedNames, m_fanSpeedNameLabels, "Fan Speed", "Fan" );
    1815              :         }
    1816              :         else
    1817              :         {
    1818            0 :             derived().createStandardIndiSelectionSw(
    1819            0 :                 m_indiP_fanSpeed, "fan_speed", m_fanSpeedNames, "Fan Speed", "Fan" );
    1820              :         }
    1821              : 
    1822            3 :         if( derived().registerIndiPropertyNew( m_indiP_fanSpeed, st_newCallBack_stdCamera ) < 0 )
    1823              :         {
    1824              : #ifndef STDCAMERA_TEST_NOLOG
    1825            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1826              : #endif
    1827            0 :             return -1;
    1828              :         }
    1829              :     }
    1830              : 
    1831              :     if( c_hasAnalogGain )
    1832              :     {
    1833            3 :         if( m_analogGainNames.empty() )
    1834              :         {
    1835              : #ifndef STDCAMERA_TEST_NOLOG
    1836            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__, "no analog gain options configured" } );
    1837              : #endif
    1838            0 :             return -1;
    1839              :         }
    1840              : 
    1841            3 :         if( m_analogGainNameLabels.size() == m_analogGainNames.size() )
    1842              :         {
    1843           24 :             derived().createStandardIndiSelectionSw(
    1844            6 :                 m_indiP_analogGain, "analog_gain", m_analogGainNames, m_analogGainNameLabels, "Analog Gain", "Gain" );
    1845              :         }
    1846              :         else
    1847              :         {
    1848            0 :             derived().createStandardIndiSelectionSw(
    1849            0 :                 m_indiP_analogGain, "analog_gain", m_analogGainNames, "Analog Gain", "Gain" );
    1850              :         }
    1851              : 
    1852            3 :         if( derived().registerIndiPropertyNew( m_indiP_analogGain, st_newCallBack_stdCamera ) < 0 )
    1853              :         {
    1854              : #ifndef STDCAMERA_TEST_NOLOG
    1855            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1856              : #endif
    1857            0 :             return -1;
    1858              :         }
    1859              :     }
    1860              : 
    1861              :     if( c_hasLED )
    1862              :     {
    1863           18 :         derived().createStandardIndiToggleSw( m_indiP_led, "led", "Status LED", "LED" );
    1864            3 :         if( derived().registerIndiPropertyNew( m_indiP_led, st_newCallBack_stdCamera ) < 0 )
    1865              :         {
    1866              : #ifndef STDCAMERA_TEST_NOLOG
    1867            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1868              : #endif
    1869            0 :             return -1;
    1870              :         }
    1871              :     }
    1872              : 
    1873              :     if( derivedT::c_stdCamera_synchro )
    1874              :     {
    1875           18 :         derived().createStandardIndiToggleSw( m_indiP_synchro, "synchro", "Synchronization", "Synchronization" );
    1876            3 :         if( derived().registerIndiPropertyNew( m_indiP_synchro, st_newCallBack_stdCamera ) < 0 )
    1877              :         {
    1878              : #ifndef STDCAMERA_TEST_NOLOG
    1879            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1880              : #endif
    1881            0 :             return -1;
    1882              :         }
    1883              :     }
    1884              : 
    1885              :     if( derivedT::c_stdCamera_usesModes )
    1886              :     {
    1887            3 :         std::vector<std::string> modeNames;
    1888            5 :         for( auto it = m_cameraModes.begin(); it != m_cameraModes.end(); ++it )
    1889              :         {
    1890            2 :             modeNames.push_back( it->first );
    1891              :         }
    1892              : 
    1893           21 :         if( derived().createStandardIndiSelectionSw( m_indiP_mode, "mode", modeNames ) < 0 )
    1894              :         {
    1895            1 :             derivedT::template log<software_critical>( { __FILE__, __LINE__ } );
    1896            1 :             return -1;
    1897              :         }
    1898            2 :         if( derived().registerIndiPropertyNew( m_indiP_mode, st_newCallBack_stdCamera ) < 0 )
    1899              :         {
    1900              : #ifndef STDCAMERA_TEST_NOLOG
    1901            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1902              : #endif
    1903            0 :             return -1;
    1904              :         }
    1905            3 :     }
    1906              : 
    1907           30 :     derived().createStandardIndiRequestSw( m_indiP_reconfig, "reconfigure" );
    1908            5 :     if( derived().registerIndiPropertyNew( m_indiP_reconfig, st_newCallBack_stdCamera ) < 0 )
    1909              :     {
    1910              : #ifndef STDCAMERA_TEST_NOLOG
    1911            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1912              : #endif
    1913            0 :         return -1;
    1914              :     }
    1915              : 
    1916              :     if( derivedT::c_stdCamera_usesROI )
    1917              :     {
    1918              :         // The min/max/step values should be set in derivedT before this is called.
    1919           24 :         derived().createStandardIndiNumber( m_indiP_roi_x, "roi_region_x", m_minROIx, m_maxROIx, m_stepROIx, "%0.1f" );
    1920            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_x, st_newCallBack_stdCamera ) < 0 )
    1921              :         {
    1922              : #ifndef STDCAMERA_TEST_NOLOG
    1923            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1924              : #endif
    1925            0 :             return -1;
    1926              :         }
    1927              : 
    1928           24 :         derived().createStandardIndiNumber( m_indiP_roi_y, "roi_region_y", m_minROIy, m_maxROIy, m_stepROIy, "%0.1f" );
    1929            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_y, st_newCallBack_stdCamera ) < 0 )
    1930              :         {
    1931              : #ifndef STDCAMERA_TEST_NOLOG
    1932            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1933              : #endif
    1934            0 :             return -1;
    1935              :         }
    1936              : 
    1937           27 :         derived().createStandardIndiNumber(
    1938            6 :             m_indiP_roi_w, "roi_region_w", m_minROIWidth, m_maxROIWidth, m_stepROIWidth, "%d" );
    1939            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_w, st_newCallBack_stdCamera ) < 0 )
    1940              :         {
    1941              : #ifndef STDCAMERA_TEST_NOLOG
    1942            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1943              : #endif
    1944            0 :             return -1;
    1945              :         }
    1946              : 
    1947           27 :         derived().createStandardIndiNumber(
    1948            6 :             m_indiP_roi_h, "roi_region_h", m_minROIHeight, m_maxROIHeight, m_stepROIHeight, "%d" );
    1949            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_h, st_newCallBack_stdCamera ) < 0 )
    1950              :         {
    1951              : #ifndef STDCAMERA_TEST_NOLOG
    1952            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1953              : #endif
    1954            0 :             return -1;
    1955              :         }
    1956              : 
    1957           27 :         derived().createStandardIndiNumber(
    1958            6 :             m_indiP_roi_bin_x, "roi_region_bin_x", m_minROIBinning_x, m_maxROIBinning_x, m_stepROIBinning_x, "%f" );
    1959            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_bin_x, st_newCallBack_stdCamera ) < 0 )
    1960              :         {
    1961              : #ifndef STDCAMERA_TEST_NOLOG
    1962            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1963              : #endif
    1964            0 :             return -1;
    1965              :         }
    1966              : 
    1967           27 :         derived().createStandardIndiNumber(
    1968            6 :             m_indiP_roi_bin_y, "roi_region_bin_y", m_minROIBinning_y, m_maxROIBinning_y, m_stepROIBinning_y, "%f" );
    1969            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_bin_y, st_newCallBack_stdCamera ) < 0 )
    1970              :         {
    1971              : #ifndef STDCAMERA_TEST_NOLOG
    1972            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1973              : #endif
    1974            0 :             return -1;
    1975              :         }
    1976              : 
    1977           18 :         derived().createROIndiNumber( m_indiP_fullROI, "roi_full_region" );
    1978            6 :         m_indiP_fullROI.add( pcf::IndiElement( "x" ) );
    1979            6 :         m_indiP_fullROI["x"] = 0;
    1980            6 :         m_indiP_fullROI.add( pcf::IndiElement( "y" ) );
    1981            6 :         m_indiP_fullROI["y"] = 0;
    1982            6 :         m_indiP_fullROI.add( pcf::IndiElement( "w" ) );
    1983            6 :         m_indiP_fullROI["w"] = 0;
    1984            6 :         m_indiP_fullROI.add( pcf::IndiElement( "h" ) );
    1985            6 :         m_indiP_fullROI["h"] = 0;
    1986            3 :         if( derived().registerIndiPropertyReadOnly( m_indiP_fullROI ) < 0 )
    1987              :         {
    1988              : #ifndef STDCAMERA_TEST_NOLOG
    1989            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1990              : #endif
    1991            0 :             return -1;
    1992              :         }
    1993              : 
    1994           18 :         derived().createStandardIndiRequestSw( m_indiP_roi_check, "roi_region_check" );
    1995            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_check, st_newCallBack_stdCamera ) < 0 )
    1996              :         {
    1997              : #ifndef STDCAMERA_TEST_NOLOG
    1998            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    1999              : #endif
    2000            0 :             return -1;
    2001              :         }
    2002              : 
    2003           18 :         derived().createStandardIndiRequestSw( m_indiP_roi_set, "roi_set" );
    2004            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_set, st_newCallBack_stdCamera ) < 0 )
    2005              :         {
    2006              : #ifndef STDCAMERA_TEST_NOLOG
    2007            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2008              : #endif
    2009            0 :             return -1;
    2010              :         }
    2011              : 
    2012           18 :         derived().createStandardIndiRequestSw( m_indiP_roi_full, "roi_set_full" );
    2013            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_full, st_newCallBack_stdCamera ) < 0 )
    2014              :         {
    2015              : #ifndef STDCAMERA_TEST_NOLOG
    2016            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2017              : #endif
    2018            0 :             return -1;
    2019              :         }
    2020              : 
    2021           18 :         derived().createStandardIndiRequestSw( m_indiP_roi_fullbin, "roi_set_full_bin" );
    2022            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_fullbin, st_newCallBack_stdCamera ) < 0 )
    2023              :         {
    2024              : #ifndef STDCAMERA_TEST_NOLOG
    2025            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2026              : #endif
    2027            0 :             return -1;
    2028              :         }
    2029              : 
    2030           18 :         derived().createStandardIndiRequestSw( m_indiP_roi_loadlast, "roi_load_last" );
    2031            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_loadlast, st_newCallBack_stdCamera ) < 0 )
    2032              :         {
    2033              : #ifndef STDCAMERA_TEST_NOLOG
    2034            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2035              : #endif
    2036            0 :             return -1;
    2037              :         }
    2038              : 
    2039           18 :         derived().createStandardIndiRequestSw( m_indiP_roi_last, "roi_set_last" );
    2040            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_last, st_newCallBack_stdCamera ) < 0 )
    2041              :         {
    2042              : #ifndef STDCAMERA_TEST_NOLOG
    2043            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2044              : #endif
    2045            0 :             return -1;
    2046              :         }
    2047              : 
    2048           18 :         derived().createStandardIndiRequestSw( m_indiP_roi_default, "roi_set_default" );
    2049            3 :         if( derived().registerIndiPropertyNew( m_indiP_roi_default, st_newCallBack_stdCamera ) < 0 )
    2050              :         {
    2051              : #ifndef STDCAMERA_TEST_NOLOG
    2052            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2053              : #endif
    2054            0 :             return -1;
    2055              :         }
    2056              :     }
    2057              : 
    2058              :     if( derivedT::c_stdCamera_cropMode )
    2059              :     {
    2060              :         derived().createStandardIndiToggleSw( m_indiP_cropMode, "roi_crop_mode", "Crop Mode", "Crop Mode" );
    2061              :         if( derived().registerIndiPropertyNew( m_indiP_cropMode, st_newCallBack_stdCamera ) < 0 )
    2062              :         {
    2063              : #ifndef STDCAMERA_TEST_NOLOG
    2064              :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2065              : #endif
    2066              :             return -1;
    2067              :         }
    2068              :     }
    2069              : 
    2070              :     // Set up INDI for shutter
    2071              :     if( derivedT::c_stdCamera_hasShutter )
    2072              :     {
    2073           22 :         derived().createROIndiText(
    2074            2 :             m_indiP_shutterStatus, "shutter_status", "status", "Shutter Status", "Shutter", "Status" );
    2075            4 :         m_indiP_shutterStatus["status"] = m_shutterStatus;
    2076            2 :         if( derived().registerIndiPropertyReadOnly( m_indiP_shutterStatus ) < 0 )
    2077              :         {
    2078              : #ifndef STDCAMERA_TEST_NOLOG
    2079            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2080              : #endif
    2081            0 :             return -1;
    2082              :         }
    2083              : 
    2084           12 :         derived().createStandardIndiToggleSw( m_indiP_shutter, "shutter", "Shutter", "Shutter" );
    2085            2 :         if( derived().registerIndiPropertyNew( m_indiP_shutter, st_newCallBack_stdCamera ) < 0 )
    2086              :         {
    2087              : #ifndef STDCAMERA_TEST_NOLOG
    2088            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2089              : #endif
    2090            0 :             return -1;
    2091              :         }
    2092              :     }
    2093              : 
    2094              :     if( derivedT::c_stdCamera_usesStateString )
    2095              :     {
    2096           20 :         derived().createROIndiText( m_indiP_stateString, "state_string", "current", "State String", "State", "String" );
    2097            4 :         m_indiP_stateString.add( pcf::IndiElement( "valid" ) );
    2098            4 :         m_indiP_stateString["valid"] = "no";
    2099            2 :         if( derived().registerIndiPropertyReadOnly( m_indiP_stateString ) < 0 )
    2100              :         {
    2101              : #ifndef STDCAMERA_TEST_NOLOG
    2102            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2103              : #endif
    2104            0 :             return -1;
    2105              :         }
    2106              :     }
    2107              : 
    2108            5 :     return 0;
    2109              : } // int stdCamera<derivedT>::appStartup()
    2110              : 
    2111              : template <class derivedT>
    2112           43 : int stdCamera<derivedT>::appLogic()
    2113              : {
    2114              :     try
    2115              :     {
    2116              : 
    2117           43 :         if( derived().state() == stateCodes::POWERON )
    2118              :         {
    2119            3 :             if( derived().powerOnWaitElapsed() )
    2120              :             {
    2121            1 :                 derived().state( stateCodes::NOTCONNECTED );
    2122              : 
    2123            1 :                 m_currentROI.x     = m_default_x;
    2124            1 :                 m_currentROI.y     = m_default_y;
    2125            1 :                 m_currentROI.w     = m_default_w;
    2126            1 :                 m_currentROI.h     = m_default_h;
    2127            1 :                 m_currentROI.bin_x = m_default_bin_x;
    2128            1 :                 m_currentROI.bin_y = m_default_bin_y;
    2129              : 
    2130            1 :                 m_nextROI.x     = m_default_x;
    2131            1 :                 m_nextROI.y     = m_default_y;
    2132            1 :                 m_nextROI.w     = m_default_w;
    2133            1 :                 m_nextROI.h     = m_default_h;
    2134            1 :                 m_nextROI.bin_x = m_default_bin_x;
    2135            1 :                 m_nextROI.bin_y = m_default_bin_y;
    2136              : 
    2137              :                 // Set power-on defaults
    2138            1 :                 derived().powerOnDefaults();
    2139              : 
    2140              :                 if( derivedT::c_stdCamera_tempControl )
    2141              :                 {
    2142              :                     // then set startupTemp if configured
    2143            1 :                     if( m_startupTemp > -999 )
    2144            1 :                         m_ccdTempSetpt = m_startupTemp;
    2145            3 :                     derived().updateIfChanged( m_indiP_temp, "target", m_ccdTempSetpt, INDI_IDLE );
    2146              :                 }
    2147              : 
    2148              :                 if( derivedT::c_stdCamera_usesROI )
    2149              :                 {
    2150              :                     // m_currentROI should be set to default/startup values in derivedT::powerOnDefaults
    2151            0 :                     m_nextROI.x     = m_currentROI.x;
    2152            0 :                     m_nextROI.y     = m_currentROI.y;
    2153            0 :                     m_nextROI.w     = m_currentROI.w;
    2154            0 :                     m_nextROI.h     = m_currentROI.h;
    2155            0 :                     m_nextROI.bin_x = m_currentROI.bin_x;
    2156            0 :                     m_nextROI.bin_y = m_currentROI.bin_y;
    2157              : 
    2158            0 :                     derived().updateIfChanged( m_indiP_roi_x, "current", m_currentROI.x, INDI_IDLE );
    2159            0 :                     derived().updateIfChanged( m_indiP_roi_x, "target", m_nextROI.x, INDI_IDLE );
    2160              : 
    2161            0 :                     derived().updateIfChanged( m_indiP_roi_y, "current", m_currentROI.y, INDI_IDLE );
    2162            0 :                     derived().updateIfChanged( m_indiP_roi_y, "target", m_nextROI.y, INDI_IDLE );
    2163              : 
    2164            0 :                     derived().updateIfChanged( m_indiP_roi_w, "current", m_currentROI.w, INDI_IDLE );
    2165            0 :                     derived().updateIfChanged( m_indiP_roi_w, "target", m_nextROI.w, INDI_IDLE );
    2166              : 
    2167            0 :                     derived().updateIfChanged( m_indiP_roi_h, "current", m_currentROI.h, INDI_IDLE );
    2168            0 :                     derived().updateIfChanged( m_indiP_roi_h, "target", m_nextROI.h, INDI_IDLE );
    2169              : 
    2170            0 :                     derived().updateIfChanged( m_indiP_roi_bin_x, "current", m_currentROI.bin_x, INDI_IDLE );
    2171            0 :                     derived().updateIfChanged( m_indiP_roi_bin_x, "target", m_nextROI.bin_x, INDI_IDLE );
    2172              : 
    2173            0 :                     derived().updateIfChanged( m_indiP_roi_bin_y, "current", m_currentROI.bin_y, INDI_IDLE );
    2174            0 :                     derived().updateIfChanged( m_indiP_roi_bin_y, "target", m_nextROI.bin_y, INDI_IDLE );
    2175              :                 }
    2176              : 
    2177              :                 if( derivedT::c_stdCamera_hasShutter )
    2178              :                 {
    2179            1 :                     if( m_shutterStatus == "OPERATING" )
    2180              :                     {
    2181            0 :                         derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_BUSY );
    2182              :                     }
    2183            1 :                     if( m_shutterStatus == "POWERON" || m_shutterStatus == "READY" )
    2184              :                     {
    2185            0 :                         derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_OK );
    2186              :                     }
    2187              :                     else
    2188              :                     {
    2189            3 :                         derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_IDLE );
    2190              :                     }
    2191              : 
    2192            1 :                     if( m_shutterState == 1 )
    2193              :                     {
    2194            0 :                         derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::On, INDI_OK );
    2195              :                     }
    2196              :                     else
    2197              :                     {
    2198            3 :                         derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2199              :                     }
    2200              :                 }
    2201              : 
    2202            1 :                 return 0;
    2203              :             }
    2204              :             else
    2205              :             {
    2206            2 :                 return 0;
    2207              :             }
    2208              :         }
    2209           40 :         else if( derived().state() == stateCodes::READY || derived().state() == stateCodes::OPERATING )
    2210              :         {
    2211           12 :             if( c_hasFan && m_fanSpeedValid )
    2212              :             {
    2213            0 :                 indi::updateSelectionSwitchIfChanged(
    2214            0 :                     m_indiP_fanSpeed, m_fanSpeedName, derived().m_indiDriver, INDI_IDLE );
    2215              :             }
    2216              : 
    2217           12 :             if( c_hasAnalogGain && m_analogGainValid )
    2218              :             {
    2219            0 :                 indi::updateSelectionSwitchIfChanged(
    2220            0 :                     m_indiP_analogGain, m_analogGainName, derived().m_indiDriver, INDI_IDLE );
    2221              :             }
    2222              : 
    2223           12 :             if( c_hasLED && m_ledStateValid )
    2224              :             {
    2225            0 :                 if( m_ledState )
    2226              :                 {
    2227            0 :                     derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::On, INDI_IDLE );
    2228              :                 }
    2229              :                 else
    2230              :                 {
    2231            0 :                     derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2232              :                 }
    2233              :             }
    2234              : 
    2235              :             if( derivedT::c_stdCamera_usesROI )
    2236              :             {
    2237           24 :                 derived().updateIfChanged( m_indiP_roi_x, "current", m_currentROI.x, INDI_IDLE );
    2238           24 :                 derived().updateIfChanged( m_indiP_roi_x, "target", m_nextROI.x, INDI_IDLE );
    2239              : 
    2240           24 :                 derived().updateIfChanged( m_indiP_roi_y, "current", m_currentROI.y, INDI_IDLE );
    2241           24 :                 derived().updateIfChanged( m_indiP_roi_y, "target", m_nextROI.y, INDI_IDLE );
    2242              : 
    2243           24 :                 derived().updateIfChanged( m_indiP_roi_w, "current", m_currentROI.w, INDI_IDLE );
    2244           24 :                 derived().updateIfChanged( m_indiP_roi_w, "target", m_nextROI.w, INDI_IDLE );
    2245              : 
    2246           24 :                 derived().updateIfChanged( m_indiP_roi_h, "current", m_currentROI.h, INDI_IDLE );
    2247           24 :                 derived().updateIfChanged( m_indiP_roi_h, "target", m_nextROI.h, INDI_IDLE );
    2248              : 
    2249           24 :                 derived().updateIfChanged( m_indiP_roi_bin_x, "current", m_currentROI.bin_x, INDI_IDLE );
    2250           24 :                 derived().updateIfChanged( m_indiP_roi_bin_x, "target", m_nextROI.bin_x, INDI_IDLE );
    2251              : 
    2252           24 :                 derived().updateIfChanged( m_indiP_roi_bin_y, "current", m_currentROI.bin_y, INDI_IDLE );
    2253           36 :                 derived().updateIfChanged( m_indiP_roi_bin_y, "target", m_nextROI.bin_y, INDI_IDLE );
    2254              :             }
    2255              :         }
    2256              : 
    2257           40 :         return 0;
    2258              :     }
    2259            0 :     catch( const std::exception &e )
    2260              :     {
    2261            0 :         return derivedT::template log<software_error, -1>(
    2262            0 :             { __FILE__, __LINE__, std::string( "Exception caught: " ) + e.what() } );
    2263              :     }
    2264              : }
    2265              : 
    2266              : template <class derivedT>
    2267            2 : int stdCamera<derivedT>::onPowerOff()
    2268              : {
    2269            2 :     if( !derived().m_indiDriver )
    2270              :     {
    2271            2 :         return 0;
    2272              :     }
    2273              : 
    2274              :     if( derivedT::c_stdCamera_usesModes )
    2275              :     {
    2276            0 :         for( auto it = m_cameraModes.begin(); it != m_cameraModes.end(); ++it )
    2277              :         {
    2278            0 :             derived().updateSwitchIfChanged( m_indiP_mode, it->first, pcf::IndiElement::Off, INDI_IDLE );
    2279              :         }
    2280              :     }
    2281              : 
    2282              :     if( derivedT::c_stdCamera_usesROI )
    2283              :     {
    2284              :         // Blank these values
    2285            0 :         indi::updateIfChanged( m_indiP_roi_x, "current", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2286            0 :         indi::updateIfChanged( m_indiP_roi_x, "target", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2287              : 
    2288            0 :         indi::updateIfChanged( m_indiP_roi_y, "current", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2289            0 :         indi::updateIfChanged( m_indiP_roi_y, "target", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2290              : 
    2291            0 :         indi::updateIfChanged( m_indiP_roi_w, "current", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2292            0 :         indi::updateIfChanged( m_indiP_roi_w, "target", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2293              : 
    2294            0 :         indi::updateIfChanged( m_indiP_roi_h, "current", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2295            0 :         indi::updateIfChanged( m_indiP_roi_h, "target", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2296              : 
    2297            0 :         indi::updateIfChanged( m_indiP_roi_bin_x, "current", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2298            0 :         indi::updateIfChanged( m_indiP_roi_bin_x, "target", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2299              : 
    2300            0 :         indi::updateIfChanged( m_indiP_roi_bin_y, "current", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2301            0 :         indi::updateIfChanged( m_indiP_roi_bin_y, "target", std::string( "" ), derived().m_indiDriver, INDI_IDLE );
    2302              : 
    2303              :         // But we also set these to their defaults so that when we power up it's all good
    2304            0 :         m_currentROI.x     = m_default_x;
    2305            0 :         m_currentROI.y     = m_default_y;
    2306            0 :         m_currentROI.w     = m_default_w;
    2307            0 :         m_currentROI.h     = m_default_h;
    2308            0 :         m_currentROI.bin_x = m_default_bin_x;
    2309            0 :         m_currentROI.bin_y = m_default_bin_y;
    2310              : 
    2311            0 :         m_nextROI.x     = m_default_x;
    2312            0 :         m_nextROI.y     = m_default_y;
    2313            0 :         m_nextROI.w     = m_default_w;
    2314            0 :         m_nextROI.h     = m_default_h;
    2315            0 :         m_nextROI.bin_x = m_default_bin_x;
    2316            0 :         m_nextROI.bin_y = m_default_bin_y;
    2317              :     }
    2318              : 
    2319              :     // Shutters can be independent pieces of hardware . . .
    2320              :     if( derivedT::c_stdCamera_hasShutter )
    2321              :     {
    2322            0 :         if( m_shutterStatus == "OPERATING" )
    2323              :         {
    2324            0 :             derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_BUSY );
    2325              :         }
    2326            0 :         if( m_shutterStatus == "POWERON" || m_shutterStatus == "READY" )
    2327              :         {
    2328            0 :             derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_OK );
    2329              :         }
    2330              :         else
    2331              :         {
    2332            0 :             derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_IDLE );
    2333              :         }
    2334              : 
    2335            0 :         if( m_shutterState == 0 )
    2336              :         {
    2337            0 :             derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::On, INDI_OK );
    2338              :         }
    2339              :         else
    2340              :         {
    2341            0 :             derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2342              :         }
    2343              :     }
    2344              : 
    2345            0 :     if( c_hasFan && m_fanSpeedValid )
    2346              :     {
    2347            0 :         indi::updateSelectionSwitchIfChanged( m_indiP_fanSpeed, m_fanSpeedName, derived().m_indiDriver, INDI_IDLE );
    2348              :     }
    2349              : 
    2350            0 :     if( c_hasAnalogGain && m_analogGainValid )
    2351              :     {
    2352            0 :         indi::updateSelectionSwitchIfChanged( m_indiP_analogGain, m_analogGainName, derived().m_indiDriver, INDI_IDLE );
    2353              :     }
    2354              : 
    2355            0 :     if( c_hasLED && m_ledStateValid )
    2356              :     {
    2357            0 :         if( m_ledState )
    2358              :         {
    2359            0 :             derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::On, INDI_IDLE );
    2360              :         }
    2361              :         else
    2362              :         {
    2363            0 :             derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2364              :         }
    2365              :     }
    2366              : 
    2367            0 :     return 0;
    2368              : }
    2369              : 
    2370              : template <class derivedT>
    2371            2 : int stdCamera<derivedT>::whilePowerOff()
    2372              : {
    2373              :     // Shutters can be independent pieces of hardware . . .
    2374              :     if( derivedT::c_stdCamera_hasShutter )
    2375              :     {
    2376            1 :         if( m_shutterStatus == "OPERATING" )
    2377              :         {
    2378            0 :             derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_BUSY );
    2379              :         }
    2380            1 :         if( m_shutterStatus == "POWERON" || m_shutterStatus == "READY" )
    2381              :         {
    2382            0 :             derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_OK );
    2383              :         }
    2384              :         else
    2385              :         {
    2386            3 :             derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_IDLE );
    2387              :         }
    2388              : 
    2389            1 :         if( m_shutterState == 0 )
    2390              :         {
    2391            0 :             derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::On, INDI_OK );
    2392              :         }
    2393              :         else
    2394              :         {
    2395            3 :             derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2396              :         }
    2397              :     }
    2398              : 
    2399            1 :     if( c_hasFan && m_fanSpeedValid )
    2400              :     {
    2401            0 :         indi::updateSelectionSwitchIfChanged( m_indiP_fanSpeed, m_fanSpeedName, derived().m_indiDriver, INDI_IDLE );
    2402              :     }
    2403              : 
    2404            1 :     if( c_hasAnalogGain && m_analogGainValid )
    2405              :     {
    2406            0 :         indi::updateSelectionSwitchIfChanged( m_indiP_analogGain, m_analogGainName, derived().m_indiDriver, INDI_IDLE );
    2407              :     }
    2408              : 
    2409            1 :     if( c_hasLED && m_ledStateValid )
    2410              :     {
    2411            0 :         if( m_ledState )
    2412              :         {
    2413            0 :             derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::On, INDI_IDLE );
    2414              :         }
    2415              :         else
    2416              :         {
    2417            0 :             derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2418              :         }
    2419              :     }
    2420              : 
    2421            2 :     return 0;
    2422              : }
    2423              : 
    2424              : template <class derivedT>
    2425            4 : int stdCamera<derivedT>::appShutdown()
    2426              : {
    2427            4 :     return 0;
    2428              : }
    2429              : 
    2430              : template <class derivedT>
    2431            0 : int stdCamera<derivedT>::st_newCallBack_stdCamera( void *app, const pcf::IndiProperty &ipRecv )
    2432              : {
    2433            0 :     derivedT *_app = static_cast<derivedT *>( app );
    2434            0 :     return _app->newCallBack_stdCamera( ipRecv );
    2435              : }
    2436              : 
    2437              : template <class derivedT>
    2438            0 : int stdCamera<derivedT>::newCallBack_stdCamera( const pcf::IndiProperty &ipRecv )
    2439              : {
    2440              : 
    2441            0 :     if( ipRecv.getDevice() != derived().configName() )
    2442              :     {
    2443              : #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    2444            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__, "unknown INDI property" } );
    2445              : #endif
    2446              : 
    2447            0 :         return -1;
    2448              :     }
    2449              : 
    2450            0 :     std::string name = ipRecv.getName();
    2451              : 
    2452            0 :     if( name == "reconfigure" )
    2453            0 :         return newCallBack_reconfigure( ipRecv );
    2454            0 :     else if( derivedT::c_stdCamera_temp && name == "temp_ccd" )
    2455            0 :         return newCallBack_temp( ipRecv );
    2456            0 :     else if( derivedT::c_stdCamera_tempControl && name == "temp_ccd" )
    2457            0 :         return newCallBack_temp( ipRecv );
    2458            0 :     else if( derivedT::c_stdCamera_tempControl && name == "temp_controller" )
    2459            0 :         return newCallBack_temp_controller( ipRecv );
    2460              :     else if( derivedT::c_stdCamera_readoutSpeed && name == "readout_speed" )
    2461              :         return newCallBack_readoutSpeed( ipRecv );
    2462              :     else if( derivedT::c_stdCamera_vShiftSpeed && name == "vshift_speed" )
    2463              :         return newCallBack_vShiftSpeed( ipRecv );
    2464            0 :     else if( derivedT::c_stdCamera_emGain && name == "emgain" )
    2465            0 :         return newCallBack_emgain( ipRecv );
    2466              :     else if( derivedT::c_stdCamera_exptimeCtrl && name == "exptime" )
    2467              :         return newCallBack_exptime( ipRecv );
    2468            0 :     else if( derivedT::c_stdCamera_fpsCtrl && name == "fps" )
    2469            0 :         return newCallBack_fps( ipRecv );
    2470            0 :     else if( c_hasFan && name == "fan_speed" )
    2471            0 :         return newCallBack_fanSpeed( ipRecv );
    2472            0 :     else if( c_hasAnalogGain && name == "analog_gain" )
    2473            0 :         return newCallBack_analogGain( ipRecv );
    2474            0 :     else if( c_hasLED && name == "led" )
    2475            0 :         return newCallBack_led( ipRecv );
    2476            0 :     else if( derivedT::c_stdCamera_synchro && name == "synchro" )
    2477            0 :         return newCallBack_synchro( ipRecv );
    2478            0 :     else if( derivedT::c_stdCamera_usesModes && name == "mode" )
    2479            0 :         return newCallBack_mode( ipRecv );
    2480              :     else if( derivedT::c_stdCamera_cropMode && name == "roi_crop_mode" )
    2481              :         return newCallBack_cropMode( ipRecv );
    2482            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_region_x" )
    2483            0 :         return newCallBack_roi_x( ipRecv );
    2484            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_region_y" )
    2485            0 :         return newCallBack_roi_y( ipRecv );
    2486            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_region_w" )
    2487            0 :         return newCallBack_roi_w( ipRecv );
    2488            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_region_h" )
    2489            0 :         return newCallBack_roi_h( ipRecv );
    2490            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_region_bin_x" )
    2491            0 :         return newCallBack_roi_bin_x( ipRecv );
    2492            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_region_bin_y" )
    2493            0 :         return newCallBack_roi_bin_y( ipRecv );
    2494            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_region_check" )
    2495            0 :         return newCallBack_roi_check( ipRecv );
    2496            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_set" )
    2497            0 :         return newCallBack_roi_set( ipRecv );
    2498            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_set_full" )
    2499            0 :         return newCallBack_roi_full( ipRecv );
    2500            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_set_full_bin" )
    2501            0 :         return newCallBack_roi_fullbin( ipRecv );
    2502            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_load_last" )
    2503            0 :         return newCallBack_roi_loadlast( ipRecv );
    2504            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_set_last" )
    2505            0 :         return newCallBack_roi_last( ipRecv );
    2506            0 :     else if( derivedT::c_stdCamera_usesROI && name == "roi_set_default" )
    2507            0 :         return newCallBack_roi_default( ipRecv );
    2508            0 :     else if( derivedT::c_stdCamera_hasShutter && name == "shutter" )
    2509            0 :         return newCallBack_shutter( ipRecv );
    2510              : 
    2511              : #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    2512            0 :     derivedT::template log<software_error>( { __FILE__, __LINE__, "unknown INDI property" } );
    2513              : #endif
    2514              : 
    2515            0 :     return -1;
    2516            0 : }
    2517              : 
    2518              : template <class derivedT>
    2519            0 : int stdCamera<derivedT>::setTempSetPt( const mx::meta::trueFalseT<true> &t )
    2520              : {
    2521              :     static_cast<void>( t );
    2522            0 :     return derived().setTempSetPt();
    2523              : }
    2524              : 
    2525              : template <class derivedT>
    2526              : int stdCamera<derivedT>::setTempSetPt( const mx::meta::trueFalseT<false> &f )
    2527              : {
    2528              :     static_cast<void>( f );
    2529              :     return 0;
    2530              : }
    2531              : 
    2532              : template <class derivedT>
    2533            0 : int stdCamera<derivedT>::newCallBack_temp( const pcf::IndiProperty &ipRecv )
    2534              : {
    2535              :     if( derivedT::c_stdCamera_tempControl )
    2536              :     {
    2537              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2538              :         return 0;
    2539              : #endif
    2540              : 
    2541              :         float target;
    2542              : 
    2543            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2544              : 
    2545            0 :         if( derived().indiTargetUpdate( m_indiP_temp, target, ipRecv, true ) < 0 )
    2546              :         {
    2547            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2548            0 :             return -1;
    2549              :         }
    2550              : 
    2551            0 :         m_ccdTempSetpt = target;
    2552              : 
    2553              :         mx::meta::trueFalseT<derivedT::c_stdCamera_tempControl> tf;
    2554            0 :         return setTempSetPt( tf );
    2555            0 :     }
    2556              :     else
    2557              :     {
    2558              :         return 0;
    2559              :     }
    2560              : }
    2561              : 
    2562              : template <class derivedT>
    2563            0 : int stdCamera<derivedT>::setTempControl( const mx::meta::trueFalseT<true> &t )
    2564              : {
    2565              :     static_cast<void>( t );
    2566            0 :     return derived().setTempControl();
    2567              : }
    2568              : 
    2569              : template <class derivedT>
    2570              : int stdCamera<derivedT>::setTempControl( const mx::meta::trueFalseT<false> &f )
    2571              : {
    2572              :     static_cast<void>( f );
    2573              :     return 0;
    2574              : }
    2575              : 
    2576              : template <class derivedT>
    2577            0 : int stdCamera<derivedT>::newCallBack_temp_controller( const pcf::IndiProperty &ipRecv )
    2578              : {
    2579              :     if( derivedT::c_stdCamera_tempControl )
    2580              :     {
    2581              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2582              :         return 0;
    2583              : #endif
    2584              : 
    2585            0 :         if( !ipRecv.find( "toggle" ) )
    2586            0 :             return 0;
    2587              : 
    2588            0 :         m_tempControlStatusSet = false;
    2589              : 
    2590            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2591              : 
    2592            0 :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    2593              :         {
    2594            0 :             m_tempControlStatusSet = true;
    2595            0 :             derived().updateSwitchIfChanged( m_indiP_tempcont, "toggle", pcf::IndiElement::On, INDI_BUSY );
    2596              :         }
    2597            0 :         else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    2598              :         {
    2599            0 :             m_tempControlStatusSet = false;
    2600            0 :             derived().updateSwitchIfChanged( m_indiP_tempcont, "toggle", pcf::IndiElement::Off, INDI_BUSY );
    2601              :         }
    2602              : 
    2603              :         mx::meta::trueFalseT<derivedT::c_stdCamera_tempControl> tf;
    2604            0 :         return setTempControl( tf );
    2605            0 :     }
    2606              :     else
    2607              :     {
    2608              :         return 0;
    2609              :     }
    2610              : }
    2611              : 
    2612              : template <class derivedT>
    2613              : int stdCamera<derivedT>::setReadoutSpeed( const mx::meta::trueFalseT<true> &t )
    2614              : {
    2615              :     static_cast<void>( t );
    2616              :     return derived().setReadoutSpeed();
    2617              : }
    2618              : 
    2619              : template <class derivedT>
    2620              : int stdCamera<derivedT>::setReadoutSpeed( const mx::meta::trueFalseT<false> &f )
    2621              : {
    2622              :     static_cast<void>( f );
    2623              :     return 0;
    2624              : }
    2625              : 
    2626              : template <class derivedT>
    2627              : int stdCamera<derivedT>::newCallBack_readoutSpeed( const pcf::IndiProperty &ipRecv )
    2628              : {
    2629              :     if( derivedT::c_stdCamera_readoutSpeed )
    2630              :     {
    2631              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2632              :         return 0;
    2633              : #endif
    2634              : 
    2635              :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2636              : 
    2637              :         std::string newspeed;
    2638              : 
    2639              :         for( size_t i = 0; i < m_readoutSpeedNames.size(); ++i )
    2640              :         {
    2641              :             if( !ipRecv.find( m_readoutSpeedNames[i] ) )
    2642              :                 continue;
    2643              : 
    2644              :             if( ipRecv[m_readoutSpeedNames[i]].getSwitchState() == pcf::IndiElement::On )
    2645              :             {
    2646              :                 if( newspeed != "" )
    2647              :                 {
    2648              :                     derivedT::template log<text_log>( "More than one readout speed selected", logPrio::LOG_ERROR );
    2649              :                     return -1;
    2650              :                 }
    2651              : 
    2652              :                 newspeed = m_readoutSpeedNames[i];
    2653              :             }
    2654              :         }
    2655              : 
    2656              :         if( newspeed == "" )
    2657              :         {
    2658              :             // We do a reset
    2659              :             m_readoutSpeedNameSet = m_readoutSpeedName;
    2660              :         }
    2661              :         else
    2662              :         {
    2663              :             m_readoutSpeedNameSet = newspeed;
    2664              :         }
    2665              : 
    2666              :         mx::meta::trueFalseT<derivedT::c_stdCamera_readoutSpeed> tf;
    2667              :         return setReadoutSpeed( tf );
    2668              :     }
    2669              : 
    2670              :     return 0;
    2671              : }
    2672              : 
    2673              : template <class derivedT>
    2674              : int stdCamera<derivedT>::setVShiftSpeed( const mx::meta::trueFalseT<true> &t )
    2675              : {
    2676              :     static_cast<void>( t );
    2677              :     return derived().setVShiftSpeed();
    2678              : }
    2679              : 
    2680              : template <class derivedT>
    2681              : int stdCamera<derivedT>::setVShiftSpeed( const mx::meta::trueFalseT<false> &f )
    2682              : {
    2683              :     static_cast<void>( f );
    2684              :     return 0;
    2685              : }
    2686              : 
    2687              : template <class derivedT>
    2688              : int stdCamera<derivedT>::newCallBack_vShiftSpeed( const pcf::IndiProperty &ipRecv )
    2689              : {
    2690              :     if( derivedT::c_stdCamera_vShiftSpeed )
    2691              :     {
    2692              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2693              :         return 0;
    2694              : #endif
    2695              : 
    2696              :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2697              : 
    2698              :         std::string newspeed;
    2699              : 
    2700              :         for( size_t i = 0; i < m_vShiftSpeedNames.size(); ++i )
    2701              :         {
    2702              :             if( !ipRecv.find( m_vShiftSpeedNames[i] ) )
    2703              :                 continue;
    2704              : 
    2705              :             if( ipRecv[m_vShiftSpeedNames[i]].getSwitchState() == pcf::IndiElement::On )
    2706              :             {
    2707              :                 if( newspeed != "" )
    2708              :                 {
    2709              :                     derivedT::template log<text_log>( "More than one vShift speed selected", logPrio::LOG_ERROR );
    2710              :                     return -1;
    2711              :                 }
    2712              : 
    2713              :                 newspeed = m_vShiftSpeedNames[i];
    2714              :             }
    2715              :         }
    2716              : 
    2717              :         if( newspeed == "" )
    2718              :         {
    2719              :             // We do a reset
    2720              :             m_vShiftSpeedNameSet = m_vShiftSpeedName;
    2721              :         }
    2722              :         else
    2723              :         {
    2724              :             m_vShiftSpeedNameSet = newspeed;
    2725              :         }
    2726              : 
    2727              :         mx::meta::trueFalseT<derivedT::c_stdCamera_vShiftSpeed> tf;
    2728              :         return setVShiftSpeed( tf );
    2729              :     }
    2730              : 
    2731              :     return 0;
    2732              : }
    2733              : 
    2734              : template <class derivedT>
    2735            0 : int stdCamera<derivedT>::setEMGain( const mx::meta::trueFalseT<true> &t )
    2736              : {
    2737              :     static_cast<void>( t );
    2738            0 :     return derived().setEMGain();
    2739              : }
    2740              : 
    2741              : template <class derivedT>
    2742              : int stdCamera<derivedT>::setEMGain( const mx::meta::trueFalseT<false> &f )
    2743              : {
    2744              :     static_cast<void>( f );
    2745              :     return 0;
    2746              : }
    2747              : 
    2748              : template <class derivedT>
    2749            0 : int stdCamera<derivedT>::newCallBack_emgain( const pcf::IndiProperty &ipRecv )
    2750              : {
    2751              :     if( derivedT::c_stdCamera_emGain )
    2752              :     {
    2753              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2754              :         return 0;
    2755              : #endif
    2756              : 
    2757              :         float target;
    2758              : 
    2759            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2760              : 
    2761            0 :         if( derived().indiTargetUpdate( m_indiP_emGain, target, ipRecv, true ) < 0 )
    2762              :         {
    2763            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2764            0 :             return -1;
    2765              :         }
    2766              : 
    2767            0 :         m_emGainSet = target;
    2768              : 
    2769              :         mx::meta::trueFalseT<derivedT::c_stdCamera_emGain> tf;
    2770            0 :         return setEMGain( tf );
    2771            0 :     }
    2772              : 
    2773              :     return 0;
    2774              : }
    2775              : 
    2776              : template <class derivedT>
    2777              : int stdCamera<derivedT>::setExpTime( const mx::meta::trueFalseT<true> &t )
    2778              : {
    2779              :     static_cast<void>( t );
    2780              :     return derived().setExpTime();
    2781              : }
    2782              : 
    2783              : template <class derivedT>
    2784              : int stdCamera<derivedT>::setExpTime( const mx::meta::trueFalseT<false> &f )
    2785              : {
    2786              :     static_cast<void>( f );
    2787              :     return 0;
    2788              : }
    2789              : 
    2790              : template <class derivedT>
    2791              : int stdCamera<derivedT>::newCallBack_exptime( const pcf::IndiProperty &ipRecv )
    2792              : {
    2793              :     if( derivedT::c_stdCamera_exptimeCtrl )
    2794              :     {
    2795              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2796              :         return 0;
    2797              : #endif
    2798              : 
    2799              :         float target;
    2800              : 
    2801              :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2802              : 
    2803              :         if( derived().indiTargetUpdate( m_indiP_exptime, target, ipRecv, true ) < 0 )
    2804              :         {
    2805              :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2806              :             return -1;
    2807              :         }
    2808              : 
    2809              :         m_expTimeSet = target;
    2810              : 
    2811              :         mx::meta::trueFalseT<derivedT::c_stdCamera_exptimeCtrl> tf;
    2812              :         return setExpTime( tf );
    2813              :     }
    2814              : 
    2815              :     return 0;
    2816              : }
    2817              : 
    2818              : template <class derivedT>
    2819            0 : int stdCamera<derivedT>::setFPS( const mx::meta::trueFalseT<true> &t )
    2820              : {
    2821              :     static_cast<void>( t );
    2822            0 :     return derived().setFPS();
    2823              : }
    2824              : 
    2825              : template <class derivedT>
    2826              : int stdCamera<derivedT>::setFPS( const mx::meta::trueFalseT<false> &f )
    2827              : {
    2828              :     static_cast<void>( f );
    2829              :     return 0;
    2830              : }
    2831              : 
    2832              : template <class derivedT>
    2833            0 : int stdCamera<derivedT>::newCallBack_fps( const pcf::IndiProperty &ipRecv )
    2834              : {
    2835              :     if( derivedT::c_stdCamera_fpsCtrl )
    2836              :     {
    2837              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2838              :         return 0;
    2839              : #endif
    2840              : 
    2841              :         float target;
    2842              : 
    2843            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2844              : 
    2845            0 :         if( derived().indiTargetUpdate( m_indiP_fps, target, ipRecv, true ) < 0 )
    2846              :         {
    2847            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    2848            0 :             return -1;
    2849              :         }
    2850              : 
    2851            0 :         m_fpsSet = target;
    2852              : 
    2853              :         mx::meta::trueFalseT<derivedT::c_stdCamera_fpsCtrl> tf;
    2854            0 :         return setFPS( tf );
    2855            0 :     }
    2856              : 
    2857              :     return 0;
    2858              : }
    2859              : 
    2860              : template <class derivedT>
    2861            0 : int stdCamera<derivedT>::setFanSpeed( const mx::meta::trueFalseT<true> &t )
    2862              : {
    2863              :     static_cast<void>( t );
    2864            0 :     return derived().setFanSpeed();
    2865              : }
    2866              : 
    2867              : template <class derivedT>
    2868              : int stdCamera<derivedT>::setFanSpeed( const mx::meta::trueFalseT<false> &f )
    2869              : {
    2870              :     static_cast<void>( f );
    2871              :     return 0;
    2872              : }
    2873              : 
    2874              : template <class derivedT>
    2875            0 : int stdCamera<derivedT>::newCallBack_fanSpeed( const pcf::IndiProperty &ipRecv )
    2876              : {
    2877              :     if( c_hasFan )
    2878              :     {
    2879              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2880              :         return 0;
    2881              : #endif
    2882              : 
    2883            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2884              : 
    2885            0 :         std::string newFanSpeed;
    2886              : 
    2887            0 :         for( size_t i = 0; i < m_fanSpeedNames.size(); ++i )
    2888              :         {
    2889            0 :             if( !ipRecv.find( m_fanSpeedNames[i] ) )
    2890              :             {
    2891            0 :                 continue;
    2892              :             }
    2893              : 
    2894            0 :             if( ipRecv[m_fanSpeedNames[i]].getSwitchState() == pcf::IndiElement::On )
    2895              :             {
    2896            0 :                 if( newFanSpeed != "" )
    2897              :                 {
    2898            0 :                     derivedT::template log<text_log>( "More than one fan speed selected", logPrio::LOG_ERROR );
    2899            0 :                     return -1;
    2900              :                 }
    2901              : 
    2902            0 :                 newFanSpeed = m_fanSpeedNames[i];
    2903              :             }
    2904              :         }
    2905              : 
    2906            0 :         if( newFanSpeed == "" )
    2907              :         {
    2908            0 :             m_fanSpeedNameSet = m_fanSpeedName;
    2909              :         }
    2910              :         else
    2911              :         {
    2912            0 :             m_fanSpeedNameSet = newFanSpeed;
    2913              :         }
    2914              : 
    2915              :         mx::meta::trueFalseT<c_hasFan> tf;
    2916            0 :         return setFanSpeed( tf );
    2917            0 :     }
    2918              : 
    2919              :     return 0;
    2920              : }
    2921              : 
    2922              : template <class derivedT>
    2923            0 : int stdCamera<derivedT>::setAnalogGain( const mx::meta::trueFalseT<true> &t )
    2924              : {
    2925              :     static_cast<void>( t );
    2926            0 :     return derived().setAnalogGain();
    2927              : }
    2928              : 
    2929              : template <class derivedT>
    2930              : int stdCamera<derivedT>::setAnalogGain( const mx::meta::trueFalseT<false> &f )
    2931              : {
    2932              :     static_cast<void>( f );
    2933              :     return 0;
    2934              : }
    2935              : 
    2936              : template <class derivedT>
    2937            0 : int stdCamera<derivedT>::newCallBack_analogGain( const pcf::IndiProperty &ipRecv )
    2938              : {
    2939              :     if( c_hasAnalogGain )
    2940              :     {
    2941              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    2942              :         return 0;
    2943              : #endif
    2944              : 
    2945            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    2946              : 
    2947            0 :         std::string newAnalogGain;
    2948              : 
    2949            0 :         for( size_t i = 0; i < m_analogGainNames.size(); ++i )
    2950              :         {
    2951            0 :             if( !ipRecv.find( m_analogGainNames[i] ) )
    2952              :             {
    2953            0 :                 continue;
    2954              :             }
    2955              : 
    2956            0 :             if( ipRecv[m_analogGainNames[i]].getSwitchState() == pcf::IndiElement::On )
    2957              :             {
    2958            0 :                 if( newAnalogGain != "" )
    2959              :                 {
    2960            0 :                     derivedT::template log<text_log>( "More than one analog gain selected", logPrio::LOG_ERROR );
    2961            0 :                     return -1;
    2962              :                 }
    2963              : 
    2964            0 :                 newAnalogGain = m_analogGainNames[i];
    2965              :             }
    2966              :         }
    2967              : 
    2968            0 :         if( newAnalogGain == "" )
    2969              :         {
    2970            0 :             m_analogGainNameSet = m_analogGainName;
    2971              :         }
    2972              :         else
    2973              :         {
    2974            0 :             m_analogGainNameSet = newAnalogGain;
    2975              :         }
    2976              : 
    2977              :         mx::meta::trueFalseT<c_hasAnalogGain> tf;
    2978            0 :         return setAnalogGain( tf );
    2979            0 :     }
    2980              : 
    2981              :     return 0;
    2982              : }
    2983              : 
    2984              : template <class derivedT>
    2985            0 : int stdCamera<derivedT>::setLED( const mx::meta::trueFalseT<true> &t )
    2986              : {
    2987              :     static_cast<void>( t );
    2988            0 :     return derived().setLED();
    2989              : }
    2990              : 
    2991              : template <class derivedT>
    2992              : int stdCamera<derivedT>::setLED( const mx::meta::trueFalseT<false> &f )
    2993              : {
    2994              :     static_cast<void>( f );
    2995              :     return 0;
    2996              : }
    2997              : 
    2998              : template <class derivedT>
    2999            0 : int stdCamera<derivedT>::newCallBack_led( const pcf::IndiProperty &ipRecv )
    3000              : {
    3001              :     if( c_hasLED )
    3002              :     {
    3003              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3004              :         return 0;
    3005              : #endif
    3006              : 
    3007            0 :         if( !ipRecv.find( "toggle" ) )
    3008              :         {
    3009            0 :             return 0;
    3010              :         }
    3011              : 
    3012            0 :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    3013              :         {
    3014            0 :             m_ledStateSet = false;
    3015              :         }
    3016              : 
    3017            0 :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    3018              :         {
    3019            0 :             m_ledStateSet = true;
    3020              :         }
    3021              : 
    3022            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3023              : 
    3024              :         mx::meta::trueFalseT<c_hasLED> tf;
    3025            0 :         return setLED( tf );
    3026            0 :     }
    3027              : 
    3028              :     return 0;
    3029              : }
    3030              : 
    3031              : template <class derivedT>
    3032            0 : int stdCamera<derivedT>::setSynchro( const mx::meta::trueFalseT<true> &t )
    3033              : {
    3034              :     static_cast<void>( t );
    3035            0 :     return derived().setSynchro();
    3036              : }
    3037              : 
    3038              : template <class derivedT>
    3039              : int stdCamera<derivedT>::setSynchro( const mx::meta::trueFalseT<false> &f )
    3040              : {
    3041              :     static_cast<void>( f );
    3042              :     return 0;
    3043              : }
    3044              : 
    3045              : template <class derivedT>
    3046            0 : int stdCamera<derivedT>::newCallBack_synchro( const pcf::IndiProperty &ipRecv )
    3047              : {
    3048              :     if( derivedT::c_stdCamera_synchro )
    3049              :     {
    3050              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3051              :         return 0;
    3052              : #endif
    3053              : 
    3054            0 :         if( !ipRecv.find( "toggle" ) )
    3055            0 :             return 0;
    3056              : 
    3057            0 :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    3058              :         {
    3059            0 :             m_synchroSet = false;
    3060              :         }
    3061              : 
    3062            0 :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    3063              :         {
    3064            0 :             m_synchroSet = true;
    3065              :         }
    3066              : 
    3067            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3068              : 
    3069              :         mx::meta::trueFalseT<derivedT::c_stdCamera_synchro> tf;
    3070            0 :         return setSynchro( tf );
    3071            0 :     }
    3072              : 
    3073              :     return 0;
    3074              : }
    3075              : 
    3076              : template <class derivedT>
    3077            0 : int stdCamera<derivedT>::newCallBack_mode( const pcf::IndiProperty &ipRecv )
    3078              : {
    3079              :     if( derivedT::c_stdCamera_usesModes )
    3080              :     {
    3081              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3082              :         return 0;
    3083              : #endif
    3084              : 
    3085            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3086              : 
    3087            0 :         if( ipRecv.getName() != m_indiP_mode.getName() )
    3088              :         {
    3089            0 :             derivedT::template log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    3090            0 :             return -1;
    3091              :         }
    3092              : 
    3093              :         // look for selected mode switch which matches a known mode.  Make sure only one is selected.
    3094            0 :         std::string newName = "";
    3095            0 :         for( auto it = m_cameraModes.begin(); it != m_cameraModes.end(); ++it )
    3096              :         {
    3097            0 :             if( !ipRecv.find( it->first ) )
    3098            0 :                 continue;
    3099              : 
    3100            0 :             if( ipRecv[it->first].getSwitchState() == pcf::IndiElement::On )
    3101              :             {
    3102            0 :                 if( newName != "" )
    3103              :                 {
    3104            0 :                     derivedT::template log<text_log>( "More than one camera mode selected", logPrio::LOG_ERROR );
    3105            0 :                     return -1;
    3106              :                 }
    3107              : 
    3108            0 :                 newName = it->first;
    3109              :             }
    3110              :         }
    3111              : 
    3112            0 :         if( newName == "" )
    3113              :         {
    3114            0 :             return 0;
    3115              :         }
    3116              : 
    3117              :         // Now signal the f.g. thread to reconfigure
    3118            0 :         m_nextMode           = newName;
    3119            0 :         derived().m_reconfig = true;
    3120              : 
    3121            0 :         return 0;
    3122            0 :     }
    3123              :     else
    3124              :     {
    3125              :         return 0;
    3126              :     }
    3127              : }
    3128              : 
    3129              : template <class derivedT>
    3130            0 : int stdCamera<derivedT>::newCallBack_reconfigure( const pcf::IndiProperty &ipRecv )
    3131              : {
    3132              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3133              :     return 0;
    3134              : #endif
    3135              : 
    3136            0 :     if( !ipRecv.find( "request" ) )
    3137            0 :         return 0;
    3138              : 
    3139            0 :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3140              :     {
    3141            0 :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3142              : 
    3143            0 :         indi::updateSwitchIfChanged(
    3144            0 :             m_indiP_reconfig, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3145              : 
    3146            0 :         m_nextMode           = m_modeName;
    3147            0 :         derived().m_reconfig = true;
    3148            0 :         return 0;
    3149            0 :     }
    3150              : 
    3151            0 :     return 0;
    3152              : }
    3153              : 
    3154              : template <class derivedT>
    3155              : int stdCamera<derivedT>::setCropMode( const mx::meta::trueFalseT<true> &t )
    3156              : {
    3157              :     static_cast<void>( t );
    3158              :     return derived().setCropMode();
    3159              : }
    3160              : 
    3161              : template <class derivedT>
    3162              : int stdCamera<derivedT>::setCropMode( const mx::meta::trueFalseT<false> &f )
    3163              : {
    3164              :     static_cast<void>( f );
    3165              :     return 0;
    3166              : }
    3167              : 
    3168              : template <class derivedT>
    3169              : int stdCamera<derivedT>::newCallBack_cropMode( const pcf::IndiProperty &ipRecv )
    3170              : {
    3171              :     if( derivedT::c_stdCamera_cropMode )
    3172              :     {
    3173              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3174              :         return 0;
    3175              : #endif
    3176              : 
    3177              :         if( !ipRecv.find( "toggle" ) )
    3178              :             return 0;
    3179              : 
    3180              :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    3181              :         {
    3182              :             m_cropModeSet = false;
    3183              :         }
    3184              : 
    3185              :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    3186              :         {
    3187              :             m_cropModeSet = true;
    3188              :         }
    3189              : 
    3190              :         std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3191              : 
    3192              :         mx::meta::trueFalseT<derivedT::c_stdCamera_cropMode> tf;
    3193              :         return setCropMode( tf );
    3194              :     }
    3195              : 
    3196              :     return 0;
    3197              : }
    3198              : 
    3199              : ///\todo why don't these check if usesROI is true?
    3200              : template <class derivedT>
    3201            0 : int stdCamera<derivedT>::newCallBack_roi_x( const pcf::IndiProperty &ipRecv )
    3202              : {
    3203              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3204              :     return 0;
    3205              : #endif
    3206              : 
    3207              :     float target;
    3208              : 
    3209            0 :     std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3210              : 
    3211            0 :     if( derived().indiTargetUpdate( m_indiP_roi_x, target, ipRecv, false ) < 0 )
    3212              :     {
    3213            0 :         m_nextROI.x = m_currentROI.x;
    3214            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    3215            0 :         return -1;
    3216              :     }
    3217              : 
    3218            0 :     m_nextROI.x = target;
    3219              : 
    3220            0 :     return 0;
    3221            0 : }
    3222              : 
    3223              : template <class derivedT>
    3224            0 : int stdCamera<derivedT>::newCallBack_roi_y( const pcf::IndiProperty &ipRecv )
    3225              : {
    3226              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3227              :     return 0;
    3228              : #endif
    3229              : 
    3230              :     float target;
    3231              : 
    3232            0 :     std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3233              : 
    3234            0 :     if( derived().indiTargetUpdate( m_indiP_roi_y, target, ipRecv, false ) < 0 )
    3235              :     {
    3236            0 :         m_nextROI.y = m_currentROI.y;
    3237            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    3238            0 :         return -1;
    3239              :     }
    3240              : 
    3241            0 :     m_nextROI.y = target;
    3242              : 
    3243            0 :     return 0;
    3244            0 : }
    3245              : 
    3246              : template <class derivedT>
    3247            0 : int stdCamera<derivedT>::newCallBack_roi_w( const pcf::IndiProperty &ipRecv )
    3248              : {
    3249              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3250              :     return 0;
    3251              : #endif
    3252              : 
    3253              :     int target;
    3254              : 
    3255            0 :     std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3256              : 
    3257            0 :     if( derived().indiTargetUpdate( m_indiP_roi_w, target, ipRecv, false ) < 0 )
    3258              :     {
    3259            0 :         m_nextROI.w = m_currentROI.w;
    3260            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    3261            0 :         return -1;
    3262              :     }
    3263              : 
    3264            0 :     m_nextROI.w = target;
    3265              : 
    3266            0 :     return 0;
    3267            0 : }
    3268              : 
    3269              : template <class derivedT>
    3270            0 : int stdCamera<derivedT>::newCallBack_roi_h( const pcf::IndiProperty &ipRecv )
    3271              : {
    3272              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3273              :     return 0;
    3274              : #endif
    3275              : 
    3276              :     int target;
    3277              : 
    3278            0 :     std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3279              : 
    3280            0 :     if( derived().indiTargetUpdate( m_indiP_roi_h, target, ipRecv, false ) < 0 )
    3281              :     {
    3282            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    3283            0 :         m_nextROI.h = m_currentROI.h;
    3284            0 :         return -1;
    3285              :     }
    3286              : 
    3287            0 :     m_nextROI.h = target;
    3288              : 
    3289            0 :     return 0;
    3290            0 : }
    3291              : 
    3292              : template <class derivedT>
    3293            0 : int stdCamera<derivedT>::newCallBack_roi_bin_x( const pcf::IndiProperty &ipRecv )
    3294              : {
    3295              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3296              :     return 0;
    3297              : #endif
    3298              : 
    3299              :     int target;
    3300              : 
    3301            0 :     std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3302              : 
    3303            0 :     if( derived().indiTargetUpdate( m_indiP_roi_bin_x, target, ipRecv, false ) < 0 )
    3304              :     {
    3305            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    3306            0 :         m_nextROI.bin_x = m_currentROI.bin_x;
    3307            0 :         return -1;
    3308              :     }
    3309              : 
    3310            0 :     m_nextROI.bin_x = target;
    3311              : 
    3312            0 :     return 0;
    3313            0 : }
    3314              : 
    3315              : template <class derivedT>
    3316            0 : int stdCamera<derivedT>::newCallBack_roi_bin_y( const pcf::IndiProperty &ipRecv )
    3317              : {
    3318              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3319              :     return 0;
    3320              : #endif
    3321              : 
    3322              :     int target;
    3323              : 
    3324            0 :     std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3325              : 
    3326            0 :     if( derived().indiTargetUpdate( m_indiP_roi_bin_y, target, ipRecv, false ) < 0 )
    3327              :     {
    3328            0 :         derivedT::template log<software_error>( { __FILE__, __LINE__ } );
    3329            0 :         m_nextROI.bin_y = m_currentROI.bin_y;
    3330            0 :         return -1;
    3331              :     }
    3332              : 
    3333            0 :     m_nextROI.bin_y = target;
    3334              : 
    3335            0 :     return 0;
    3336            0 : }
    3337              : 
    3338              : template <class derivedT>
    3339            0 : int stdCamera<derivedT>::checkNextROI( const mx::meta::trueFalseT<true> &t )
    3340              : {
    3341              :     static_cast<void>( t );
    3342            0 :     return derived().checkNextROI();
    3343              : }
    3344              : 
    3345              : template <class derivedT>
    3346              : int stdCamera<derivedT>::checkNextROI( const mx::meta::trueFalseT<false> &f )
    3347              : {
    3348              :     static_cast<void>( f );
    3349              :     return 0;
    3350              : }
    3351              : 
    3352              : template <class derivedT>
    3353            0 : int stdCamera<derivedT>::newCallBack_roi_check( const pcf::IndiProperty &ipRecv )
    3354              : {
    3355              :     if( derivedT::c_stdCamera_usesROI )
    3356              :     {
    3357              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3358              :         return 0;
    3359              : #endif
    3360              : 
    3361            0 :         if( !ipRecv.find( "request" ) )
    3362            0 :             return 0;
    3363              : 
    3364            0 :         if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3365              :         {
    3366            0 :             std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3367              : 
    3368            0 :             indi::updateSwitchIfChanged(
    3369            0 :                 m_indiP_roi_check, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3370              : 
    3371              :             mx::meta::trueFalseT<derivedT::c_stdCamera_usesROI> tf;
    3372            0 :             return checkNextROI( tf );
    3373            0 :         }
    3374              : 
    3375            0 :         return 0;
    3376              :     }
    3377              : 
    3378              :     return 0;
    3379              : }
    3380              : 
    3381              : template <class derivedT>
    3382            0 : int stdCamera<derivedT>::setNextROI( const mx::meta::trueFalseT<true> &t )
    3383              : {
    3384              :     static_cast<void>( t );
    3385            0 :     return derived().setNextROI();
    3386              : }
    3387              : 
    3388              : template <class derivedT>
    3389              : int stdCamera<derivedT>::setNextROI( const mx::meta::trueFalseT<false> &f )
    3390              : {
    3391              :     static_cast<void>( f );
    3392              :     return 0;
    3393              : }
    3394              : 
    3395              : template <class derivedT>
    3396            0 : int stdCamera<derivedT>::newCallBack_roi_set( const pcf::IndiProperty &ipRecv )
    3397              : {
    3398              :     if( derivedT::c_stdCamera_usesROI )
    3399              :     {
    3400              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3401              :         return 0;
    3402              : #endif
    3403              : 
    3404            0 :         if( !ipRecv.find( "request" ) )
    3405            0 :             return 0;
    3406              : 
    3407            0 :         if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3408              :         {
    3409            0 :             std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3410              : 
    3411            0 :             indi::updateSwitchIfChanged(
    3412            0 :                 m_indiP_roi_set, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3413              : 
    3414            0 :             m_lastROI = m_currentROI;
    3415              : 
    3416              :             mx::meta::trueFalseT<derivedT::c_stdCamera_usesROI> tf;
    3417            0 :             return setNextROI( tf );
    3418            0 :         }
    3419              : 
    3420            0 :         return 0;
    3421              :     }
    3422              : 
    3423              :     return 0;
    3424              : }
    3425              : 
    3426              : template <class derivedT>
    3427            0 : int stdCamera<derivedT>::newCallBack_roi_full( const pcf::IndiProperty &ipRecv )
    3428              : {
    3429              :     if( derivedT::c_stdCamera_usesROI )
    3430              :     {
    3431              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3432              :         return 0;
    3433              : #endif
    3434              : 
    3435            0 :         if( !ipRecv.find( "request" ) )
    3436            0 :             return 0;
    3437              : 
    3438            0 :         if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3439              :         {
    3440            0 :             std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3441              : 
    3442            0 :             indi::updateSwitchIfChanged(
    3443            0 :                 m_indiP_roi_full, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3444              : 
    3445            0 :             m_nextROI.x     = m_full_x;
    3446            0 :             m_nextROI.y     = m_full_y;
    3447            0 :             m_nextROI.w     = m_full_w;
    3448            0 :             m_nextROI.h     = m_full_h;
    3449            0 :             m_nextROI.bin_x = m_full_bin_x;
    3450            0 :             m_nextROI.bin_y = m_full_bin_y;
    3451            0 :             m_lastROI       = m_currentROI;
    3452              :             mx::meta::trueFalseT<derivedT::c_stdCamera_usesROI> tf;
    3453            0 :             return setNextROI( tf );
    3454            0 :         }
    3455              : 
    3456            0 :         return 0;
    3457              :     }
    3458              : 
    3459              :     return 0;
    3460              : }
    3461              : 
    3462              : template <class derivedT>
    3463            0 : int stdCamera<derivedT>::newCallBack_roi_fullbin( const pcf::IndiProperty &ipRecv )
    3464              : {
    3465              :     if( derivedT::c_stdCamera_usesROI )
    3466              :     {
    3467              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3468              :         return 0;
    3469              : #endif
    3470              : 
    3471            0 :         if( !ipRecv.find( "request" ) )
    3472            0 :             return 0;
    3473              : 
    3474            0 :         if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3475              :         {
    3476            0 :             std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3477              : 
    3478            0 :             indi::updateSwitchIfChanged(
    3479            0 :                 m_indiP_roi_fullbin, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3480              : 
    3481            0 :             bool reset = false;
    3482              : 
    3483            0 :             if( m_full_currbin_x == 0 ) // still defaulted
    3484              :             {
    3485            0 :                 derivedT::template log<text_log>( "current-binning full ROI not implemented for this camera",
    3486              :                                                   logPrio::LOG_WARNING );
    3487            0 :                 m_full_currbin_x = m_full_x;
    3488            0 :                 m_full_currbin_y = m_full_y;
    3489            0 :                 m_full_currbin_w = m_full_w;
    3490            0 :                 m_full_currbin_h = m_full_h;
    3491            0 :                 reset            = true;
    3492              :             }
    3493              : 
    3494            0 :             m_nextROI.x = m_full_currbin_x;
    3495            0 :             m_nextROI.y = m_full_currbin_y;
    3496            0 :             m_nextROI.w = m_full_currbin_w;
    3497            0 :             m_nextROI.h = m_full_currbin_h;
    3498            0 :             if( reset )
    3499              :             {
    3500              :                 // Use full binning
    3501            0 :                 m_nextROI.bin_x = m_full_bin_x;
    3502            0 :                 m_nextROI.bin_y = m_full_bin_y;
    3503              : 
    3504              :                 // restore defaults for next time
    3505            0 :                 m_full_currbin_x = 0;
    3506            0 :                 m_full_currbin_y = 0;
    3507            0 :                 m_full_currbin_w = 0;
    3508            0 :                 m_full_currbin_h = 0;
    3509              :             }
    3510              :             else
    3511              :             {
    3512            0 :                 m_nextROI.bin_x = m_currentROI.bin_x;
    3513            0 :                 m_nextROI.bin_y = m_currentROI.bin_y;
    3514              :             }
    3515              : 
    3516            0 :             m_lastROI = m_currentROI;
    3517              :             mx::meta::trueFalseT<derivedT::c_stdCamera_usesROI> tf;
    3518            0 :             return setNextROI( tf );
    3519            0 :         }
    3520              : 
    3521            0 :         return 0;
    3522              :     }
    3523              : 
    3524              :     return 0;
    3525              : }
    3526              : 
    3527              : template <class derivedT>
    3528            0 : int stdCamera<derivedT>::newCallBack_roi_loadlast( const pcf::IndiProperty &ipRecv )
    3529              : {
    3530              :     if( derivedT::c_stdCamera_usesROI )
    3531              :     {
    3532              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3533              :         return 0;
    3534              : #endif
    3535              : 
    3536            0 :         if( !ipRecv.find( "request" ) )
    3537            0 :             return 0;
    3538              : 
    3539            0 :         if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3540              :         {
    3541            0 :             std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3542              : 
    3543            0 :             indi::updateSwitchIfChanged(
    3544            0 :                 m_indiP_roi_loadlast, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3545              : 
    3546            0 :             m_nextROI = m_lastROI;
    3547            0 :             return 0;
    3548            0 :         }
    3549              : 
    3550            0 :         return 0;
    3551              :     }
    3552              : 
    3553              :     return 0;
    3554              : }
    3555              : 
    3556              : template <class derivedT>
    3557            0 : int stdCamera<derivedT>::newCallBack_roi_last( const pcf::IndiProperty &ipRecv )
    3558              : {
    3559              :     if( derivedT::c_stdCamera_usesROI )
    3560              :     {
    3561              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3562              :         return 0;
    3563              : #endif
    3564            0 :         if( !ipRecv.find( "request" ) )
    3565            0 :             return 0;
    3566              : 
    3567            0 :         if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3568              :         {
    3569            0 :             std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3570              : 
    3571            0 :             indi::updateSwitchIfChanged(
    3572            0 :                 m_indiP_roi_last, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3573              : 
    3574            0 :             m_nextROI = m_lastROI;
    3575            0 :             m_lastROI = m_currentROI;
    3576              :             mx::meta::trueFalseT<derivedT::c_stdCamera_usesROI> tf;
    3577            0 :             return setNextROI( tf );
    3578            0 :         }
    3579              : 
    3580            0 :         return 0;
    3581              :     }
    3582              : 
    3583              :     return 0;
    3584              : }
    3585              : 
    3586              : template <class derivedT>
    3587            0 : int stdCamera<derivedT>::newCallBack_roi_default( const pcf::IndiProperty &ipRecv )
    3588              : {
    3589              :     if( derivedT::c_stdCamera_usesROI )
    3590              :     {
    3591              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3592              :         return 0;
    3593              : #endif
    3594              : 
    3595            0 :         if( !ipRecv.find( "request" ) )
    3596            0 :             return 0;
    3597              : 
    3598            0 :         if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3599              :         {
    3600            0 :             std::unique_lock<std::mutex> lock( derived().m_indiMutex );
    3601              : 
    3602            0 :             indi::updateSwitchIfChanged(
    3603            0 :                 m_indiP_roi_default, "request", pcf::IndiElement::Off, derived().m_indiDriver, INDI_IDLE );
    3604              : 
    3605            0 :             m_nextROI.x     = m_default_x;
    3606            0 :             m_nextROI.y     = m_default_y;
    3607            0 :             m_nextROI.w     = m_default_w;
    3608            0 :             m_nextROI.h     = m_default_h;
    3609            0 :             m_nextROI.bin_x = m_default_bin_x;
    3610            0 :             m_nextROI.bin_y = m_default_bin_y;
    3611            0 :             m_lastROI       = m_currentROI;
    3612              :             mx::meta::trueFalseT<derivedT::c_stdCamera_usesROI> tf;
    3613            0 :             return setNextROI( tf );
    3614            0 :         }
    3615              : 
    3616            0 :         return 0;
    3617              :     }
    3618              : 
    3619              :     return 0;
    3620              : }
    3621              : 
    3622              : template <class derivedT>
    3623            0 : int stdCamera<derivedT>::setShutter( int ss, const mx::meta::trueFalseT<true> &t )
    3624              : {
    3625              :     static_cast<void>( t );
    3626            0 :     return derived().setShutter( ss );
    3627              : }
    3628              : 
    3629              : template <class derivedT>
    3630              : int stdCamera<derivedT>::setShutter( int ss, const mx::meta::trueFalseT<false> &f )
    3631              : {
    3632              :     static_cast<void>( ss );
    3633              :     static_cast<void>( f );
    3634              :     return 0;
    3635              : }
    3636              : 
    3637              : template <class derivedT>
    3638            0 : int stdCamera<derivedT>::newCallBack_shutter( const pcf::IndiProperty &ipRecv )
    3639              : {
    3640              :     if( derivedT::c_stdCamera_hasShutter )
    3641              :     {
    3642              : #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    3643              :         return 0;
    3644              : #endif
    3645              : 
    3646            0 :         if( !ipRecv.find( "toggle" ) )
    3647            0 :             return 0;
    3648              : 
    3649              :         mx::meta::trueFalseT<derivedT::c_stdCamera_hasShutter> tf;
    3650              : 
    3651            0 :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    3652              :         {
    3653            0 :             setShutter( 1, tf );
    3654              :         }
    3655              : 
    3656            0 :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    3657              :         {
    3658            0 :             setShutter( 0, tf );
    3659              :         }
    3660              : 
    3661            0 :         return 0;
    3662              :     }
    3663              :     return 0;
    3664              : }
    3665              : 
    3666              : template <class derivedT>
    3667            0 : std::string stdCamera<derivedT>::stateString( const mx::meta::trueFalseT<true> &t )
    3668              : {
    3669              :     static_cast<void>( t );
    3670            0 :     return derived().stateString();
    3671              : }
    3672              : 
    3673              : template <class derivedT>
    3674              : std::string stdCamera<derivedT>::stateString( const mx::meta::trueFalseT<false> &f )
    3675              : {
    3676              :     static_cast<void>( f );
    3677              :     return "";
    3678              : }
    3679              : 
    3680              : template <class derivedT>
    3681            0 : bool stdCamera<derivedT>::stateStringValid( const mx::meta::trueFalseT<true> &t )
    3682              : {
    3683              :     static_cast<void>( t );
    3684            0 :     return derived().stateStringValid();
    3685              : }
    3686              : 
    3687              : template <class derivedT>
    3688              : bool stdCamera<derivedT>::stateStringValid( const mx::meta::trueFalseT<false> &f )
    3689              : {
    3690              :     static_cast<void>( f );
    3691              :     return false;
    3692              : }
    3693              : 
    3694              : template <class derivedT>
    3695            5 : int stdCamera<derivedT>::updateINDI()
    3696              : {
    3697              :     try
    3698              :     {
    3699            5 :         if( !derived().m_indiDriver )
    3700            5 :             return 0;
    3701              : 
    3702              :         if( derivedT::c_stdCamera_readoutSpeed )
    3703              :         {
    3704              :             indi::updateSelectionSwitchIfChanged(
    3705              :                 m_indiP_readoutSpeed, m_readoutSpeedName, derived().m_indiDriver, INDI_OK );
    3706              :         }
    3707              : 
    3708              :         if( derivedT::c_stdCamera_vShiftSpeed )
    3709              :         {
    3710              :             indi::updateSelectionSwitchIfChanged(
    3711              :                 m_indiP_vShiftSpeed, m_vShiftSpeedName, derived().m_indiDriver, INDI_OK );
    3712              :         }
    3713              : 
    3714              :         if( derivedT::c_stdCamera_emGain )
    3715              :         {
    3716            0 :             derived().updateIfChanged( m_indiP_emGain, "current", m_emGain, INDI_IDLE );
    3717            0 :             derived().updateIfChanged( m_indiP_emGain, "target", m_emGainSet, INDI_IDLE );
    3718              :         }
    3719              : 
    3720              :         if( derivedT::c_stdCamera_exptimeCtrl )
    3721              :         {
    3722              :             derived().updateIfChanged( m_indiP_exptime, "current", m_expTime, INDI_IDLE );
    3723              :             derived().updateIfChanged( m_indiP_exptime, "target", m_expTimeSet, INDI_IDLE );
    3724              :         }
    3725              : 
    3726              :         if( derivedT::c_stdCamera_fpsCtrl )
    3727              :         {
    3728            0 :             derived().updateIfChanged( m_indiP_fps, "current", m_fps, INDI_IDLE );
    3729            0 :             derived().updateIfChanged( m_indiP_fps, "target", m_fpsSet, INDI_IDLE );
    3730              :         }
    3731              :         else if( derivedT::c_stdCamera_fps )
    3732              :         {
    3733              :             derived().updateIfChanged( m_indiP_fps, "current", m_fps, INDI_IDLE );
    3734              :         }
    3735              : 
    3736            0 :         if( c_hasFan && m_fanSpeedValid )
    3737              :         {
    3738            0 :             indi::updateSelectionSwitchIfChanged( m_indiP_fanSpeed, m_fanSpeedName, derived().m_indiDriver, INDI_OK );
    3739              :         }
    3740              : 
    3741            0 :         if( c_hasLED && m_ledStateValid )
    3742              :         {
    3743            0 :             if( m_ledState )
    3744              :             {
    3745            0 :                 derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::On, INDI_OK );
    3746              :             }
    3747              :             else
    3748              :             {
    3749            0 :                 derived().updateSwitchIfChanged( m_indiP_led, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    3750              :             }
    3751              :         }
    3752              : 
    3753              :         if( derivedT::c_stdCamera_synchro )
    3754              :         {
    3755            0 :             if( m_synchro == false )
    3756              :             {
    3757            0 :                 derived().updateSwitchIfChanged( m_indiP_synchro, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    3758              :             }
    3759              :             else
    3760              :             {
    3761            0 :                 derived().updateSwitchIfChanged( m_indiP_synchro, "toggle", pcf::IndiElement::On, INDI_OK );
    3762              :             }
    3763              :         }
    3764              : 
    3765              :         if( derivedT::c_stdCamera_usesModes )
    3766              :         {
    3767            0 :             auto st = pcf::IndiProperty::Ok;
    3768            0 :             if( m_nextMode != "" )
    3769            0 :                 st = pcf::IndiProperty::Busy;
    3770              : 
    3771            0 :             for( auto it = m_cameraModes.begin(); it != m_cameraModes.end(); ++it )
    3772              :             {
    3773            0 :                 if( it->first == m_modeName )
    3774            0 :                     derived().updateSwitchIfChanged( m_indiP_mode, it->first, pcf::IndiElement::On, st );
    3775              :                 else
    3776            0 :                     derived().updateSwitchIfChanged( m_indiP_mode, it->first, pcf::IndiElement::Off, st );
    3777              :             }
    3778              :         }
    3779              : 
    3780              :         if( derivedT::c_stdCamera_cropMode )
    3781              :         {
    3782              :             if( m_cropMode == false )
    3783              :             {
    3784              :                 derived().updateSwitchIfChanged( m_indiP_cropMode, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    3785              :             }
    3786              :             else
    3787              :             {
    3788              :                 derived().updateSwitchIfChanged( m_indiP_cropMode, "toggle", pcf::IndiElement::On, INDI_OK );
    3789              :             }
    3790              :         }
    3791              : 
    3792              :         if( derivedT::c_stdCamera_usesROI )
    3793              :         {
    3794              :             // These can't change after initialization, but might not be discoverable until powered on and connected.
    3795              :             // so we'll check every time I guess.
    3796            0 :             derived().updateIfChanged( m_indiP_fullROI, "x", m_full_x, INDI_IDLE );
    3797            0 :             derived().updateIfChanged( m_indiP_fullROI, "y", m_full_y, INDI_IDLE );
    3798            0 :             derived().updateIfChanged( m_indiP_fullROI, "w", m_full_w, INDI_IDLE );
    3799            0 :             derived().updateIfChanged( m_indiP_fullROI, "h", m_full_h, INDI_IDLE );
    3800              :         }
    3801              : 
    3802              :         if( derivedT::c_stdCamera_tempControl )
    3803              :         {
    3804            0 :             if( m_tempControlStatus == false )
    3805              :             {
    3806            0 :                 derived().updateSwitchIfChanged( m_indiP_tempcont, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    3807            0 :                 derived().updateIfChanged( m_indiP_temp, "current", m_ccdTemp, INDI_IDLE );
    3808            0 :                 derived().updateIfChanged( m_indiP_temp, "target", m_ccdTempSetpt, INDI_IDLE );
    3809            0 :                 derived().updateIfChanged( m_indiP_tempstat, "status", m_tempControlStatusStr, INDI_IDLE );
    3810              :             }
    3811              :             else
    3812              :             {
    3813            0 :                 if( m_tempControlOnTarget )
    3814              :                 {
    3815            0 :                     derived().updateSwitchIfChanged( m_indiP_tempcont, "toggle", pcf::IndiElement::On, INDI_OK );
    3816            0 :                     derived().updateIfChanged( m_indiP_temp, "current", m_ccdTemp, INDI_OK );
    3817            0 :                     derived().updateIfChanged( m_indiP_temp, "target", m_ccdTempSetpt, INDI_OK );
    3818            0 :                     derived().updateIfChanged( m_indiP_tempstat, "status", m_tempControlStatusStr, INDI_OK );
    3819              :                 }
    3820              :                 else
    3821              :                 {
    3822            0 :                     derived().updateSwitchIfChanged( m_indiP_tempcont, "toggle", pcf::IndiElement::On, INDI_BUSY );
    3823            0 :                     derived().updateIfChanged( m_indiP_temp, "current", m_ccdTemp, INDI_BUSY );
    3824            0 :                     derived().updateIfChanged( m_indiP_temp, "target", m_ccdTempSetpt, INDI_BUSY );
    3825            0 :                     derived().updateIfChanged( m_indiP_tempstat, "status", m_tempControlStatusStr, INDI_BUSY );
    3826              :                 }
    3827              :             }
    3828              :         }
    3829              :         else if( derivedT::c_stdCamera_temp )
    3830              :         {
    3831              :             derived().updateIfChanged( m_indiP_temp, "current", m_ccdTemp, INDI_IDLE );
    3832              :         }
    3833              : 
    3834              :         if( derivedT::c_stdCamera_hasShutter )
    3835              :         {
    3836            0 :             if( m_shutterStatus == "OPERATING" )
    3837              :             {
    3838            0 :                 derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_BUSY );
    3839              :             }
    3840            0 :             if( m_shutterStatus == "POWERON" || m_shutterStatus == "READY" )
    3841              :             {
    3842            0 :                 derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_IDLE );
    3843              :             }
    3844              :             else
    3845              :             {
    3846            0 :                 derived().updateIfChanged( m_indiP_shutterStatus, "status", m_shutterStatus, INDI_IDLE );
    3847              :             }
    3848              : 
    3849            0 :             if( m_shutterState == 0 ) // 0 shut, 1 open
    3850              :             {
    3851            0 :                 derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::On, INDI_OK );
    3852              :             }
    3853              :             else
    3854              :             {
    3855            0 :                 derived().updateSwitchIfChanged( m_indiP_shutter, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    3856              :             }
    3857              :         }
    3858              : 
    3859              :         if( derivedT::c_stdCamera_usesStateString )
    3860              :         {
    3861              :             mx::meta::trueFalseT<derivedT::c_stdCamera_usesStateString> tf;
    3862            0 :             derived().updateIfChanged( m_indiP_stateString, "current", stateString( tf ), INDI_IDLE );
    3863            0 :             if( stateStringValid( tf ) )
    3864              :             {
    3865            0 :                 derived().updateIfChanged( m_indiP_stateString, "valid", "yes", INDI_IDLE );
    3866              :             }
    3867              :             else
    3868              :             {
    3869            0 :                 derived().updateIfChanged( m_indiP_stateString, "valid", "no", INDI_IDLE );
    3870              :             }
    3871              :         }
    3872            0 :         return 0;
    3873              :     }
    3874            0 :     catch( const std::exception &e )
    3875              :     {
    3876            0 :         return derivedT::template log<software_error, -1>(
    3877            0 :             { __FILE__, __LINE__, std::string( "Exception caught: " ) + e.what() } );
    3878              :     }
    3879              : }
    3880              : 
    3881              : template <class derivedT>
    3882          108 : int stdCamera<derivedT>::recordCamera( bool force )
    3883              : {
    3884          108 :     static std::string last_mode;
    3885              :     static roi         last_roi;
    3886              :     static float       last_expTime             = -1e30; // ensure first one goes
    3887              :     static float       last_fps                 = 0;
    3888              :     static float       last_adcSpeed            = -1;
    3889              :     static float       last_emGain              = -1;
    3890              :     static float       last_ccdTemp             = 0;
    3891              :     static float       last_ccdTempSetpt        = 0;
    3892              :     static bool        last_tempControlStatus   = 0;
    3893              :     static bool        last_tempControlOnTarget = 0;
    3894          108 :     static std::string last_tempControlStatusStr;
    3895          108 :     static std::string last_shutterStatus;
    3896              :     static int         last_shutterState = false;
    3897              :     static bool        last_synchro      = false;
    3898              :     static float       last_vshiftSpeed  = -1;
    3899              :     static bool        last_cropMode     = false;
    3900          108 :     static std::string last_fanSpeed;
    3901          108 :     static std::string last_analogGain;
    3902              :     static bool        last_ledState = false;
    3903          108 :     static std::string last_readoutSpeed;
    3904              : 
    3905           96 :     if( force || m_modeName != last_mode || m_currentROI.x != last_roi.x || m_currentROI.y != last_roi.y ||
    3906           91 :         m_currentROI.w != last_roi.w || m_currentROI.h != last_roi.h || m_currentROI.bin_x != last_roi.bin_x ||
    3907           91 :         m_currentROI.bin_y != last_roi.bin_y || m_expTime != last_expTime || m_fps != last_fps ||
    3908           60 :         m_emGain != last_emGain || m_adcSpeed != last_adcSpeed || m_ccdTemp != last_ccdTemp ||
    3909           40 :         m_ccdTempSetpt != last_ccdTempSetpt || m_tempControlStatus != last_tempControlStatus ||
    3910           36 :         m_tempControlOnTarget != last_tempControlOnTarget || m_tempControlStatusStr != last_tempControlStatusStr ||
    3911           36 :         m_shutterStatus != last_shutterStatus || m_shutterState != last_shutterState || m_synchro != last_synchro ||
    3912           36 :         m_vshiftSpeed != last_vshiftSpeed || m_cropMode != last_cropMode || m_readoutSpeedName != last_readoutSpeed ||
    3913           21 :         ( c_hasFan && m_fanSpeedValid && m_fanSpeedName != last_fanSpeed ) ||
    3914          216 :         ( c_hasAnalogGain && m_analogGainValid && m_analogGainName != last_analogGain ) ||
    3915           12 :         ( c_hasLED && m_ledStateValid && m_ledState != last_ledState ) )
    3916              :     {
    3917          232 :         derived().template telem<telem_stdcam>(
    3918           90 :             { m_modeName,
    3919           90 :               m_currentROI.x,
    3920           90 :               m_currentROI.y,
    3921           90 :               m_currentROI.w,
    3922           90 :               m_currentROI.h,
    3923           90 :               m_currentROI.bin_x,
    3924           90 :               m_currentROI.bin_y,
    3925           90 :               m_expTime,
    3926           90 :               m_fps,
    3927           90 :               m_emGain,
    3928           90 :               m_adcSpeed,
    3929           90 :               m_ccdTemp,
    3930           90 :               m_ccdTempSetpt,
    3931            0 :               (uint8_t)m_tempControlStatus,
    3932            0 :               (uint8_t)m_tempControlOnTarget,
    3933           90 :               m_tempControlStatusStr,
    3934           90 :               m_shutterStatus,
    3935            0 :               (int8_t)m_shutterState,
    3936            0 :               (uint8_t)m_synchro,
    3937           90 :               m_vshiftSpeed,
    3938           26 :               (uint8_t)m_cropMode,
    3939          181 :               c_hasFan && m_fanSpeedValid ? m_fanSpeedName : std::string( "" ),
    3940           90 :               m_readoutSpeedName,
    3941          262 :               c_hasAnalogGain && m_analogGainValid ? m_analogGainName : std::string( "" ),
    3942          180 :               c_hasLED && m_ledStateValid ? static_cast<int8_t>( m_ledState ? 1 : 0 ) : static_cast<int8_t>( -1 ) } );
    3943              : 
    3944           90 :         last_mode                 = m_modeName;
    3945           90 :         last_roi                  = m_currentROI;
    3946           90 :         last_expTime              = m_expTime;
    3947           90 :         last_fps                  = m_fps;
    3948           90 :         last_emGain               = m_emGain;
    3949           90 :         last_adcSpeed             = m_adcSpeed;
    3950           90 :         last_ccdTemp              = m_ccdTemp;
    3951           90 :         last_ccdTempSetpt         = m_ccdTempSetpt;
    3952           90 :         last_tempControlStatus    = m_tempControlStatus;
    3953           90 :         last_tempControlOnTarget  = m_tempControlOnTarget;
    3954           90 :         last_tempControlStatusStr = m_tempControlStatusStr;
    3955           90 :         last_shutterStatus        = m_shutterStatus;
    3956           90 :         last_shutterState         = m_shutterState;
    3957           90 :         last_synchro              = m_synchro;
    3958           90 :         last_vshiftSpeed          = m_vshiftSpeed;
    3959           90 :         last_cropMode             = m_cropMode;
    3960          169 :         last_fanSpeed             = c_hasFan && m_fanSpeedValid ? m_fanSpeedName : std::string( "" );
    3961          144 :         last_analogGain           = c_hasAnalogGain && m_analogGainValid ? m_analogGainName : std::string( "" );
    3962           90 :         last_ledState             = m_ledState;
    3963           90 :         last_readoutSpeed         = m_readoutSpeedName;
    3964              :     }
    3965              : 
    3966          108 :     return 0;
    3967              : }
    3968              : 
    3969              : /// Call stdCameraT::setupConfig with error checking for stdCamera
    3970              : /**
    3971              :  * \param cfig the application configurator
    3972              :  */
    3973              : #define STDCAMERA_SETUP_CONFIG( cfig )                                                                                 \
    3974              :     if( stdCameraT::setupConfig( cfig ) < 0 )                                                                          \
    3975              :     {                                                                                                                  \
    3976              :         log<software_error>( { __FILE__, __LINE__, "Error from stdCameraT::setupConfig" } );                           \
    3977              :         m_shutdown = true;                                                                                             \
    3978              :         return;                                                                                                        \
    3979              :     }
    3980              : 
    3981              : /// Call stdCameraT::loadConfig with error checking for stdCamera
    3982              : /** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
    3983              :  * \param cfig the application configurator
    3984              :  */
    3985              : #define STDCAMERA_LOAD_CONFIG( cfig )                                                                                  \
    3986              :     if( stdCameraT::loadConfig( cfig ) < 0 )                                                                           \
    3987              :     {                                                                                                                  \
    3988              :         return log<software_error, -1>( { __FILE__, __LINE__, "Error from stdCameraT::loadConfig" } );                 \
    3989              :     }
    3990              : 
    3991              : /// Call stdCameraT::appStartup with error checking for stdCamera
    3992              : #define STDCAMERA_APP_STARTUP                                                                                          \
    3993              :     if( stdCameraT::appStartup() < 0 )                                                                                 \
    3994              :     {                                                                                                                  \
    3995              :         return log<software_error, -1>( { __FILE__, __LINE__, "Error from stdCameraT::appStartup" } );                 \
    3996              :     }
    3997              : 
    3998              : /// Call stdCameraT::appLogic with error checking for stdCamera
    3999              : #define STDCAMERA_APP_LOGIC                                                                                            \
    4000              :     if( stdCameraT::appLogic() < 0 )                                                                                   \
    4001              :     {                                                                                                                  \
    4002              :         return log<software_error, -1>( { __FILE__, __LINE__, "Error from stdCameraT::appLogic" } );                   \
    4003              :     }
    4004              : 
    4005              : /// Call stdCameraT::updateINDI with error checking for stdCamera
    4006              : #define STDCAMERA_UPDATE_INDI                                                                                          \
    4007              :     if( stdCameraT::updateINDI() < 0 )                                                                                 \
    4008              :     {                                                                                                                  \
    4009              :         return log<software_error, -1>( { __FILE__, __LINE__, "Error from stdCameraT::updateINDI" } );                 \
    4010              :     }
    4011              : 
    4012              : /// Call stdCameraT::appShutdown with error checking for stdCamera
    4013              : #define STDCAMERA_APP_SHUTDOWN                                                                                         \
    4014              :     if( stdCameraT::appShutdown() < 0 )                                                                                \
    4015              :     {                                                                                                                  \
    4016              :         return log<software_error, -1>( { __FILE__, __LINE__, "Error from stdCameraT::appShutdown" } );                \
    4017              :     }
    4018              : 
    4019              : } // namespace dev
    4020              : } // namespace app
    4021              : } // namespace MagAOX
    4022              : 
    4023              : #endif // stdCamera_hpp
        

Generated by: LCOV version 2.0-1