LCOV - code coverage report
Current view: top level - apps/observerCtrl - observerCtrl.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 4.4 % 294 13
Test Date: 2026-04-15 19:34:29 Functions: 13.6 % 44 6

            Line data    Source code
       1              : /** \file observerCtrl.hpp
       2              :  * \brief The MagAO-X Observer Controller header file
       3              :  *
       4              :  * \ingroup observerCtrl_files
       5              :  */
       6              : 
       7              : #ifndef observerCtrl_hpp
       8              : #define observerCtrl_hpp
       9              : 
      10              : #include <map>
      11              : #include <mx/math/geo.hpp>
      12              : 
      13              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      14              : #include "../../magaox_git_version.h"
      15              : 
      16              : /** \defgroup observerCtrl
      17              :  * \brief The MagAO-X Observer Controller application
      18              :  *
      19              :  * <a href="../handbook/operating/software/apps/observerCtrl.html">Application Documentation</a>
      20              :  *
      21              :  * \ingroup apps
      22              :  *
      23              :  */
      24              : 
      25              : /** \defgroup observerCtrl_files
      26              :  * \ingroup observerCtrl
      27              :  */
      28              : 
      29              : namespace MagAOX
      30              : {
      31              : namespace app
      32              : {
      33              : 
      34              : /// The MagAO-X Observer Controller
      35              : /**
      36              :  * \ingroup observerCtrl
      37              :  */
      38              : class observerCtrl : public MagAOXApp<true>, public dev::telemeter<observerCtrl>
      39              : {
      40              : 
      41              :     // Give the test harness access.
      42              :     friend class observerCtrl_test;
      43              : 
      44              :     friend class dev::telemeter<observerCtrl>;
      45              : 
      46              :     typedef dev::telemeter<observerCtrl> telemeterT;
      47              : 
      48              :     typedef std::chrono::time_point<std::chrono::steady_clock> timePointT;
      49              :     typedef std::string                                        timeStampT;
      50              :     typedef std::chrono::duration<double>                      durationT;
      51              :     std::string timeStampAsISO8601( const std::chrono::time_point<std::chrono::system_clock> &tp );
      52              : 
      53              :   protected:
      54              :     /** \name Configurable Parameters
      55              :      *@{
      56              :      */
      57              : 
      58              :     std::vector<std::string> m_streamWriters; ///< The stream writers to stop and start
      59              : 
      60              :     std::string m_tcsDev{ "tcsi" };
      61              :     std::string m_catalogProp{ "catalog" };
      62              :     std::string m_objEl{ "object" };
      63              :     std::string m_catdataProp{ "catdata" };
      64              :     std::string m_raEl{ "ra" };
      65              :     std::string m_decEl{ "dec" };
      66              :     std::string m_labModeProp{ "labMode" };
      67              : 
      68              :     std::string m_teldataProp{ "teldata" };
      69              :     std::string m_parangEl{ "pa" };
      70              : 
      71              :     std::string m_loopDev{ "holoop" };
      72              :     std::string m_loopStateProp{ "loop_state" };
      73              : 
      74              :     ///@}
      75              : 
      76              :     /// The observer specification
      77              :     struct observer
      78              :     {
      79              :         std::string m_fullName;       ///< Obsever's full name
      80              :         std::string m_pfoa;           ///< Observer's preferred forma of address
      81              :         std::string m_pronunciation;  ///< Guide for the TTS to pronounced the pfoa (defaults to pfoa)
      82              :         std::string m_email;          ///< Observer's email.  Must be unique.
      83              :         std::string m_sanitizedEmail; ///< Observer's email sanitized for use in INDI properties
      84              :         std::string m_institution;    ///< The observer's institution
      85              :     };
      86              : 
      87              :     typedef std::map<std::string, observer> observerMapT;
      88              : 
      89              :     observerMapT m_observers; ///< The observers from the configuration file
      90              : 
      91              :     observer m_currentObserver; ///< The current selected observer
      92              : 
      93              :     observer m_currentOperator; ///< The current selected observer
      94              : 
      95              :     std::string m_obsName;          ///< The name of the observation.
      96              :     double      m_obsDuration{ 0 }; ///< The desired duration of the observation.  If 0 then until stopped.
      97              : 
      98              :     bool m_observing{ false }; ///< Flag indicating whether or not we are in an observation
      99              : 
     100              :     std::string m_target;
     101              : 
     102              :     std::string m_catObj;
     103              : 
     104              :     std::string m_catRA;
     105              :     std::string m_catDec;
     106              : 
     107              :     bool m_loop{ false }; ///< Flag tracking loop state.  true is loop closed.
     108              : 
     109              :     bool m_labMode{ false }; ///< Flag tracking whether the TCS interface is in lab mode.
     110              : 
     111              :     bool m_newTargetBlock{ true }; /**< Flag to indicate that this is a new target block.  This starts out as true
     112              :                                         but becomes false on the first observation.  Then becomes true when the
     113              :                                         loop closes for the first time after a target change. */
     114              :     bool m_newTarget{ false }; /**< Flag to track when the target changes.  Occurs either automatically on a TCS update
     115              :                                     or on a user override.*/
     116              : 
     117              :     /// The start time of the current observation
     118              :     timePointT m_obsStartTime;
     119              : 
     120              :     /// The UTC start time of the current observation
     121              :     timeStampT m_obsStartTimeStamp;
     122              : 
     123              :     /// The parallactic angle at the start of the observation
     124              :     double m_obsStartParang{ 0 };
     125              : 
     126              :     /// The start time of the current target
     127              :     timePointT m_tgtStartTime;
     128              : 
     129              :     /// The UTC start time of the current target
     130              :     timeStampT m_tgtStartTimeStamp;
     131              : 
     132              :     /// The parallactic angle at the start of observing the current target
     133              :     double m_tgtStartParang{ 0 };
     134              : 
     135              :     /// The current parallactic angle
     136              :     double m_parang;
     137              : 
     138              :     /// The current target time.  Only updated while observing.
     139              :     durationT m_tgtTime;
     140              : 
     141              :     /// The current target angle. Only updated while observing.
     142              :     double m_tgtAng{ 0 };
     143              : 
     144              :   public:
     145              :     /// Default c'tor.
     146              :     observerCtrl();
     147              : 
     148              :     /// D'tor, declared and defined for noexcept.
     149           12 :     ~observerCtrl() noexcept
     150           12 :     {
     151           12 :     }
     152              : 
     153              :     virtual void setupConfig();
     154              : 
     155              :     /// Implementation of loadConfig logic, separated for testing.
     156              :     /** This is called by loadConfig().
     157              :      */
     158              :     int loadConfigImpl( mx::app::appConfigurator &_config /**< [in] an application configuration
     159              :                                                                     from which to load values*/
     160              :     );
     161              : 
     162              :     virtual void loadConfig();
     163              : 
     164              :     /// Startup function
     165              :     /**
     166              :      *
     167              :      */
     168              :     virtual int appStartup();
     169              : 
     170              :     /// Implementation of the FSM for observerCtrl.
     171              :     /**
     172              :      * \returns 0 on no critical error
     173              :      * \returns -1 on an error requiring shutdown
     174              :      */
     175              :     virtual int appLogic();
     176              : 
     177              :     /// Shutdown the app.
     178              :     /**
     179              :      *
     180              :      */
     181              :     virtual int appShutdown();
     182              : 
     183              :     void startObserving();
     184              : 
     185              :     void stopObserving();
     186              : 
     187              :     ///\name INDI
     188              :     /** @{
     189              :      */
     190              :   protected:
     191              :     pcf::IndiProperty m_indiP_observers; ///< Selection switch to allow selection of the observer
     192              :     pcf::IndiProperty m_indiP_observer;  ///< Text which contains the specifications of the current observer
     193              : 
     194              :     pcf::IndiProperty m_indiP_operators; ///< Selection switch to allow selection of the observer
     195              :     pcf::IndiProperty m_indiP_operator;  ///< Text which contains the specifications of the current observer
     196              : 
     197              :     pcf::IndiProperty m_indiP_obsName;     /**< The current observation name, used to specify the
     198              :                                                 purpose of the observation*/
     199              :     pcf::IndiProperty m_indiP_observing;   ///< Toggle switch to trigger observation
     200              :     pcf::IndiProperty m_indiP_obsDuration; ///< Number to set the desired duration of observation
     201              :     pcf::IndiProperty m_indiP_obsStart;    ///< String timestamp indicating the start for target/observation
     202              :     pcf::IndiProperty m_indiP_obsTime;     ///< Number tracking the elapsed time
     203              :     pcf::IndiProperty m_indiP_obsAngle;    ///< Number tracking the change in angle
     204              :     pcf::IndiProperty m_indiP_sws;         ///< Selection to switch which stream writers are enabled
     205              :     pcf::IndiProperty m_indiP_userlog;     ///< Text to enter a user log
     206              : 
     207              :     pcf::IndiProperty m_indiP_resetTarget; ///< Reset the target statistics
     208              : 
     209              :     pcf::IndiProperty m_indiP_target; ///< The target name, which can be overridden by the user
     210              : 
     211              :     pcf::IndiProperty m_indiP_tcsTarget; ///< Set the target to match TCS catObj
     212              : 
     213              :     pcf::IndiProperty m_indiP_catalog; ///< Catalog text data
     214              :     pcf::IndiProperty m_indiP_catdata; ///< Catalog numeric data
     215              :     pcf::IndiProperty m_indiP_teldata; ///< Telescope data (for parang)
     216              : 
     217              :     pcf::IndiProperty m_indiP_labMode; ///< Tracks whether TCS is in lab mode.
     218              : 
     219              :     pcf::IndiProperty m_indiP_loop; ///< Tracks the loop state
     220              : 
     221              :   public:
     222            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_observers );
     223              : 
     224            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_operators );
     225              : 
     226            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_obsName );
     227            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_observing );
     228            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_obsDuration );
     229              : 
     230            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_sws );
     231              : 
     232            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_userlog );
     233              : 
     234            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_resetTarget );
     235              : 
     236            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_target );
     237              : 
     238            0 :     INDI_NEWCALLBACK_DECL( observerCtrl, m_indiP_tcsTarget );
     239              : 
     240            0 :     INDI_SETCALLBACK_DECL( observerCtrl, m_indiP_catalog );
     241              : 
     242            0 :     INDI_SETCALLBACK_DECL( observerCtrl, m_indiP_catdata );
     243              : 
     244            0 :     INDI_SETCALLBACK_DECL( observerCtrl, m_indiP_teldata );
     245              : 
     246            0 :     INDI_SETCALLBACK_DECL( observerCtrl, m_indiP_labMode );
     247              : 
     248            0 :     INDI_SETCALLBACK_DECL( observerCtrl, m_indiP_loop );
     249              : 
     250              :     ///@}
     251              : 
     252              :     /** \name Telemeter Interface
     253              :      *
     254              :      * @{
     255              :      */
     256              :     int checkRecordTimes();
     257              : 
     258              :     int recordTelem( const telem_observer * );
     259              : 
     260              :     int recordObserver( bool force = false );
     261              : 
     262              :     ///@}
     263              : };
     264              : 
     265          300 : observerCtrl::observerCtrl() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     266              : {
     267           12 :     return;
     268            0 : }
     269              : 
     270            0 : void observerCtrl::setupConfig()
     271              : {
     272            0 :     config.add( "stream.writers",
     273              :                 "",
     274              :                 "stream.writers",
     275              :                 argType::Required,
     276              :                 "stream",
     277              :                 "writers",
     278              :                 false,
     279              :                 "string",
     280              :                 "The device names of the stream writers to control." );
     281              : 
     282            0 :     dev::telemeter<observerCtrl>::setupConfig( config );
     283            0 : }
     284              : 
     285            0 : int observerCtrl::loadConfigImpl( mx::app::appConfigurator &_config )
     286              : {
     287            0 :     _config( m_streamWriters, "stream.writers" );
     288              : 
     289            0 :     std::vector<std::string> sections;
     290              : 
     291            0 :     _config.unusedSections( sections );
     292              : 
     293            0 :     if( sections.size() == 0 )
     294              :     {
     295            0 :         log<text_log>( "no observers found in config", logPrio::LOG_CRITICAL );
     296            0 :         return -1;
     297              :     }
     298              : 
     299            0 :     for( size_t i = 0; i < sections.size(); ++i )
     300              :     {
     301            0 :         bool pfoaSet = _config.isSetUnused( mx::app::iniFile::makeKey( sections[i], "pfoa" ) );
     302            0 :         if( !pfoaSet )
     303            0 :             continue;
     304              : 
     305            0 :         std::string email = sections[i];
     306              : 
     307            0 :         std::string pfoa;
     308            0 :         _config.configUnused( pfoa, mx::app::iniFile::makeKey( sections[i], "pfoa" ) );
     309              : 
     310            0 :         std::string pronunciation;
     311            0 :         _config.configUnused( pronunciation, mx::app::iniFile::makeKey( sections[i], "pronunciation" ) );
     312              : 
     313            0 :         if( pronunciation == "" )
     314              :         {
     315            0 :             pronunciation = pfoa;
     316              :         }
     317              : 
     318            0 :         std::string fullName;
     319            0 :         _config.configUnused( fullName, mx::app::iniFile::makeKey( sections[i], "full_name" ) );
     320              : 
     321            0 :         std::string institution;
     322            0 :         _config.configUnused( institution, mx::app::iniFile::makeKey( sections[i], "institution" ) );
     323              : 
     324            0 :         std::string sanitizedEmail = "";
     325            0 :         for( size_t n = 0; n < email.size(); ++n )
     326              :         {
     327            0 :             if( email[n] == '@' )
     328              :             {
     329            0 :                 sanitizedEmail = sanitizedEmail + "-at-";
     330              :             }
     331            0 :             else if( email[n] == '.' )
     332              :             {
     333            0 :                 sanitizedEmail = sanitizedEmail + "-dot-";
     334              :             }
     335              :             else
     336              :             {
     337            0 :                 sanitizedEmail.push_back( email[n] );
     338              :             }
     339              :         }
     340            0 :         m_observers[email] = observer( { fullName, pfoa, pronunciation, email, sanitizedEmail, institution } );
     341            0 :     }
     342              : 
     343            0 :     return 0;
     344            0 : }
     345              : 
     346            0 : void observerCtrl::loadConfig()
     347              : {
     348            0 :     if( loadConfigImpl( config ) < 0 )
     349              :     {
     350            0 :         m_shutdown = 1;
     351            0 :         return;
     352              :     }
     353              : 
     354            0 :     if( m_observers.size() < 1 )
     355              :     {
     356            0 :         log<text_log>( "no observers found in config", logPrio::LOG_CRITICAL );
     357            0 :         m_shutdown = 1;
     358            0 :         return;
     359              :     }
     360              : 
     361            0 :     dev::telemeter<observerCtrl>::loadConfig( config );
     362              : }
     363              : 
     364            0 : int observerCtrl::appStartup()
     365              : {
     366            0 :     std::vector<std::string> sanitizedEmails;
     367            0 :     std::vector<std::string> emails;
     368            0 :     for( auto it = m_observers.begin(); it != m_observers.end(); ++it )
     369              :     {
     370            0 :         sanitizedEmails.push_back( it->second.m_sanitizedEmail );
     371            0 :         emails.push_back( it->second.m_email );
     372              :     }
     373              : 
     374            0 :     if( createStandardIndiSelectionSw( m_indiP_observers, "observers", sanitizedEmails, emails ) < 0 )
     375              :     {
     376            0 :         log<software_critical>( { __FILE__, __LINE__ } );
     377            0 :         return -1;
     378              :     }
     379              : 
     380              :     // Set to default user of jared
     381              :     ///\todo do something else. maybe a default user is specified in the config?
     382            0 :     for( auto &it : m_observers )
     383              :     {
     384            0 :         if( it.first.find( "jrmales" ) != std::string::npos )
     385              :         {
     386            0 :             m_indiP_observers[it.second.m_sanitizedEmail].setSwitchState( pcf::IndiElement::On );
     387            0 :             m_currentObserver = it.second;
     388              :         }
     389              :         else
     390              :         {
     391            0 :             m_indiP_observers[it.second.m_sanitizedEmail].setSwitchState( pcf::IndiElement::Off );
     392              :         }
     393              :     }
     394              : 
     395            0 :     REG_INDI_NEWPROP_NOSETUP( m_indiP_observers );
     396              : 
     397            0 :     if( createStandardIndiSelectionSw( m_indiP_operators, "operators", sanitizedEmails, emails ) < 0 )
     398              :     {
     399            0 :         log<software_critical>( { __FILE__, __LINE__ } );
     400            0 :         return -1;
     401              :     }
     402              : 
     403              :     // Set to default user of jared
     404              :     ///\todo do something else. maybe a default user is specified in the config?
     405            0 :     for( auto &it : m_observers )
     406              :     {
     407            0 :         if( it.first.find( "jrmales" ) != std::string::npos )
     408              :         {
     409            0 :             m_indiP_operators[it.second.m_sanitizedEmail].setSwitchState( pcf::IndiElement::On );
     410            0 :             m_currentOperator = it.second;
     411              :         }
     412              :         else
     413              :         {
     414            0 :             m_indiP_operators[it.second.m_sanitizedEmail].setSwitchState( pcf::IndiElement::Off );
     415              :         }
     416              :     }
     417              : 
     418            0 :     REG_INDI_NEWPROP_NOSETUP( m_indiP_operators );
     419              : 
     420            0 :     CREATE_REG_INDI_NEW_TEXT( m_indiP_obsName, "obs_name", "Observation Name", "Observer" );
     421              : 
     422            0 :     CREATE_REG_INDI_NEW_TOGGLESWITCH( m_indiP_observing, "obs_on" );
     423              : 
     424            0 :     CREATE_REG_INDI_NEW_NUMBERD( m_indiP_obsDuration, "obs_duration", 0, 300, 0.1, "%0.1f", "Duration", "Observer" );
     425              : 
     426            0 :     CREATE_REG_INDI_RO_NUMBER( m_indiP_obsTime, "obs_time", "Observation Time", "Observer" );
     427            0 :     indi::addNumberElement<double>( m_indiP_obsTime, "observation", 0, 14400, 0.1, "%0.1f", "Current Obs" );
     428            0 :     indi::addNumberElement<double>( m_indiP_obsTime, "target", 0, 14400, 0.1, "%0.1f", "Target" );
     429              : 
     430            0 :     REG_INDI_NEWPROP_NOCB( m_indiP_obsStart, "obs_start", pcf::IndiProperty::Text );
     431            0 :     indi::addTextElement( m_indiP_obsStart, "observation" );
     432            0 :     indi::addTextElement( m_indiP_obsStart, "target" );
     433              : 
     434            0 :     CREATE_REG_INDI_RO_NUMBER( m_indiP_obsAngle, "obs_delta_parang", "Change in Par. Ang.", "Observer" );
     435            0 :     indi::addNumberElement<double>( m_indiP_obsAngle, "observation", 0, 360, 0.1, "%0.1f", "Current Obs" );
     436            0 :     indi::addNumberElement<double>( m_indiP_obsAngle, "target", 0, 360, 0.1, "%0.1f", "Target" );
     437              : 
     438            0 :     REG_INDI_NEWPROP_NOCB( m_indiP_observer, "current_observer", pcf::IndiProperty::Text );
     439            0 :     indi::addTextElement( m_indiP_observer, "full_name" );
     440            0 :     indi::addTextElement( m_indiP_observer, "email" );
     441            0 :     indi::addTextElement( m_indiP_observer, "pfoa" );
     442            0 :     indi::addTextElement( m_indiP_observer, "pronunciation" );
     443            0 :     indi::addTextElement( m_indiP_observer, "institution" );
     444              : 
     445            0 :     REG_INDI_NEWPROP_NOCB( m_indiP_operator, "current_operator", pcf::IndiProperty::Text );
     446            0 :     indi::addTextElement( m_indiP_operator, "full_name" );
     447            0 :     indi::addTextElement( m_indiP_operator, "email" );
     448            0 :     indi::addTextElement( m_indiP_operator, "pfoa" );
     449            0 :     indi::addTextElement( m_indiP_operator, "pronunciation" );
     450            0 :     indi::addTextElement( m_indiP_operator, "institution" );
     451              : 
     452            0 :     m_indiP_sws = pcf::IndiProperty( pcf::IndiProperty::Switch );
     453            0 :     m_indiP_sws.setDevice( configName() );
     454            0 :     m_indiP_sws.setName( "writers" );
     455            0 :     m_indiP_sws.setPerm( pcf::IndiProperty::ReadWrite );
     456            0 :     m_indiP_sws.setState( pcf::IndiProperty::Idle );
     457            0 :     m_indiP_sws.setRule( pcf::IndiProperty::AnyOfMany );
     458              : 
     459            0 :     for( size_t n = 0; n < m_streamWriters.size(); ++n )
     460              :     {
     461            0 :         m_indiP_sws.add( pcf::IndiElement( m_streamWriters[n], pcf::IndiElement::Off ) );
     462              :     }
     463              : 
     464            0 :     REG_INDI_NEWPROP_NOSETUP( m_indiP_sws );
     465              : 
     466            0 :     m_indiP_userlog = pcf::IndiProperty( pcf::IndiProperty::Text );
     467            0 :     m_indiP_userlog.setDevice( configName() );
     468            0 :     m_indiP_userlog.setName( "user_log" );
     469            0 :     m_indiP_userlog.setPerm( pcf::IndiProperty::ReadWrite );
     470            0 :     m_indiP_userlog.setState( pcf::IndiProperty::Idle );
     471            0 :     m_indiP_userlog.add( pcf::IndiElement( "email" ) );
     472            0 :     m_indiP_userlog.add( pcf::IndiElement( "message" ) );
     473            0 :     m_indiP_userlog.add( pcf::IndiElement( "time_s" ) );
     474            0 :     m_indiP_userlog.add( pcf::IndiElement( "time_ns" ) );
     475              : 
     476            0 :     REG_INDI_NEWPROP_NOSETUP( m_indiP_userlog );
     477              : 
     478            0 :     CREATE_REG_INDI_NEW_REQUESTSWITCH( m_indiP_resetTarget, "target_reset" );
     479              : 
     480            0 :     CREATE_REG_INDI_NEW_TEXT( m_indiP_target, "target", "Target", "Observer" );
     481              : 
     482            0 :     CREATE_REG_INDI_NEW_REQUESTSWITCH( m_indiP_tcsTarget, "target_load_from_tcs" );
     483              : 
     484            0 :     REG_INDI_SETPROP( m_indiP_catalog, m_tcsDev, m_catalogProp );
     485            0 :     REG_INDI_SETPROP( m_indiP_catdata, m_tcsDev, m_catdataProp );
     486            0 :     REG_INDI_SETPROP( m_indiP_teldata, m_tcsDev, m_teldataProp );
     487            0 :     REG_INDI_SETPROP( m_indiP_labMode, m_tcsDev, m_labModeProp );
     488              : 
     489            0 :     REG_INDI_SETPROP( m_indiP_loop, m_loopDev, m_loopStateProp );
     490              : 
     491            0 :     TELEMETER_APP_STARTUP;
     492              : 
     493            0 :     state( stateCodes::READY );
     494            0 :     return 0;
     495            0 : }
     496              : 
     497            0 : int observerCtrl::appLogic()
     498              : {
     499              : 
     500            0 :     std::unique_lock<std::mutex> lock( m_indiMutex, std::try_to_lock );
     501              : 
     502            0 :     if( lock.owns_lock() )
     503              :     {
     504            0 :         updatesIfChanged<std::string>( m_indiP_observer,
     505              :                                        { "full_name", "email", "pfoa", "pronunciation", "institution" },
     506            0 :                                        { m_currentObserver.m_fullName,
     507            0 :                                          m_currentObserver.m_email,
     508            0 :                                          m_currentObserver.m_pfoa,
     509            0 :                                          m_currentObserver.m_pronunciation,
     510            0 :                                          m_currentObserver.m_institution } );
     511              : 
     512            0 :         for( auto it = m_observers.begin(); it != m_observers.end(); ++it )
     513              :         {
     514            0 :             if( it->first == m_currentObserver.m_email )
     515              :             {
     516            0 :                 updateSwitchIfChanged(
     517            0 :                     m_indiP_observers, it->second.m_sanitizedEmail, pcf::IndiElement::On, INDI_IDLE );
     518              :             }
     519              :             else
     520              :             {
     521            0 :                 updateSwitchIfChanged(
     522            0 :                     m_indiP_observers, it->second.m_sanitizedEmail, pcf::IndiElement::Off, INDI_IDLE );
     523              :             }
     524              :         }
     525              : 
     526            0 :         updatesIfChanged<std::string>( m_indiP_operator,
     527              :                                        { "full_name", "email", "pfoa", "pronunciation", "institution" },
     528            0 :                                        { m_currentOperator.m_fullName,
     529            0 :                                          m_currentOperator.m_email,
     530            0 :                                          m_currentOperator.m_pfoa,
     531            0 :                                          m_currentOperator.m_pronunciation,
     532            0 :                                          m_currentOperator.m_institution } );
     533              : 
     534            0 :         for( auto it = m_observers.begin(); it != m_observers.end(); ++it )
     535              :         {
     536            0 :             if( it->first == m_currentOperator.m_email )
     537              :             {
     538            0 :                 updateSwitchIfChanged(
     539            0 :                     m_indiP_operators, it->second.m_sanitizedEmail, pcf::IndiElement::On, INDI_IDLE );
     540              :             }
     541              :             else
     542              :             {
     543            0 :                 updateSwitchIfChanged(
     544            0 :                     m_indiP_operators, it->second.m_sanitizedEmail, pcf::IndiElement::Off, INDI_IDLE );
     545              :             }
     546              :         }
     547              : 
     548            0 :         updatesIfChanged<std::string>( m_indiP_obsName, { "current", "target" }, { m_obsName, m_obsName } );
     549              : 
     550            0 :         updatesIfChanged<double>( m_indiP_obsDuration, { "current", "target" }, { m_obsDuration, m_obsDuration } );
     551              : 
     552            0 :         if( m_observing )
     553              :         {
     554            0 :             timePointT                          ct      = std::chrono::steady_clock::now();
     555            0 :             const std::chrono::duration<double> obstime = ct - m_obsStartTime;
     556            0 :             m_tgtTime                                   = ct - m_tgtStartTime;
     557              : 
     558            0 :             double obsang = mx::math::angleDiff<mx::math::degreesT<double>>( m_parang, m_obsStartParang );
     559            0 :             m_tgtAng      = mx::math::angleDiff<mx::math::degreesT<double>>( m_parang, m_tgtStartParang );
     560              : 
     561            0 :             updateSwitchIfChanged( m_indiP_observing, "toggle", pcf::IndiElement::On, INDI_OK );
     562            0 :             updatesIfChanged<double>(
     563            0 :                 m_indiP_obsTime, { "observation", "target" }, { obstime.count(), m_tgtTime.count() } );
     564              : 
     565            0 :             updatesIfChanged<double>( m_indiP_obsAngle, { "observation", "target" }, { obsang, m_tgtAng } );
     566            0 :             updatesIfChanged<std::string>(
     567            0 :                 m_indiP_obsStart, { "observation", "target" }, { m_obsStartTimeStamp, m_tgtStartTimeStamp } );
     568              : 
     569            0 :             if( m_obsDuration > 0.0 && obstime.count() > m_obsDuration )
     570              :             {
     571            0 :                 stopObserving();
     572              :             }
     573              :         }
     574              :         else
     575              :         {
     576            0 :             updateSwitchIfChanged( m_indiP_observing, "toggle", pcf::IndiElement::Off, INDI_IDLE );
     577            0 :             updatesIfChanged<std::string>( m_indiP_obsStart, { "observation", "target" }, { "", m_tgtStartTimeStamp } );
     578            0 :             updatesIfChanged<double>( m_indiP_obsTime, { "observation", "target" }, { 0.0, m_tgtTime.count() } );
     579            0 :             updatesIfChanged<double>( m_indiP_obsAngle, { "observation", "target" }, { 0.0, m_tgtAng } );
     580              :         }
     581              :     }
     582              : 
     583            0 :     TELEMETER_APP_LOGIC;
     584              : 
     585            0 :     return 0;
     586            0 : }
     587              : 
     588            0 : int observerCtrl::appShutdown()
     589              : {
     590            0 :     TELEMETER_APP_SHUTDOWN;
     591              : 
     592            0 :     return 0;
     593              : }
     594              : 
     595            0 : void observerCtrl::startObserving()
     596              : {
     597            0 :     for( size_t n = 0; n < m_streamWriters.size(); ++n )
     598              :     {
     599            0 :         if( m_indiP_sws[m_streamWriters[n]].getSwitchState() == pcf::IndiElement::On )
     600              :         {
     601            0 :             pcf::IndiProperty ip( pcf::IndiProperty::Switch );
     602              : 
     603            0 :             ip.setDevice( m_streamWriters[n] + "-sw" );
     604            0 :             ip.setName( "writing" );
     605            0 :             ip.add( pcf::IndiElement( "toggle" ) );
     606            0 :             ip["toggle"].setSwitchState( pcf::IndiElement::On );
     607              : 
     608            0 :             sendNewProperty( ip );
     609            0 :         }
     610              :     }
     611              : 
     612            0 :     mx::sys::sleep( 1 );
     613              : 
     614            0 :     m_obsStartTime      = std::chrono::steady_clock::now();
     615            0 :     m_obsStartTimeStamp = timeStampAsISO8601( std::chrono::system_clock::now() );
     616            0 :     m_obsStartParang    = m_parang;
     617              : 
     618            0 :     if( m_newTargetBlock || m_labMode ) // We always reset if in lab mode
     619              :     {
     620            0 :         m_tgtStartTime      = m_obsStartTime;
     621            0 :         m_tgtStartTimeStamp = m_obsStartTimeStamp;
     622            0 :         m_tgtStartParang    = m_obsStartParang;
     623              : 
     624            0 :         m_newTargetBlock = false;
     625              :     }
     626              : 
     627            0 :     m_observing = true;
     628            0 :     recordObserver( true );
     629            0 : }
     630              : 
     631            0 : void observerCtrl::stopObserving()
     632              : {
     633            0 :     m_observing = false;
     634            0 :     recordObserver( true );
     635              : 
     636            0 :     for( size_t n = 0; n < m_streamWriters.size(); ++n )
     637              :     {
     638            0 :         if( m_indiP_sws[m_streamWriters[n]].getSwitchState() == pcf::IndiElement::On )
     639              :         {
     640            0 :             pcf::IndiProperty ip( pcf::IndiProperty::Switch );
     641              : 
     642            0 :             ip.setDevice( m_streamWriters[n] + "-sw" );
     643            0 :             ip.setName( "writing" );
     644            0 :             ip.add( pcf::IndiElement( "toggle" ) );
     645            0 :             ip["toggle"].setSwitchState( pcf::IndiElement::Off );
     646              : 
     647            0 :             sendNewProperty( ip );
     648            0 :         }
     649              :     }
     650            0 : }
     651              : 
     652            3 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_observers )( const pcf::IndiProperty &ipRecv )
     653              : {
     654              : 
     655            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_observers, ipRecv );
     656              : 
     657              :     // look for selected mode switch which matches a known mode.  Make sure only one is selected.
     658              :     std::string newEmail = "";
     659              :     for( auto it = m_observers.begin(); it != m_observers.end(); ++it )
     660              :     {
     661              :         if( !ipRecv.find( it->second.m_sanitizedEmail ) )
     662              :             continue;
     663              : 
     664              :         if( ipRecv[it->second.m_sanitizedEmail].getSwitchState() == pcf::IndiElement::On )
     665              :         {
     666              :             if( newEmail != "" )
     667              :             {
     668              :                 log<text_log>( "More than one observer selected", logPrio::LOG_ERROR );
     669              :                 return -1;
     670              :             }
     671              : 
     672              :             newEmail = it->first;
     673              :         }
     674              :     }
     675              : 
     676              :     if( newEmail == "" )
     677              :     {
     678              :         std::cerr << "nothing\n";
     679              :         return 0;
     680              :     }
     681              : 
     682              :     {
     683              :         std::unique_lock<std::mutex> lock( m_indiMutex );
     684              : 
     685              :         m_currentObserver = m_observers[newEmail];
     686              : 
     687              :         for( auto it = m_observers.begin(); it != m_observers.end(); ++it )
     688              :         {
     689              :             if( it->first == m_currentObserver.m_sanitizedEmail )
     690              :             {
     691              :                 updateSwitchIfChanged(
     692              :                     m_indiP_observers, it->second.m_sanitizedEmail, pcf::IndiElement::On, INDI_IDLE );
     693              :             }
     694              :             else
     695              :             {
     696              :                 updateSwitchIfChanged(
     697              :                     m_indiP_observers, it->second.m_sanitizedEmail, pcf::IndiElement::Off, INDI_IDLE );
     698              :             }
     699              :         }
     700              :     }
     701              : 
     702              :     log<logger::observer>( { m_currentObserver.m_fullName,
     703              :                              m_currentObserver.m_pfoa,
     704              :                              m_currentObserver.m_email,
     705              :                              m_currentObserver.m_institution } );
     706              : 
     707              :     return 0;
     708              : }
     709              : 
     710            0 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_operators )( const pcf::IndiProperty &ipRecv )
     711              : {
     712              : 
     713            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_operators, ipRecv );
     714              : 
     715              :     // look for selected mode switch which matches a known mode.  Make sure only one is selected.
     716              :     std::string newEmail = "";
     717              :     for( auto it = m_observers.begin(); it != m_observers.end(); ++it )
     718              :     {
     719              :         if( !ipRecv.find( it->second.m_sanitizedEmail ) )
     720              :             continue;
     721              : 
     722              :         if( ipRecv[it->second.m_sanitizedEmail].getSwitchState() == pcf::IndiElement::On )
     723              :         {
     724              :             if( newEmail != "" )
     725              :             {
     726              :                 log<text_log>( "More than one operator selected", logPrio::LOG_ERROR );
     727              :                 return -1;
     728              :             }
     729              : 
     730              :             newEmail = it->first;
     731              :         }
     732              :     }
     733              : 
     734              :     if( newEmail == "" )
     735              :     {
     736              :         std::cerr << "nothing\n";
     737              :         return 0;
     738              :     }
     739              : 
     740              :     {
     741              :         std::unique_lock<std::mutex> lock( m_indiMutex );
     742              : 
     743              :         m_currentOperator = m_observers[newEmail];
     744              : 
     745              :         for( auto it = m_observers.begin(); it != m_observers.end(); ++it )
     746              :         {
     747              :             if( it->first == m_currentOperator.m_sanitizedEmail )
     748              :             {
     749              :                 updateSwitchIfChanged(
     750              :                     m_indiP_operators, it->second.m_sanitizedEmail, pcf::IndiElement::On, INDI_IDLE );
     751              :             }
     752              :             else
     753              :             {
     754              :                 updateSwitchIfChanged(
     755              :                     m_indiP_operators, it->second.m_sanitizedEmail, pcf::IndiElement::Off, INDI_IDLE );
     756              :             }
     757              :         }
     758              :     }
     759              : 
     760              :     log<logger::ao_operator>( { m_currentOperator.m_fullName,
     761              :                                 m_currentOperator.m_pfoa,
     762              :                                 m_currentOperator.m_email,
     763              :                                 m_currentOperator.m_institution } );
     764              : 
     765              :     return 0;
     766              : }
     767              : 
     768            0 : std::string observerCtrl::timeStampAsISO8601( const std::chrono::time_point<std::chrono::system_clock> &tp )
     769              : {
     770              :     // Convert to time_t for whole seconds
     771            0 :     std::time_t t  = std::chrono::system_clock::to_time_t( tp );
     772            0 :     std::tm     tm = *std::gmtime( &t );
     773              : 
     774              :     // Get nanoseconds
     775            0 :     auto duration = tp.time_since_epoch();
     776            0 :     auto seconds  = std::chrono::duration_cast<std::chrono::seconds>( duration );
     777            0 :     auto nanos    = std::chrono::duration_cast<std::chrono::nanoseconds>( duration - seconds ).count();
     778              : 
     779              :     // Format ISO 8601 with nanoseconds
     780            0 :     std::stringstream ss;
     781            0 :     ss << std::put_time( &tm, "%Y-%m-%dT%H:%M:%S" );
     782            0 :     ss << '.' << std::setw( 9 ) << std::setfill( '0' ) << nanos << "Z"; // Z for UTC
     783              : 
     784            0 :     return ss.str();
     785            0 : }
     786              : 
     787            3 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_obsName )( const pcf::IndiProperty &ipRecv )
     788              : {
     789            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_obsName, ipRecv );
     790              : 
     791              :     std::string target;
     792              : 
     793              :     std::unique_lock<std::mutex> lock( m_indiMutex );
     794              : 
     795              :     if( indiTargetUpdate( m_indiP_obsName, target, ipRecv, true ) < 0 )
     796              :     {
     797              :         log<software_error>( { __FILE__, __LINE__ } );
     798              :         return -1;
     799              :     }
     800              : 
     801              :     m_obsName = target;
     802              : 
     803              :     return 0;
     804              : }
     805              : 
     806            3 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_observing )( const pcf::IndiProperty &ipRecv )
     807              : {
     808            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_observing, ipRecv );
     809              : 
     810              :     if( !ipRecv.find( "toggle" ) )
     811              :     {
     812              :         return 0;
     813              :     }
     814              : 
     815              :     recordObserver( true );
     816              :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
     817              :     {
     818              :         std::unique_lock<std::mutex> lock( m_indiMutex );
     819              :         startObserving();
     820              :         updateSwitchIfChanged( m_indiP_observing, "toggle", pcf::IndiElement::On, INDI_OK );
     821              :     }
     822              :     else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
     823              :     {
     824              :         std::unique_lock<std::mutex> lock( m_indiMutex );
     825              :         stopObserving();
     826              :         updateSwitchIfChanged( m_indiP_observing, "toggle", pcf::IndiElement::Off, INDI_IDLE );
     827              :     }
     828              : 
     829              :     return 0;
     830              : }
     831              : 
     832            0 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_obsDuration )( const pcf::IndiProperty &ipRecv )
     833              : {
     834            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_obsDuration, ipRecv );
     835              : 
     836              :     return indiTargetUpdate( m_indiP_obsDuration, m_obsDuration, ipRecv, false );
     837              : }
     838              : 
     839            3 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_sws )( const pcf::IndiProperty &ipRecv )
     840              : {
     841            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_sws, ipRecv );
     842              : 
     843              :     if( m_observing == true )
     844              :     {
     845              :         log<text_log>( { "Can't change stream writers while observing" }, logPrio::LOG_WARNING );
     846              :         return 0;
     847              :     }
     848              : 
     849              :     for( size_t n = 0; n < m_streamWriters.size(); ++n )
     850              :     {
     851              :         if( !ipRecv.find( m_streamWriters[n] ) )
     852              :         {
     853              :             continue;
     854              :         }
     855              : 
     856              :         std::unique_lock<std::mutex> lock( m_indiMutex );
     857              :         updateSwitchIfChanged( m_indiP_sws, m_streamWriters[n], ipRecv[m_streamWriters[n]].getSwitchState() );
     858              :     }
     859              : 
     860              :     return 0;
     861              : }
     862              : 
     863            0 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_userlog )( const pcf::IndiProperty &ipRecv )
     864              : {
     865            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_userlog, ipRecv );
     866              : 
     867              :     std::string email;
     868              :     std::string message;
     869              : 
     870              :     timespecX ts{};
     871              : 
     872              :     if( ipRecv.find( "email" ) )
     873              :     {
     874              :         email = ipRecv["email"].get();
     875              :     }
     876              : 
     877              :     if( ipRecv.find( "message" ) )
     878              :     {
     879              :         message = ipRecv["message"].get();
     880              :     }
     881              : 
     882              :     if( message == "" )
     883              :     {
     884              :         return 0;
     885              :     }
     886              : 
     887              :     if( email == "" )
     888              :     {
     889              :         email = m_currentObserver.m_email;
     890              :     }
     891              : 
     892              :     if( ipRecv.find( "time_s" ) )
     893              :     {
     894              :         ts.time_s = ipRecv["time_s"].get<flatlogs::secT>();
     895              :     }
     896              : 
     897              :     if( ipRecv.find( "time_ns" ) )
     898              :     {
     899              :         ts.time_ns = ipRecv["time_ns"].get<flatlogs::nanosecT>();
     900              :     }
     901              : 
     902              :     if( ts.time_s != 0 )
     903              :     {
     904              :         m_log.template log<user_log>( ts, { email, message }, logPrio::LOG_INFO );
     905              :     }
     906              :     else
     907              :     {
     908              :         log<user_log>( { email, message } );
     909              :     }
     910              : 
     911              :     return 0;
     912              : }
     913              : 
     914            0 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_resetTarget )( const pcf::IndiProperty &ipRecv )
     915              : {
     916            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_resetTarget, ipRecv );
     917              : 
     918              :     if( !ipRecv.find( "request" ) )
     919              :     {
     920              :         return 0;
     921              :     }
     922              : 
     923              :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
     924              :     {
     925              :         if( m_observing )
     926              :         {
     927              :             m_tgtStartTime   = m_obsStartTime;
     928              :             m_tgtStartParang = m_obsStartParang;
     929              :         }
     930              :         else
     931              :         {
     932              :             m_newTargetBlock = true;
     933              :         }
     934              :     }
     935              : 
     936              :     return 0;
     937              : }
     938              : 
     939            0 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_target )( const pcf::IndiProperty &ipRecv )
     940              : {
     941            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_target, ipRecv );
     942              : 
     943              :     std::string target;
     944              :     if( indiTargetUpdate( m_indiP_target, target, ipRecv ) < 0 )
     945              :     {
     946              :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     947              :     }
     948              : 
     949              :     if( target != m_target )
     950              :     {
     951              :         if( m_observing )
     952              :         {
     953              :             m_tgtStartTime   = m_obsStartTime;
     954              :             m_tgtStartParang = m_obsStartParang;
     955              :         }
     956              :         else
     957              :         {
     958              :             m_newTargetBlock = true;
     959              :         }
     960              : 
     961              :         m_target    = target;
     962              :         m_newTarget = true;
     963              : 
     964              :         log<text_log>( "Target updated by observer to " + m_target, logPrio::LOG_NOTICE );
     965              : 
     966              :         std::unique_lock<std::mutex> lock( m_indiMutex );
     967              :         updatesIfChanged<std::string>( m_indiP_target, { "current", "target" }, { m_target, m_target } );
     968              :     }
     969              : 
     970              :     return 0;
     971              : }
     972              : 
     973            0 : INDI_NEWCALLBACK_DEFN( observerCtrl, m_indiP_tcsTarget )( const pcf::IndiProperty &ipRecv )
     974              : {
     975            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_tcsTarget, ipRecv );
     976              : 
     977              :     if( !ipRecv.find( "request" ) )
     978              :     {
     979              :         return 0;
     980              :     }
     981              : 
     982              :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
     983              :     {
     984              :         if( m_target != m_catObj )
     985              :         {
     986              :             if( m_observing )
     987              :             {
     988              :                 m_tgtStartTime   = m_obsStartTime;
     989              :                 m_tgtStartParang = m_obsStartParang;
     990              :             }
     991              :             else
     992              :             {
     993              :                 m_newTargetBlock = true;
     994              :             }
     995              : 
     996              :             m_target    = m_catObj;
     997              :             m_newTarget = true;
     998              : 
     999              :             log<text_log>( "Target updated by observer to TCS target: " + m_target, logPrio::LOG_NOTICE );
    1000              :             updatesIfChanged<std::string>( m_indiP_target, { "current", "target" }, { m_target, m_target } );
    1001              :         }
    1002              :     }
    1003              : 
    1004              :     return 0;
    1005              : }
    1006              : 
    1007            0 : INDI_SETCALLBACK_DEFN( observerCtrl, m_indiP_catalog )( const pcf::IndiProperty &ipRecv )
    1008              : {
    1009            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_catalog, ipRecv );
    1010              : 
    1011              :     if( !ipRecv.find( "object" ) )
    1012              :     {
    1013              :         return -1;
    1014              :     }
    1015              : 
    1016              :     std::string object = ipRecv["object"].get();
    1017              : 
    1018              :     if( object != m_catObj )
    1019              :     {
    1020              :         m_catObj    = object;
    1021              :         //m_target    = object;
    1022              :         //m_newTarget = true;
    1023              : 
    1024              :         // Always log change in name (different from RA and DEC)
    1025              :         log<text_log>( "TCS target updated to " + m_catObj, logPrio::LOG_NOTICE );
    1026              : 
    1027              :         //std::unique_lock<std::mutex> lock( m_indiMutex );
    1028              :         //updatesIfChanged<std::string>( m_indiP_target, { "current", "target" }, { m_target, m_target } );
    1029              :     }
    1030              : 
    1031              :     return 0;
    1032              : }
    1033              : 
    1034            0 : INDI_SETCALLBACK_DEFN( observerCtrl, m_indiP_catdata )( const pcf::IndiProperty &ipRecv )
    1035              : {
    1036            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_catdata, ipRecv );
    1037              : 
    1038              :     bool change = false;
    1039              :     if( ipRecv.find( "ra" ) )
    1040              :     {
    1041              :         std::string ra = ipRecv["ra"].get();
    1042              : 
    1043              :         if( ra != m_catRA )
    1044              :         {
    1045              :             m_catRA  = ra;
    1046              :             //m_target = m_catObj;
    1047              : 
    1048              :             if( !m_newTarget ) // Only log if not already new
    1049              :             {
    1050              :                 change      = true;
    1051              :                 m_newTarget = true;
    1052              :             }
    1053              : 
    1054              :             /*std::unique_lock<std::mutex> lock( m_indiMutex );
    1055              :             updatesIfChanged<std::string>( m_indiP_target, { "current", "target" }, { m_target, m_target } );*/
    1056              :         }
    1057              :     }
    1058              : 
    1059              :     if( ipRecv.find( "dec" ) )
    1060              :     {
    1061              :         std::string dec = ipRecv["dec"].get();
    1062              : 
    1063              :         if( dec != m_catDec )
    1064              :         {
    1065              :             m_catDec = dec;
    1066              :             //m_target = m_catObj;
    1067              : 
    1068              :             if( !m_newTarget ) // Only log if not already new
    1069              :             {
    1070              :                 change      = true;
    1071              :                 m_newTarget = true;
    1072              :             }
    1073              : 
    1074              :             /*std::unique_lock<std::mutex> lock( m_indiMutex );
    1075              :             updatesIfChanged<std::string>( m_indiP_target, { "current", "target" }, { m_target, m_target } );*/
    1076              :         }
    1077              :     }
    1078              : 
    1079              :     if( change ) // Only log if not already new
    1080              :     {
    1081              :         log<text_log>( "Pointing change.  Probable target change.", logPrio::LOG_NOTICE );
    1082              :     }
    1083              : 
    1084              :     return 0;
    1085              : }
    1086              : 
    1087            0 : INDI_SETCALLBACK_DEFN( observerCtrl, m_indiP_teldata )( const pcf::IndiProperty &ipRecv )
    1088              : {
    1089            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_teldata, ipRecv );
    1090              : 
    1091              :     if( ipRecv.find( "pa" ) )
    1092              :     {
    1093              :         m_parang = ipRecv["pa"].get<double>();
    1094              :     }
    1095              : 
    1096              :     return 0;
    1097              : }
    1098              : 
    1099            0 : INDI_SETCALLBACK_DEFN( observerCtrl, m_indiP_labMode )( const pcf::IndiProperty &ipRecv )
    1100              : {
    1101            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_labMode, ipRecv );
    1102              : 
    1103              :     if( ipRecv.find( "toggle" ) )
    1104              :     {
    1105              :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    1106              :         {
    1107              :             m_labMode = true;
    1108              :         }
    1109              :         else
    1110              :         {
    1111              :             m_labMode        = false;
    1112              :         }
    1113              :     }
    1114              : 
    1115              :     std::cerr << "got labmode: " << m_labMode << '\n';
    1116              :     return 0;
    1117              : }
    1118              : 
    1119            0 : INDI_SETCALLBACK_DEFN( observerCtrl, m_indiP_loop )( const pcf::IndiProperty &ipRecv )
    1120              : {
    1121            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_loop, ipRecv );
    1122              : 
    1123              :     if( ipRecv.find( "toggle" ) )
    1124              :     {
    1125              :         if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    1126              :         {
    1127              :             //Now that we're manually synch-ing the target name, we don't need loop closed logic
    1128              :             //i.e. the target block starts when the observation starts after setting the name.
    1129              :             /*if( m_newTarget == true && !m_loop )
    1130              :             {
    1131              :                 m_newTargetBlock = true;
    1132              :                 m_newTarget      = false;
    1133              :             }*/
    1134              : 
    1135              :             m_loop = true;
    1136              :         }
    1137              :         else
    1138              :         {
    1139              :             m_loop = false;
    1140              :         }
    1141              :     }
    1142              : 
    1143              :     return 0;
    1144              : }
    1145              : 
    1146            0 : inline int observerCtrl::checkRecordTimes()
    1147              : {
    1148            0 :     return telemeter<observerCtrl>::checkRecordTimes( telem_observer() );
    1149              : }
    1150              : 
    1151            0 : inline int observerCtrl::recordTelem( const telem_observer * )
    1152              : {
    1153            0 :     return recordObserver( true );
    1154              : }
    1155              : 
    1156            0 : inline int observerCtrl::recordObserver( bool force )
    1157              : {
    1158            0 :     static std::string last_email;
    1159            0 :     static std::string last_obsName;
    1160              :     static bool        last_observing;
    1161            0 :     static std::string last_target;
    1162            0 :     static std::string last_operator;
    1163              : 
    1164            0 :     if( last_email != m_currentObserver.m_email || last_obsName != m_obsName || last_observing != m_observing ||
    1165            0 :         last_target != m_target || last_operator != m_currentOperator.m_email || force )
    1166              :     {
    1167            0 :         telem<telem_observer>(
    1168            0 :             { m_currentObserver.m_email, m_obsName, m_observing, m_target, m_currentOperator.m_email } );
    1169              : 
    1170            0 :         last_email     = m_currentObserver.m_email;
    1171            0 :         last_obsName   = m_obsName;
    1172            0 :         last_observing = m_observing;
    1173            0 :         last_target    = m_target;
    1174            0 :         last_operator  = m_currentOperator.m_email;
    1175              :     }
    1176              : 
    1177            0 :     return 0;
    1178              : }
    1179              : 
    1180              : } // namespace app
    1181              : } // namespace MagAOX
    1182              : 
    1183              : #endif // observerCtrl_hpp
        

Generated by: LCOV version 2.0-1