7#ifndef observerCtrl_hpp
8#define observerCtrl_hpp
11#include <mx/math/geo.hpp>
13#include "../../libMagAOX/libMagAOX.hpp"
14#include "../../magaox_git_version.h"
48 typedef std::chrono::time_point<std::chrono::steady_clock>
timePointT;
54 const std::chrono::time_point<std::chrono::system_clock> &
tp );
180 pcf::IndiElement::SwitchStateType
state );
305 const pcf::IndiProperty &
ipRecv );
408 pcf::IndiProperty
ip( pcf::IndiProperty::Switch );
411 ip.setName(
"writing" );
412 ip.add( pcf::IndiElement(
"toggle" ) );
413 ip[
"toggle"].setSwitchState(
state );
420 config.add(
"stream.writers",
428 "The device names of the stream writers available for user selection." );
430 config.add(
"stream.defWriters",
438 "The device names of the stream writers always controlled for each observation." );
467 _config.configUnused( pfoa, mx::app::iniFile::makeKey(
sections[
i],
"pfoa" ) );
477 std::string fullName;
478 _config.configUnused( fullName, mx::app::iniFile::makeKey(
sections[
i],
"full_name" ) );
480 std::string institution;
481 _config.configUnused( institution, mx::app::iniFile::makeKey(
sections[
i],
"institution" ) );
484 for(
size_t n = 0;
n < email.size(); ++
n )
486 if( email[
n] ==
'@' )
490 else if( email[
n] ==
'.' )
526 std::vector<std::string>
emails;
530 emails.push_back(
it->second.m_email );
543 if(
it.first.find(
"jrmales" ) != std::string::npos )
566 if(
it.first.find(
"jrmales" ) != std::string::npos )
586 indi::addNumberElement<double>(
m_indiP_obsTime,
"observation", 0, 14400, 0.1,
"%0.1f",
"Current Obs" );
587 indi::addNumberElement<double>(
m_indiP_obsTime,
"target", 0, 14400, 0.1,
"%0.1f",
"Target" );
594 indi::addNumberElement<double>(
m_indiP_obsAngle,
"observation", 0, 360, 0.1,
"%0.1f",
"Current Obs" );
595 indi::addNumberElement<double>(
m_indiP_obsAngle,
"target", 0, 360, 0.1,
"%0.1f",
"Target" );
611 m_indiP_sws = pcf::IndiProperty( pcf::IndiProperty::Switch );
614 m_indiP_sws.setPerm( pcf::IndiProperty::ReadWrite );
616 m_indiP_sws.setRule( pcf::IndiProperty::AnyOfMany );
674 if(
lock.owns_lock() )
677 {
"full_name",
"email",
"pfoa",
"pronunciation",
"institution" },
699 {
"full_name",
"email",
"pfoa",
"pronunciation",
"institution" },
840 if( !
ipRecv.hasValidDevice() || !
ipRecv.find(
"toggle" ) )
868 std::string newEmail =
"";
869 for(
auto it = m_observers.begin();
it != m_observers.end(); ++
it )
871 if( !
ipRecv.find(
it->second.m_sanitizedEmail ) )
874 if(
ipRecv[
it->second.m_sanitizedEmail].getSwitchState() == pcf::IndiElement::On )
882 newEmail =
it->first;
888 std::cerr <<
"nothing\n";
893 std::unique_lock<std::mutex>
lock( m_indiMutex );
895 m_currentObserver = m_observers[newEmail];
897 for(
auto it = m_observers.begin();
it != m_observers.end(); ++
it )
899 if(
it->first == m_currentObserver.m_sanitizedEmail )
901 updateSwitchIfChanged(
902 m_indiP_observers,
it->second.m_sanitizedEmail, pcf::IndiElement::On,
INDI_IDLE );
906 updateSwitchIfChanged(
907 m_indiP_observers,
it->second.m_sanitizedEmail, pcf::IndiElement::Off,
INDI_IDLE );
912 log<logger::observer>( { m_currentObserver.m_fullName,
913 m_currentObserver.m_pfoa,
914 m_currentObserver.m_email,
915 m_currentObserver.m_institution } );
926 std::string newEmail =
"";
927 for(
auto it = m_observers.begin();
it != m_observers.end(); ++
it )
929 if( !
ipRecv.find(
it->second.m_sanitizedEmail ) )
932 if(
ipRecv[
it->second.m_sanitizedEmail].getSwitchState() == pcf::IndiElement::On )
940 newEmail =
it->first;
946 std::cerr <<
"nothing\n";
951 std::unique_lock<std::mutex>
lock( m_indiMutex );
953 m_currentOperator = m_observers[newEmail];
955 for(
auto it = m_observers.begin();
it != m_observers.end(); ++
it )
957 if(
it->first == m_currentOperator.m_sanitizedEmail )
959 updateSwitchIfChanged(
960 m_indiP_operators,
it->second.m_sanitizedEmail, pcf::IndiElement::On,
INDI_IDLE );
964 updateSwitchIfChanged(
965 m_indiP_operators,
it->second.m_sanitizedEmail, pcf::IndiElement::Off,
INDI_IDLE );
970 log<logger::ao_operator>( { m_currentOperator.m_fullName,
971 m_currentOperator.m_pfoa,
972 m_currentOperator.m_email,
973 m_currentOperator.m_institution } );
981 std::time_t t = std::chrono::system_clock::to_time_t(
tp );
982 std::tm
tm = *std::gmtime( &t );
985 auto duration =
tp.time_since_epoch();
986 auto seconds = std::chrono::duration_cast<std::chrono::seconds>( duration );
987 auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>( duration -
seconds ).count();
990 std::stringstream
ss;
991 ss << std::put_time( &
tm,
"%Y-%m-%dT%H:%M:%S" );
992 ss <<
'.' << std::setw( 9 ) << std::setfill(
'0' ) <<
nanos <<
"Z";
1003 std::unique_lock<std::mutex>
lock( m_indiMutex );
1005 if( indiTargetUpdate( m_indiP_obsName,
target,
ipRecv,
true ) < 0 )
1007 log<software_error>( { __FILE__, __LINE__ } );
1020 if( !
ipRecv.find(
"toggle" ) )
1025 recordObserver(
true );
1026 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1028 std::unique_lock<std::mutex>
lock( m_indiMutex );
1030 updateSwitchIfChanged( m_indiP_observing,
"toggle", pcf::IndiElement::On,
INDI_OK );
1032 else if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
1034 std::unique_lock<std::mutex>
lock( m_indiMutex );
1036 updateSwitchIfChanged( m_indiP_observing,
"toggle", pcf::IndiElement::Off,
INDI_IDLE );
1046 return indiTargetUpdate( m_indiP_obsDuration, m_obsDuration,
ipRecv,
false );
1053 if( m_observing ==
true )
1059 for(
size_t n = 0; n < m_streamWriters.size(); ++n )
1061 if( !
ipRecv.find( m_streamWriters[n] ) )
1066 std::unique_lock<std::mutex>
lock( m_indiMutex );
1067 updateSwitchIfChanged( m_indiP_sws, m_streamWriters[n],
ipRecv[m_streamWriters[n]].getSwitchState() );
1078 std::string message;
1082 if(
ipRecv.find(
"email" ) )
1084 email =
ipRecv[
"email"].get();
1087 if(
ipRecv.find(
"message" ) )
1089 message =
ipRecv[
"message"].get();
1099 email = m_currentObserver.m_email;
1102 if(
ipRecv.find(
"time_s" ) )
1107 if(
ipRecv.find(
"time_ns" ) )
1112 if( ts.time_s != 0 )
1118 log<user_log>( { email, message } );
1128 if( !
ipRecv.find(
"request" ) )
1133 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
1137 m_tgtStartTime = m_obsStartTime;
1138 m_tgtStartParang = m_obsStartParang;
1142 m_newTargetBlock =
true;
1154 if( indiTargetUpdate( m_indiP_target,
target,
ipRecv ) < 0 )
1163 m_tgtStartTime = m_obsStartTime;
1164 m_tgtStartParang = m_obsStartParang;
1168 m_newTargetBlock =
true;
1176 std::unique_lock<std::mutex>
lock( m_indiMutex );
1177 updatesIfChanged<std::string>( m_indiP_target, {
"current",
"target" }, { m_target, m_target } );
1187 if( !
ipRecv.find(
"request" ) )
1192 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
1194 if( m_target != m_catObj )
1198 m_tgtStartTime = m_obsStartTime;
1199 m_tgtStartParang = m_obsStartParang;
1203 m_newTargetBlock =
true;
1206 m_target = m_catObj;
1209 log<text_log>(
"Target updated by observer to TCS target: " + m_target,
logPrio::LOG_NOTICE );
1210 updatesIfChanged<std::string>( m_indiP_target, {
"current",
"target" }, { m_target, m_target } );
1221 if( !
ipRecv.find(
"object" ) )
1226 std::string
object =
ipRecv[
"object"].get();
1228 if(
object != m_catObj )
1248 bool change =
false;
1249 if(
ipRecv.find(
"ra" ) )
1251 std::string ra =
ipRecv[
"ra"].get();
1269 if(
ipRecv.find(
"dec" ) )
1271 std::string dec =
ipRecv[
"dec"].get();
1273 if( dec != m_catDec )
1301 if(
ipRecv.find(
"pa" ) )
1303 m_parang =
ipRecv[
"pa"].get<
double>();
1313 if(
ipRecv.find(
"toggle" ) )
1315 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1325 std::cerr <<
"got labmode: " << m_labMode <<
'\n';
1333 if(
ipRecv.find(
"toggle" ) )
1335 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
The base-class for XWCTk applications.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::mutex m_indiMutex
Mutex for locking INDI communications.
std::string configName()
Get the config name.
int createStandardIndiSelectionSw(pcf::IndiProperty &prop, const std::string &name, const std::vector< std::string > &elements, const std::vector< std::string > &elementLabels, const std::string &label="", const std::string &group="")
Create a standard R/W INDI selection (one of many) switch with vector of elements and element labels.
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
int registerIndiPropertySet(pcf::IndiProperty &prop, const std::string &devName, const std::string &propName, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is monitored for updates from others.
The MagAO-X Observer Controller.
INDI_SETCALLBACK_DECL(observerCtrl, m_indiP_loop)
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_resetTarget)
pcf::IndiProperty m_indiP_observer
Text which contains the specifications of the current observer.
std::string m_pronunciation
Guide for the TTS to pronounced the pfoa (defaults to pfoa)
std::chrono::time_point< std::chrono::steady_clock > timePointT
pcf::IndiProperty m_indiP_obsName
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_obsDuration)
void stopObserving()
Stop the current observation and any stream writers owned by observerCtrl.
double m_parang
The current parallactic angle.
int recordTelem(const telem_observer *)
timePointT m_obsStartTime
The start time of the current observation.
pcf::IndiProperty m_indiP_resetTarget
Reset the target statistics.
std::string m_sanitizedEmail
Observer's email sanitized for use in INDI properties.
pcf::IndiProperty m_indiP_tcsTarget
Set the target to match TCS catObj.
int setCallBack_streamWriterWriting(const pcf::IndiProperty &ipRecv)
Handle remote stream writer writing property updates.
pcf::IndiProperty m_indiP_obsTime
Number tracking the elapsed time.
pcf::IndiProperty m_indiP_obsAngle
Number tracking the change in angle.
std::string m_labModeProp
durationT m_tgtTime
The current target time. Only updated while observing.
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_operators)
virtual int appLogic()
Implementation of the FSM for observerCtrl.
std::map< std::string, std::string > m_streamWriterDevices
Reverse lookup from remote stream writer device name to configured writer name.
INDI_SETCALLBACK_DECL(observerCtrl, m_indiP_labMode)
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_observing)
bool streamWriterSelected(const std::string &writerName) const
Return whether a stream writer is enabled for observation control.
std::string m_pfoa
Observer's preferred forma of address.
pcf::IndiProperty m_indiP_observers
Selection switch to allow selection of the observer.
int recordObserver(bool force=false)
std::string m_catRA
The latest catalog right ascension reported by the TCS.
std::string m_institution
The observer's institution.
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_sws)
pcf::IndiProperty m_indiP_obsStart
String timestamp indicating the start for target/observation.
double m_obsDuration
The desired duration of the observation. If 0 then until stopped.
pcf::IndiProperty m_indiP_labMode
Tracks whether TCS is in lab mode.
std::string m_catalogProp
std::string streamWriterDeviceName(const std::string &writerName) const
Return the INDI device name used for a configured stream writer.
~observerCtrl() noexcept
D'tor, declared and defined for noexcept.
double m_obsStartParang
The parallactic angle at the start of the observation.
std::string m_catdataProp
std::string m_catDec
The latest catalog declination reported by the TCS.
pcf::IndiProperty m_indiP_catalog
Catalog text data.
std::map< std::string, bool > m_streamWriterStartedByObserver
Tracks whether observerCtrl started each stream writer for the current observation.
std::chrono::duration< double > durationT
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_obsName)
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_observers)
INDI_SETCALLBACK_DECL(observerCtrl, m_indiP_catalog)
std::string m_loopStateProp
virtual void loadConfig()
Load the observerCtrl configuration.
dev::telemeter< observerCtrl > telemeterT
std::vector< std::string > m_defStreamWriters
The configured stream writers always managed for observations.
pcf::IndiProperty m_indiP_operator
Text which contains the specifications of the current observer.
std::vector< std::string > m_streamWriters
The configured stream writers available for user selection.
bool endObservationStreamWriter(const std::string &writerName)
Determine whether observerCtrl should stop a stream writer when an observation ends.
pcf::IndiProperty m_indiP_obsDuration
Number to set the desired duration of observation.
std::map< std::string, bool > m_streamWriterSelectable
Tracks whether each configured stream writer is user-selectable in INDI.
INDI_SETCALLBACK_DECL(observerCtrl, m_indiP_teldata)
std::map< std::string, pcf::IndiProperty > m_indiP_streamWriterWriting
Tracks the remote writing properties for configured stream writers.
int registerStreamWriter(const std::string &writerName, bool userSelectable)
Register one configured stream writer for remote writing-state tracking.
std::string m_fullName
Obsever's full name.
pcf::IndiProperty m_indiP_loop
Tracks the loop state.
pcf::IndiProperty m_indiP_target
The target name, which can be overridden by the user.
std::string m_target
The current target name shown to the observer.
bool beginObservationStreamWriter(const std::string &writerName)
Determine whether observerCtrl should start a stream writer for a new observation.
timeStampT m_obsStartTimeStamp
The UTC start time of the current observation.
pcf::IndiProperty m_indiP_operators
Selection switch to allow selection of the observer.
std::string m_teldataProp
virtual int appStartup()
Startup function.
bool m_observing
Flag indicating whether or not we are in an observation.
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_userlog)
double m_tgtStartParang
The parallactic angle at the start of observing the current target.
pcf::IndiProperty m_indiP_userlog
Text to enter a user log.
int commandStreamWriter(const std::string &writerName, pcf::IndiElement::SwitchStateType state)
Send a writing toggle command to a configured stream writer.
std::string timeStampAsISO8601(const std::chrono::time_point< std::chrono::system_clock > &tp)
Format a system-clock time point as an ISO 8601 UTC timestamp.
pcf::IndiProperty m_indiP_observing
Toggle switch to trigger observation.
observerCtrl()
Default c'tor.
void startObserving()
Start the current observation and any stream writers owned by observerCtrl.
pcf::IndiProperty m_indiP_catdata
Catalog numeric data.
std::string m_catObj
The latest catalog object name reported by the TCS.
INDI_SETCALLBACK_DECL(observerCtrl, m_indiP_catdata)
timePointT m_tgtStartTime
The start time of the current target.
double m_tgtAng
The current target angle. Only updated while observing.
virtual int appShutdown()
Shutdown the app.
std::map< std::string, bool > m_streamWriterWriting
Tracks the last known remote writing state for each configured stream writer.
static int st_setCallBack_streamWriterWriting(void *app, const pcf::IndiProperty &ipRecv)
Static wrapper for remote stream writer writing property updates.
bool m_labMode
Flag tracking whether the TCS interface is in lab mode.
bool m_loop
Flag tracking loop state. true is loop closed.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
std::string m_obsName
The name of the observation.
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_tcsTarget)
std::string m_email
Observer's email. Must be unique.
observerMapT m_observers
The observers from the configuration file.
INDI_NEWCALLBACK_DECL(observerCtrl, m_indiP_target)
friend class observerCtrl_test
observer m_currentObserver
The current selected observer.
timeStampT m_tgtStartTimeStamp
The UTC start time of the current target.
observer m_currentOperator
The current selected observer.
std::map< std::string, observer > observerMapT
pcf::IndiProperty m_indiP_sws
Selection to switch which user-managed stream writers are enabled.
pcf::IndiProperty m_indiP_teldata
Telescope data (for parang)
virtual void setupConfig()
Set up the observerCtrl configuration parameters.
std::map< std::string, bool > m_streamWriterWritingKnown
Tracks whether a remote writing state has been received for each configured stream writer.
The observer specification.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define CREATE_REG_INDI_NEW_TOGGLESWITCH(prop, name)
Create and register a NEW INDI property as a standard toggle switch, using the standard callback name...
#define CREATE_REG_INDI_NEW_REQUESTSWITCH(prop, name)
Create and register a NEW INDI property as a standard request switch, using the standard callback nam...
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
#define CREATE_REG_INDI_NEW_NUMBERD(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as double, using the standard callback n...
#define REG_INDI_NEWPROP_NOSETUP(prop)
Register a NEW INDI property with the class, using the standard callback name.
#define CREATE_REG_INDI_NEW_TEXT(prop, name, label, group)
Create and register a NEW INDI property as a standard text, using the standard callback name.
#define CREATE_REG_INDI_RO_NUMBER(prop, name, label, group)
Create and register a RO INDI property as a number, using the standard callback name.
uint32_t secT
The type used for seconds.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
int addTextElement(pcf::IndiProperty &prop, const std::string &name, const std::string &label="")
Add a standard INDI Text element.
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
uint32_t nanosecT
The type used for nanoseconds.
A device base class which saves telemetry.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
@ READY
The device is ready for operation, but is not operating.
Log entry recording the build-time git state.
A fixed-width timespec structure.
secT time_s
Time since the Unix epoch.
#define TELEMETER_APP_LOGIC
Call telemeter::appLogic with error checking.
#define TELEMETER_APP_STARTUP
Call telemeter::appStartup with error checking.
#define TELEMETER_APP_SHUTDOWN
Call telemeter::appShutdown with error checking.