API
MagAOXApp.hpp
Go to the documentation of this file.
1 /** \file magAOXApp.hpp
2  * \brief The basic MagAO-X Application
3  * \author Jared R. Males (jaredmales@gmail.com)
4  *
5  * History:
6  * - 2017-12-24 created by JRM
7  *
8  * \ingroup app_files
9  */
10 
11 #ifndef app_MagAOXApp_hpp
12 #define app_MagAOXApp_hpp
13 
14 #include <signal.h>
15 #include <sys/stat.h>
16 #include <sys/syscall.h>
17 
18 #include <cstdlib>
19 #include <fstream>
20 #include <sstream>
21 #include <thread>
22 #include <mutex>
23 
24 #include <unordered_map>
25 
26 #include <mx/mxlib.hpp>
27 #include <mx/app/application.hpp>
28 #include <mx/sys/environment.hpp>
29 #include <mx/sys/timeUtils.hpp>
30 #include <mx/ioutils/fileUtils.hpp>
31 
32 
33 #include "../common/environment.hpp"
34 #include "../common/paths.hpp"
35 #include "../common/defaults.hpp"
36 #include "../common/config.hpp"
37 
38 #include "../logger/logFileRaw.hpp"
39 #include "../logger/logManager.hpp"
40 
41 #include "../sys/thSetuid.hpp"
42 
43 #include "stateCodes.hpp"
44 #include "indiDriver.hpp"
45 #include "indiMacros.hpp"
46 #include "indiUtils.hpp"
47 
48 //#include "../../INDI/libcommon/System.hpp"
49 using namespace mx::app;
50 
51 using namespace MagAOX::logger;
52 
53 namespace MagAOX
54 {
55 namespace app
56 {
57 
58 /// The base-class for MagAO-X applications.
59 /**
60  * You can define a base configuration file for this class by defining
61  * \code
62  m_configBase = "base_name";
63  \endcode
64  * in the derived class constructor. This would be used, for instance to have a config common to
65  * all filter wheels.
66  *
67  *
68  * \todo implement libMagAOX error handling? (a stack?)
69  * \todo make m_powerMgtEnabled a template parameter, and static_assert checki if _useINDI== false and power management is true
70  *
71  * \ingroup magaoxapp
72  */
73 template< bool _useINDI = true >
74 class MagAOXApp : public application
75 {
76 
77  friend class MagAOXApp_test;
78 
79 public:
80 
81  ///The log manager type.
83 
84 protected:
85 
86  std::string MagAOXPath; ///< The base path of the MagAO-X system.
87 
88  std::string m_configName; ///< The name of the configuration file (minus .conf).
89 
90  std::string m_configDir; ///< The path to configuration files for MagAOX.
91 
92  std::string m_configBase; ///< The name of a base config class for this app (minus .conf).
93 
94  std::string m_calibDir; ///< The path to calibration files for MagAOX.
95 
96  std::string sysPath; ///< The path to the system directory, for PID file, etc.
97 
98  std::string secretsPath; ///< Path to the secrets directory, where passwords, etc, are stored.
99 
100  unsigned long m_loopPause {MAGAOX_default_loopPause}; ///< The time in nanoseconds to pause the main loop. The appLogic() function of the derived class is called every m_loopPause nanoseconds. Default is 1,000,000,000 ns. Config with loopPause=X.
101 
102  int m_shutdown {0}; ///< Flag to signal it's time to shutdown. When not 0, the main loop exits.
103 
104 private:
105 
106  ///Default c'tor is deleted.
107  MagAOXApp() = delete;
108 
109 public:
110 
111  /// Public c'tor. Handles uid, logs git repo status, and initializes static members.
112  /**
113  * Only one MagAOXApp can be instantiated per program. Hence this c'tor will issue exit(-1)
114  * if the static self-pointer m_self is already initialized.
115  *
116  * euid is set to 'real' to ensure that the application has normal privileges unless
117  * explicitly needed.
118  *
119  * Reference: http://man7.org/linux/man-pages/man2/getresuid.2.html
120  *
121  * The git repository status is required to create a MagAOXApp. Derived classes
122  * should include the results of running `gengithead.sh` and pass the defined
123  * sha1 and modified flags.
124  *
125  */
126  MagAOXApp( const std::string & git_sha1, ///< [in] The current SHA1 hash of the git repository
127  const bool git_modified ///< [in] Whether or not the repo is modified.
128  );
129 
130  ~MagAOXApp() noexcept(true);
131 
132  /// Get the value of the shutdown flag.
133  /**
134  * \returns the current value of m_shutdown
135  */
136  int shutdown();
137 
138  /// Set the paths for config files
139  /** Replaces the mx::application defaults with the MagAO-X config system.
140  *
141  * This function parses the CL for "-n" or "--name".
142  *
143  *
144  * Do not override this unless you intend to depart from the MagAO-X standard.
145  */
146  virtual void setDefaults( int argc, ///< [in] standard command line result specifying number of arguments in argv
147  char ** argv ///< [in] standard command line result containing the arguments.
148  );
149 
150  /// The basic MagAO-X configuration setup method. Should not normally be overridden.
151  /** This method sets up the config system with the standard MagAO-X key=value pairs.
152  *
153  * Though it is virtual, it should not normally be overridden unless you need
154  * to depart from the MagAO-X standard.
155  *
156  * Setting up app specific config goes in setupConfig() implemented in the derived class.
157  */
158  virtual void setupBasicConfig();
159 
160  /// The basic MagAO-X configuration processing method. Should not normally be overridden.
161  /** This method processes the standard MagAO-X key=value pairs.
162  *
163  * Though it is virtual, it should not normally be overridden unless you need
164  * to depart from the MagAO-X standard.
165  *
166  * Processing of app specific config goes in loadConfig() implemented by the derived class.
167  */
168  virtual void loadBasicConfig();
169 
170  /// Check for unused and unrecognized config options and settings.
171  /** Logs the unused targets as warnings. Unrecognized and unused options are logged as critical, and m_shutdown is set.
172  * Any command line argument (not an option) will also be critical and cause shutdown.
173  */
174  virtual void checkConfig();
175 
176  /// The execute method implementing the standard main loop. Should not normally be overridden.
177  /** Performs final startup steps. That is:
178  * - Verifies correct effective user-id by comparison to logs directory.
179  * - PID locking lockPID()
180  * - log thread startup by logThreadStart()
181  * - signal handling installation by setSigTermHandler()
182  * - appStartup() is called
183  * - INDI communications started by startINDI()
184  * - power state is checked, pausing if unknown (if being managed)
185  *
186  * Errors in the above steps will cause a process exit.
187  *
188  * Then commences the main event loop.
189  * Conditions on entry to the main loop:
190  * - PID locked
191  * - Log thread running
192  * - Signal handling installed
193  * - appStartup successful
194  * - INDI communications started successfully (if being used)
195  * - power state known (if being managed)
196  *
197  * In the event loop, the power state is checked (if being managed). If power is off, then onPowerOff is called.
198  * If power is on, or power is not managed, appLogic is called. These methods are implemented in derived classes, and
199  * are called every m_loopPause interval.
200  *
201  * If an error is returned by either onPowerOff or appLogic, or a signal is handled, then the shutdown is managed.
202  * This includes shutting down INDI, calling appShutdown, and unlocking the PID. The log thread will shutdown.
203  */
204  virtual int execute();
205 
206 
207  /** \name Pure Virtual Functions
208  * Derived applications must implement these.
209  * @{
210  */
211 
212  /// Any tasks to perform prior to the main event loop go here.
213  /** This is called after signal handling is installed. FSM state is
214  * stateCodes::INITIALIZED when this is called.
215  *
216  * Set m_shutdown = 1 on any fatal errors here.
217  */
218  virtual int appStartup() = 0;
219 
220  /// This is where derived applications implement their main FSM logic.
221  /** This will be called every m_loopPause nanoseconds until the application terminates.
222  *
223  * FSM state will be whatever it is on exti from appStartup.
224  *
225  * Should return -1 on an any unrecoverable errors which will caues app to terminate. Could also set m_shutdown=1.
226  * Return 0 on success, or at least intent to continue.
227  *
228  */
229  virtual int appLogic() = 0;
230 
231  /// Any tasks to perform after main loop exit go here.
232  /** Should be able to handle case where appStartup and/or appLogic have not run.
233  */
234  virtual int appShutdown() = 0;
235 
236  ///@} -- Pure Virtual Functions
237 
238  /** \name Logging
239  * @{
240  */
241 public:
242  static logManagerT m_log;
243 
244  /// Make a log entry
245  /** Wrapper for logManager::log
246  *
247  * \tparam logT the log entry type
248  * \tparam retval the value returned by this method.
249  *
250  */
251  template<typename logT, int retval=0>
252  static int log( const typename logT::messageT & msg, ///< [in] the message to log
253  logPrioT level = logPrio::LOG_DEFAULT ///< [in] [optional] the log level. The default is used if not specified.
254  );
255 
256  /// Make a log entry
257  /** Wrapper for logManager::log
258  *
259  * \tparam logT the log entry type
260  * \tparam retval the value returned by this method.
261  *
262  */
263  template<typename logT, int retval=0>
264  static int log( logPrioT level = logPrio::LOG_DEFAULT /**< [in] [optional] the log level. The default is used if not specified.*/);
265 
266  /// Handle a log message from the logging system
267  /** This is a callback from the logManager, and is called when the log thread is processing log entries.
268  *
269  * Decides whether to display to stderr and whether to send via INDI.
270  */
271  void logMessage( bufferPtrT & b );
272 
273 private:
274  /// Callback for config system logging.
275  /** Called by appConfigurator each time a value is set using the config() operator.
276  * You never need to call this directly.
277  */
278  static void configLog( const std::string & name, ///< [in] The name of the config value
279  const int & code, ///< [in] numeric code specifying the type
280  const std::string & value, ///< [in] the value read by the config system
281  const std::string & source ///< [in] the source of the value.
282  );
283 
284  ///@} -- logging
285 
286  /** \name Signal Handling
287  * @{libMagAOX/logger/types/software_log.hpp
288  */
289 private:
290 
291  static MagAOXApp * m_self; ///< Static pointer to this (set in constructor). Used to test whether a a MagAOXApp is already instatiated (a fatal error) and used for getting out of static signal handlers.
292 
293  ///Sets the handler for SIGTERM, SIGQUIT, and SIGINT.
294  int setSigTermHandler();
295 
296  ///The handler called when SIGTERM, SIGQUIT, or SIGINT is received. Just a wrapper for handlerSigTerm.
297  static void _handlerSigTerm( int signum, ///< [in] specifies the signal.
298  siginfo_t *siginf, ///< [in] ignored by MagAOXApp
299  void *ucont ///< [in] ignored by MagAOXApp
300  );
301 
302  ///Handles SIGTERM, SIGQUIT, and SIGINT. Sets m_shutdown to 1 and logs the signal.
303  void handlerSigTerm( int signum, ///< [in] specifies the signal.
304  siginfo_t *siginf, ///< [in] ignored by MagAOXApp
305  void *ucont ///< [in] ignored by MagAOXApp
306  );
307 
308  ///@} -- Signal Handling
309 
310  /** \name Privilege Management
311  * @{
312  */
313 private:
314  uid_t m_euidReal; ///< The real user id of the proces (i.e. the lower privileged id of the user)
315  uid_t m_euidCalled; ///< The user id of the process as called (i.e. the higher privileged id of the owner, root if setuid).
316  uid_t m_suid; ///< The save-set user id of the process
317 
318 protected:
319 
320  /// Internal class to manage setuid privilege escalation with RAII
321  /** Upon construction this elevates to the called user id, root in a setuid process.
322  * Restores privileges to real user id upon destruction (i.e. when it goes out of scope).
323  */
325  {
326  private:
328  bool m_elevated {false};
329 
330  public:
332  {
333  m_app = app;
334  elevate();
335  }
336 
337  void elevate()
338  {
339  if(m_elevated) return;
340 
341  m_app->setEuidCalled();
342  m_elevated = true;
343  }
344 
345  void restore()
346  {
347  if(!m_elevated) return;
348 
349  m_app->setEuidReal();
350  m_elevated = false;
351  }
352 
354  {
355  restore();
356  }
357  };
358 
359 private:
360  /// Set the effective user ID to the called value, i.e. the highest possible.
361  /** If setuid is set on the file, this will be super-user privileges.
362  *
363  * Reference: http://pubs.opengroup.org/onlinepubs/009695399/functions/seteuid.html
364  *
365  * \returns 0 on success
366  * \returns -1 on error from setuid().
367  */
369 
370  /// Set the effective user ID to the real value, i.e. the file owner.
371  /**
372  * Reference: http://pubs.opengroup.org/onlinepubs/009695399/functions/seteuid.html
373  *
374  * \returns 0 on success
375  * \returns -1 on error from setuid().
376  */
377  int setEuidReal();
378 
379  ///@} -- Privilege Management
380 
381 protected:
382 
383  /** \name PID Locking
384  *
385  * Each MagAOXApp has a PID lock file in the system directory. The app will not
386  * startup if it detects that the PID is already locked, preventing duplicates. This is
387  * based on the configured name, not the invoked name (argv[0]).
388  *
389  * @{
390  */
391 
392  std::string pidFileName; ///<The name of the PID file
393 
394  pid_t m_pid {0}; ///< This process's PID
395 
396  /// Attempt to lock the PID by writing it to a file. Fails if a process is already running with the same config name.
397  /** First checks the PID file for an existing PID. If found, interrogates /proc to determine if that process is
398  * running and if so if the command line matches. If a matching process is currently running, then this returns an error.
399  *
400  * Will not fail if a PID file exists but the stored PID does not correspond to a running process with the same command line name.
401  *
402  * Reference: https://linux.die.net/man/3/getpid
403  *
404  * \returns 0 on success.
405  * \returns -1 on any error, including creating the PID file or if this app is already running.
406  */
407  int lockPID();
408 
409  /// Remove the PID file.
410  int unlockPID();
411 
412  ///@} -- PID Locking
413 
414  /** \name cpusets
415  * The path to the cpusets mount is configured by the environment varialbe defined by MAGOX_env_cpuset
416  * in environment.hpp. This environment varialbe is normally named "CGROUPS1_CPUSET_MOUNTPOINT". If
417  * the environment variable is not set, the default defined by MAGAOX_cpusetPath in paths.hpp is used.
418  * This is normally "/opt/MagAOX/cpuset/"
419  */
420 
421  std::string m_cpusetPath {MAGAOX_cpusetPath};
422 
423  /** \name Threads
424  *
425  * @{
426  */
427 public:
428 
429  /// Start a thread, using this class's privileges to set priority, etc.
430  /**
431  * The thread initialization synchronizer `bool` is set to true at the beginning
432  * of this function, then is set to false once all initialization is complete. The
433  * thread exec function should wait until this is false before doing _anything_ except
434  * setting the pid. This is to avoid privilege escalation bugs.
435  *
436  * The interface of the thread start function is:
437  \code
438  static void impl::myThreadStart( impl * o )
439  {
440  o->myThreadExec(); //A member function which actually executes the thread
441  }
442  \endcode
443  * where `impl` is the derived class, and `mThreadStart` and `myThreadExec` are members
444  * of `impl`.
445  *
446  * \returns 0 on success
447  * \returns -1 on error
448  */
449  template<class thisPtr, class Function>
450  int threadStart( std::thread & thrd, ///< [out] The thread object to start executing
451  bool & thrdInit, ///< [in/out] The thread initilization synchronizer.
452  pid_t & tpid, ///< [in/out] The thread pid to be filled in by thrdStart immediately upon call
453  pcf::IndiProperty & thProp, ///< [in/out] The INDI property to publish the thread details
454  int thrdPrio, ///< [in] The r/t priority to set for this thread
455  const std::string & cpuset, ///< [in] the cpuset to place this thread on. Ignored if "".
456  const std::string & thrdName, ///< [in] The name of the thread (just for logging)
457  thisPtr * thrdThis, ///< [in] The `this` pointer to pass to the thread starter function
458  Function&& thrdStart ///< [in] The thread starting function, a static function taking a `this` pointer as argument.
459  );
460 
461  ///@} -- Threads
462 
463  /** \name Application State
464  *
465  * @{
466  */
467 private:
468  stateCodes::stateCodeT m_state {stateCodes::UNINITIALIZED}; ///< The application's state. Never ever set this directly, use state(const stateCodeT & s).
469 
470  bool m_stateAlert {false}; //Flag to control whether the FSM is in an alert state. Once set, only user acknowledgement can change this.
471 
472  bool m_gitAlert {false}; //Flag set if there is a git modified warning to alert on.
473 
474  int m_stateLogged {0} ;///< Counter and flag for use to log errors just once. Never ever access directly, use stateLogged().
475 
476 public:
477  /// Get the current state code
478  /** \returns m_state
479  */
481 
482  /// Set the current state code
483  /*
484  * If it is a change, the state change is logged. Also resets m_stateLogged to 0.
485  *
486  * Will also update INDI if there is a change.
487  */
488  void state( const stateCodes::stateCodeT & s, ///< [in] The new application state
489  bool stateAlert = false ///< [in] [optional] flag to set the alert state of the FSM property.
490  );
491 
492  /// Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change, >0 afterwards.
493  /** This method exists to facilitate logging the reason for a state change once, but not
494  * logging it on subsequent event loops. Returns the current value upon entry, but updates
495  * before returning so that the next call returns the incremented value. Example usage:
496  * \code
497  if( connection_failed ) //some condition set this to true
498  {
499  state( stateCodes::NOTCONNECTED );
500  if(!stateLogged()) log<text_log>("Not connected");
501  }
502  \endcode
503  * In this example, the log entry is made the first time the state changes. If there are no changes to a
504  * different state in the mean time, then when the event loop gets here again and decides it is not connected,
505  * the log entry will not be made.
506  *
507  * \returns current value of m_stateLogged, that is the value before it is incremented.
508  */
509  int stateLogged();
510 
511  ///@} --Application State
512 
513 private:
514 
515  /// Clear the FSM alert state.
516  /** This can only be done from within this class, and this
517  * should only be possible via user action via INDI.
518  */
520 
521  /** \name INDI Interface
522  *
523  * For reference: "Get" and "New" refer to properties we own. "Set" refers to properties owned by others.
524  * So we respond to GetProperties by listing our own properties, and NewProperty is a request to change
525  * a property we own. Whereas SetProperty is a notification that someone else has changed a property.
526  *
527  * @{
528  */
529 protected:
530 
531  ///Flag controlling whether INDI is used. If false, then no INDI code executes.
532  constexpr static bool m_useINDI = _useINDI;
533 
534 ///\todo instead of making this public, provide an accessor.
535 public:
536  ///The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communications.
537  indiDriver<MagAOXApp> * m_indiDriver {nullptr};
538 
539  ///Mutex for locking INDI communications.
540  std::mutex m_indiMutex;
541 
542 protected:
543  ///Structure to hold the call-back details for handling INDI communications.
545  {
546  pcf::IndiProperty * property {0}; ///< A pointer to an INDI property.
547  int (*callBack)( void *, const pcf::IndiProperty &) {0}; ///< The function to call for a new or set property.
548  bool m_defReceived {false}; ///< Flag indicating that a DefProperty has been received after a GetProperty.
549  };
550 
551 
552 public:
553 
554  ///Value type of the indiCallBack map.
555  typedef std::pair<std::string, indiCallBack> callBackValueType;
556 
557  ///Iterator type of the indiCallBack map.
558  typedef typename std::unordered_map<std::string, indiCallBack>::iterator callBackIterator;
559 
560  ///Return type of insert on the indiCallBack map.
561  typedef std::pair<callBackIterator,bool> callBackInsertResult;
562 
563 protected:
564  ///Map to hold the NewProperty indiCallBacks for this App, with fast lookup by property name.
565  /** The key for these is the property name.
566  */
567  std::unordered_map< std::string, indiCallBack> m_indiNewCallBacks;
568 
569  ///Map to hold the SetProperty indiCallBacks for this App, with fast lookup by property name.
570  /** The key for these is device.name
571  */
572  std::unordered_map< std::string, indiCallBack> m_indiSetCallBacks;
573 
574 protected:
575  ///Flag indicating that all registered Set properties have been updated since last Get.
576  bool m_allDefsReceived {false};
577 
578  ///Full path name of the INDI driver input FIFO.
579  std::string m_driverInName;
580 
581  ///Full path name of the INDI driver output FIFO.
582  std::string m_driverOutName;
583 
584  ///Full path name of the INDI driver control FIFO.
585  /** This is currently only used to signal restarts.
586  */
587  std::string m_driverCtrlName;
588 
589 public:
590 
591  /// Create a standard R/W INDI Text property with target and current elements.
592  /**
593  * \returns 0 on success
594  * \returns -1 on error
595  */
596  int createStandardIndiText( pcf::IndiProperty & prop, ///< [out] the property to create and setup
597  const std::string & propName, ///< [in] the name of the property
598  const std::string & label = "", ///< [in] [optional] the GUI label suggestion for this property
599  const std::string & group = "" ///< [in] [optional] the group for this property
600  );
601 
602  /// Create a standard ReadOnly INDI Text property, with at least one element.
603  /**
604  * \returns 0 on success
605  * \returns -1 on error
606  */
607  int createROIndiText( pcf::IndiProperty & prop, ///< [out] the property to create and setup
608  const std::string & propName, ///< [in] the name of the property
609  const std::string & elName, ///< [in] the name of the element
610  const std::string & propLabel = "", ///< [in] [optional] the GUI label suggestion for this property
611  const std::string & propGroup = "", ///< [in] [optional] the group for this property
612  const std::string & elLabel = "" ///< [in] [optional] the GUI label suggestion for the element
613  );
614 
615  /// Create a standard R/W INDI Number property with target and current elements.
616  /**
617  * \returns 0 on success
618  * \returns -1 on error
619  */
620  template<typename T>
621  int createStandardIndiNumber( pcf::IndiProperty & prop, ///< [out] the property to create and setup
622  const std::string & name, ///< [in] the name of the property
623  const T & min, ///< [in] the minimum value for the elements, applied to both target and current
624  const T & max, ///< [in] the minimum value for the elements, applied to both target and current
625  const T & step, ///< [in] the step size for the elements, applied to both target and current
626  const std::string & format, ///< [in] the _ value for the elements, applied to both target and current. Set to "" to use the MagAO-X standard for type.
627  const std::string & label = "", ///< [in] [optional] the GUI label suggestion for this property
628  const std::string & group = "" ///< [in] [optional] the group for this property
629  );
630 
631  /// Create a ReadOnly INDI Number property
632  /**
633  * \returns 0 on success
634  * \returns -1 on error
635  */
636  int createROIndiNumber( pcf::IndiProperty & prop, ///< [out] the property to create and setup
637  const std::string & propName, ///< [in] the name of the property
638  const std::string & propLabel = "", ///< [in] [optional] the GUI label suggestion for this property
639  const std::string & propGroup = "" ///< [in] [optional] the group for this property
640  );
641 
642  /// Create a standard R/W INDI switch with a single toggle element.
643  /** This switch is intended to function like an on/off toggle switch.
644  *
645  * \returns 0 on success
646  * \returns -1 on error
647  */
648  int createStandardIndiToggleSw( pcf::IndiProperty & prop, ///< [out] the property to create and setup
649  const std::string & name, ///< [in] the name of the property
650  const std::string & label = "", ///< [in] [optional] the GUI label suggestion for this property
651  const std::string & group = "" ///< [in] [optional] the group for this property
652  );
653 
654  /// Create a standard R/W INDI switch with a single request element.
655  /** This switch is intended to function like a momentary switch.
656  *
657  * \returns 0 on success
658  * \returns -1 on error
659  */
660  int createStandardIndiRequestSw( pcf::IndiProperty & prop, ///< [out] the property to create and setup
661  const std::string & name, ///< [in] the name of the property
662  const std::string & label = "", ///< [in] [optional] the GUI label suggestion for this property
663  const std::string & group = "" ///< [in] [optional] the group for this property
664  );
665 
666  /// Create a standard R/W INDI selection (one of many) switch with vector of elements and element labels
667  /** This switch is intended to function like drop down menu.
668  *
669  * \returns 0 on success
670  * \returns -1 on error
671  */
672  int createStandardIndiSelectionSw( pcf::IndiProperty & prop, ///< [out] the property to create and setup
673  const std::string & name, ///< [in] the name of the property,
674  const std::vector<std::string> & elements, ///< [in] the element names to give to the switches
675  const std::vector<std::string> & elementLabels, ///< [in] the element labels to give to the switches
676  const std::string & label = "", ///< [in] [optional] the GUI label suggestion for this property
677  const std::string & group = "" ///< [in] [optional] the group for this property
678  );
679 
680 /// Create a standard R/W INDI selection (one of many) switch with vector of elements using the element strings as their own labels
681  /** This switch is intended to function like drop down menu.
682  *
683  * \returns 0 on success
684  * \returns -1 on error
685  */
686  int createStandardIndiSelectionSw( pcf::IndiProperty & prop, ///< [out] the property to create and setup
687  const std::string & name, ///< [in] the name of the property,
688  const std::vector<std::string> & elements, ///< [in] the element names to give to the switches
689  const std::string & label = "", ///< [in] [optional] the GUI label suggestion for this property
690  const std::string & group = "" ///< [in] [optional] the group for this property
691  );
692 
693  /// Register an INDI property which is read only.
694  /** This version requires the property be fully set up.
695  *
696  * \returns 0 on success.
697  * \returns -1 on error.
698  *
699  */
700  int registerIndiPropertyReadOnly( pcf::IndiProperty & prop /**< [in] the property to register, must be completely setup */ );
701 
702  /// Register an INDI property which is read only.
703  /** This verison sets up the INDI property according to the arguments.
704  *
705  * \returns 0 on success.
706  * \returns -1 on error.
707  *
708  */
709  int registerIndiPropertyReadOnly( pcf::IndiProperty & prop, ///< [out] the property to register, will be configured
710  const std::string & propName, ///< [in] the name of the property
711  const pcf::IndiProperty::Type & propType, ///< [in] the type of the property
712  const pcf::IndiProperty::PropertyPermType & propPerm, ///< [in] the permissions of the property
713  const pcf::IndiProperty::PropertyStateType & propState ///< [in] the state of the property
714  );
715 
716  /// Register an INDI property which is exposed for others to request a New Property for.
717  /** In this version the supplied IndiProperty must be fully set up before passing in.
718  *
719  * \returns 0 on success.
720  * \returns -1 on error.
721  *
722  */
723  int registerIndiPropertyNew( pcf::IndiProperty & prop, ///< [in] the property to register, must be fully set up
724  int (*)( void *, const pcf::IndiProperty &) ///< [in] the callback for changing the property
725  );
726 
727  /// Register an INDI property which is exposed for others to request a New Property for.
728  /** This verison sets up the INDI property according to the arguments.
729  *
730  * \returns 0 on success.
731  * \returns -1 on error.
732  *
733  */
734  int registerIndiPropertyNew( pcf::IndiProperty & prop, ///< [out] the property to register
735  const std::string & propName, ///< [in] the name of the property
736  const pcf::IndiProperty::Type & propType, ///< [in] the type of the property
737  const pcf::IndiProperty::PropertyPermType & propPerm, ///< [in] the permissions of the property
738  const pcf::IndiProperty::PropertyStateType & propState, ///< [in] the state of the property
739  int (*)( void *, const pcf::IndiProperty &) ///< [in] the callback for changing the property
740  );
741 
742  /// Register an INDI property which is exposed for others to request a New Property for, with a switch rule
743  /** This verison sets up the INDI property according to the arguments.
744  *
745  * \returns 0 on success.
746  * \returns -1 on error.
747  *
748  */
749  int registerIndiPropertyNew( pcf::IndiProperty & prop, ///< [out] the property to register
750  const std::string & propName, ///< [in] the name of the property
751  const pcf::IndiProperty::Type & propType, ///< [in] the type of the property
752  const pcf::IndiProperty::PropertyPermType & propPerm, ///< [in] the permissions of the property
753  const pcf::IndiProperty::PropertyStateType & propState, ///< [in] the state of the property
754  const pcf::IndiProperty::SwitchRuleType & propRule, ///< [in] the switch rule type
755  int (*)( void *, const pcf::IndiProperty &) ///< [in] the callback for changing the property
756  );
757 
758  /// Register an INDI property which is monitored for updates from others.
759  /**
760  *
761  * \returns 0 on success.
762  * \returns -1 on error.
763  *
764  */
765  int registerIndiPropertySet( pcf::IndiProperty & prop, ///< [out] the property to register
766  const std::string & devName, ///< [in] the device which owns this property
767  const std::string & propName, ///< [in] the name of the property
768  int (*)( void *, const pcf::IndiProperty &) ///< [in] the callback for processing the property change
769  );
770 
771 protected:
772  /// Create the INDI FIFOs
773  /** Changes permissions to max available and creates the
774  * FIFOs at the configured path.
775  */
777 
778  /// Start INDI Communications
779  /**
780  * \returns 0 on success
781  * \returns -1 on error. This is fatal.
782  */
783  int startINDI();
784 
785 public:
786 
787  void sendGetPropertySetList(bool all=false);
788 
789  /// Handler for the DEF INDI properties notification
790  /** Uses the properties registered in m_indiSetCallBacks to process the notification. This is called by
791  * m_indiDriver's indiDriver::handleDefProperties.
792  */
793  void handleDefProperty( const pcf::IndiProperty &ipRecv /**< [in] The property being sent. */ );
794 
795  /// Handler for the get INDI properties request
796  /** Uses the properties registered in m_indiCallBacks to respond to the request. This is called by
797  * m_indiDriver's indiDriver::handleGetProperties.
798  */
799  void handleGetProperties( const pcf::IndiProperty &ipRecv /**< [in] The property being requested. */ );
800 
801  /// Handler for the new INDI property request
802  /** Uses the properties registered in m_indiCallBacks to respond to the request, looking up the callback for this
803  * property and calling it.
804  *
805  * This is called by m_indiDriver's indiDriver::handleGetProperties.
806  *
807  * \todo handle errors, are they FATAL?
808  */
809  void handleNewProperty( const pcf::IndiProperty &ipRecv /**< [in] The property being changed. */);
810 
811  /// Handler for the set INDI property request
812  /**
813  *
814  * This is called by m_indiDriver's indiDriver::handleSetProperties.
815  *
816  * \todo handle errors, are they FATAL?
817  */
818  void handleSetProperty( const pcf::IndiProperty &ipRecv /**< [in] The property being changed. */);
819 
820 protected:
821 
822  /// Update an INDI property element value if it has changed.
823  /** Will only peform a SetProperty if the new element value has changed
824  * compared to the stored value, or if the property state has changed.
825  *
826  * This comparison is done in the true
827  * type of the value.
828  *
829  * For a property with multiple elements, you should use the vector version to minimize network traffic.
830  */
831  template<typename T>
832  void updateIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
833  const std::string & el, ///< [in] The element name
834  const T & newVal, ///< [in] the new value
835  pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok
836  );
837 
838  /// Update an INDI property element value if it has changed.
839  /** Will only peform a SetProperty if the new element value has changed
840  * compared to the stored value, or if the property state has changed.
841  *
842  * This comparison is done in the true
843  * type of the value.
844  *
845  * This is a specialization for `const char *` to `std::string`.
846  *
847  * For a property with multiple elements, you should use the vector version to minimize network traffic.
848  * \overload
849  */
850  void updateIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
851  const std::string & el, ///< [in] The element name
852  const char * newVal, ///< [in] the new value
853  pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok
854  );
855 
856  /// Update an INDI switch element value if it has changed.
857  /** Will only peform a SetProperty if the new element switch state has changed, or the propery state
858  * has changed.
859  *
860  */
861  void updateSwitchIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
862  const std::string & el, ///< [in] The element name
863  const pcf::IndiElement::SwitchStateType & newVal, ///< [in] the new value
864  pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok
865  );
866 
867  /// Update an INDI property if values have changed.
868  /** Will only peform a SetProperty if at least one value has changed
869  * compared to the stored value, or if the property state has changed.
870  *
871  * Constructs the element names for each value as elX where X is the index of the vector.
872  *
873  * This comparison is done in the true
874  * type of the value.
875  *
876  *
877  * \overload
878  */
879  template<typename T>
880  void updateIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
881  const std::string & el, ///< [in] Beginning of each element name
882  const std::vector<T> & newVals, ///< [in] the new values
883  pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok ///< [in] [optional] the new state
884  );
885 
886  /// Update an INDI property if values have changed.
887  /** Will only peform a SetProperty if at least one value has changed
888  * compared to the stored value, or if the property state has changed.
889  *
890  * This comparison is done in the true
891  * type of the value.
892  *
893  *
894  * \overload
895  */
896  template<typename T>
897  void updateIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
898  const std::vector<std::string> & els, ///< [in] String vector of element names
899  const std::vector<T> & newVals ///< [in] the new values
900  );
901 
902  /// Get the target element value from an new property
903  /**
904  * \returns 0 on success
905  * \returns -1 on error
906  */
907  template<typename T>
908  int indiTargetUpdate( pcf::IndiProperty & localProperty, ///< [out] The local property to update
909  T & localTarget, ///< [out] The local value to update
910  const pcf::IndiProperty & remoteProperty, ///< [in] the new property received
911  bool setBusy = true ///< [in] [optional] set property to busy if true
912  );
913 
914  /// Send a newProperty command to another device (using the INDI Client interface)
915  /** Copies the input IndiProperty, then updates the element with the new value.
916  *
917  * \returns 0 on success.
918  * \returns -1 on an errory.
919  */
920  template<typename T>
921  int sendNewProperty( const pcf::IndiProperty & ipSend, ///< [in] The property to send a "new" INDI command for
922  const std::string & el, ///< [in] The element of the property to change
923  const T & newVal ///< [in] The value to request for the element.
924  );
925  /// Send a newProperty command to another device (using the INDI Client interface)
926  /**
927  *
928  * \returns 0 on success.
929  * \returns -1 on an error, which will be logged
930  */
931  int sendNewProperty( const pcf::IndiProperty & ipSend /**< [in] The property to send a "new" INDI command for */);
932 
933  /// Send a new property commmand for a standard toggle switch
934  /**
935  * \returns 0 on success
936  * \returns -1 on an error, which will be logged.
937  */
938  int sendNewStandardIndiToggle( const std::string & device, ///< [in] The device name
939  const std::string & property, ///< [in] The property name
940  bool onoff ///< [in] Switch state to send: true = on, false = off
941  );
942 
943 
944  ///indi Property to report the application state.
945  pcf::IndiProperty m_indiP_state;
946 
947  ///indi Property to clear an FSM alert.
948  pcf::IndiProperty m_indiP_clearFSMAlert;
949 
950  /// The static callback function to be registered for requesting to clear the FSM alert
951  /**
952  * \returns 0 on success.
953  * \returns -1 on error.
954  */
955  static int st_newCallBack_clearFSMAlert( void * app, ///< [in] a pointer to this, will be static_cast-ed to MagAOXApp.
956  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
957  );
958 
959  /// The callback called by the static version, to actually process the FSM Alert Clear request.
960  /**
961  * \returns 0 on success.
962  * \returns -1 on error.
963  */
964  int newCallBack_clearFSMAlert( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
965 
966  ///@} --INDI Interface
967 
968  /** \name Power Management
969  * For devices which have remote power management (e.g. from one of the PDUs) we implement
970  * a standard power state monitoring and management component for the FSM. This needs to be enabled
971  * in the derived app constructor. To stay enabled, m_powerDevice and m_powerChannel must be
972  * not empty strings after the configuration. These could be set in the derived app defaults.
973  *
974  * If power management is enabled, then while power is off, appLogic will not be called.
975  * Instead a parrallel set of virtual functions is called, onPowerOff (to allow apps to
976  * perform cleanup) and whilePowerOff (to allow apps to keep variables updated, etc).
977  * Note that these could merely call appLogic if desired.
978  *
979  */
980 protected:
981  bool m_powerMgtEnabled {false}; ///< Flag controls whether power mgt is used. Set this in the constructor of a derived app. If true, then if after loadConfig the powerDevice and powerChannel are empty, then the app will exit with a critical error.
982 
983  /* Configurables . . . */
984  std::string m_powerDevice; ///< The INDI device name of the power controller
985  std::string m_powerChannel; ///< The INDI property name of the channel controlling this device's power.
986  std::string m_powerElement {"state"}; ///< The INDI element name to monitor for this device's power state.
987  std::string m_powerTargetElement {"target"}; ///< The INDI element name to monitor for this device's power state.
988 
989  unsigned long m_powerOnWait {0}; ///< Time in sec to wait for device to boot after power on.
990 
991  /* Power on waiting counter . . . */
992  int m_powerOnCounter {-1}; ///< Counts numer of loops after power on, implements delay for device bootup. If -1, then device was NOT powered off on app startup.
993 
994  /* Power state . . . */
995  int m_powerState {-1}; ///< Current power state, 1=On, 0=Off, -1=Unk.
996  int m_powerTargetState {-1}; ///< Current target power state, 1=On, 0=Off, -1=Unk.
997 
998  pcf::IndiProperty m_indiP_powerChannel; ///< INDI property used to communicate power state.
999 
1000  /// This method is called when the change to poweroff is detected.
1001  /**
1002  * \returns 0 on success.
1003  * \returns -1 on any error which means the app should exit.
1004  */
1005  virtual int onPowerOff();
1006 
1007  /// This method is called while the power is off, once per FSM loop.
1008  /**
1009  * \returns 0 on success.
1010  * \returns -1 on any error which means the app should exit.
1011  */
1012  virtual int whilePowerOff();
1013 
1014  /// This method tests whether the power on wait time has elapsed.
1015  /** You would call this once per appLogic loop while in state POWERON. While false, you would return 0.
1016  * Once it becomes true, take post-power-on actions and go on with life.
1017  *
1018  * \returns true if the time since POWERON is greater than the power-on wait, or if power management is not enabled
1019  * \returns false otherwise
1020  */
1022 
1023 public:
1024 
1025  /// Returns the current power state.
1026  /** If power management is not enabled, this always returns 1=On.
1027  *
1028  * \returns -1 if power state is unknown
1029  * \returns 0 if power is off
1030  * \returns 1 if power is on or m_powerMgtEnabled==false
1031  */
1032  int powerState();
1033 
1034  /// Returns the target power state.
1035  /** If power management is not enabled, this always returns 1=On.
1036  *
1037  * \returns -1 if target power state is unknown
1038  * \returns 0 if target power state is off
1039  * \returns 1 if target power is on or m_powerMgtEnabled==false
1040  */
1042 
1043  INDI_SETCALLBACK_DECL(MagAOXApp, m_indiP_powerChannel);
1044 
1045  ///@} Power Management
1046 
1047 public:
1048 
1049  /** \name Member Accessors
1050  *
1051  * @{
1052  */
1053 
1054  ///Get the config name
1055  /**
1056  * \returns the current value of m_configName
1057  */
1058  std::string configName();
1059 
1060  ///Get the config directory
1061  /**
1062  * \returns the current value of m_configDir
1063  */
1064  std::string configDir();
1065 
1066  ///Get the INDI input FIFO file name
1067  /**
1068  * \returns the current value of m_driverInName
1069  */
1070  std::string driverInName();
1071 
1072  ///Get the INDI output FIFO file name
1073  /**
1074  * \returns the current value of m_driverOutName
1075  */
1076  std::string driverOutName();
1077 
1078  ///Get the INDI control FIFO file name
1079  /**
1080  * \returns the current value of m_driverCtrlName
1081  */
1082  std::string driverCtrlName();
1083 
1084  ///@} --Member Accessors
1085 };
1086 
1087 //Set self pointer to null so app starts up uninitialized.
1088 template<bool _useINDI> MagAOXApp<_useINDI> * MagAOXApp<_useINDI>::m_self = nullptr;
1089 
1090 //Define the logger
1091 template<bool _useINDI> typename MagAOXApp<_useINDI>::logManagerT MagAOXApp<_useINDI>::m_log;
1092 
1093 template<bool _useINDI>
1094 MagAOXApp<_useINDI>::MagAOXApp( const std::string & git_sha1,
1095  const bool git_modified
1096  )
1097 {
1098  if( m_self != nullptr )
1099  {
1100  std::cerr << "Attempt to instantiate 2nd MagAOXApp. Exiting immediately.\n";
1101  exit(-1);
1102  }
1103 
1104  m_self = this;
1105 
1106  //Get the uids of this process.
1107  getresuid(&m_euidReal, &m_euidCalled, &m_suid);
1108  setEuidReal(); //immediately step down to unpriveleged uid.
1109 
1110  m_log.parent(this);
1111 
1112  //Set up config logging
1113  config.m_sources = true;
1114  config.configLog = configLog;
1115 
1116  //We log the current GIT status.
1118  if(git_modified)
1119  {
1120  gl = logPrio::LOG_WARNING;
1121  m_gitAlert = true;
1122  }
1123  log<git_state>(git_state::messageT("MagAOX", git_sha1, git_modified), gl);
1124 
1125  gl = logPrio::LOG_INFO;
1126  if(MXLIB_UNCOMP_REPO_MODIFIED)
1127  {
1128  gl = logPrio::LOG_WARNING;
1129  m_gitAlert = true;
1130  }
1131 
1132  log<git_state>(git_state::messageT("mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED), gl);
1133 
1134 }
1135 
1136 template<bool _useINDI>
1138 {
1139  if(m_indiDriver) delete m_indiDriver;
1140  m_log.parent(nullptr);
1141 
1142  MagAOXApp<_useINDI>::m_self = nullptr;
1143 }
1144 
1145 template<bool _useINDI>
1147 {
1148  return m_shutdown;
1149 }
1150 
1151 template<bool _useINDI>
1153  char ** argv
1154  ) //virtual
1155 {
1156  std::string tmpstr;
1157 
1158  tmpstr = mx::sys::getEnv(MAGAOX_env_path);
1159  if(tmpstr != "")
1160  {
1161  MagAOXPath = tmpstr;
1162  }
1163  else
1164  {
1165  MagAOXPath = MAGAOX_path;
1166  }
1167 
1168  //Set the config path relative to MagAOXPath
1169  tmpstr = mx::sys::getEnv(MAGAOX_env_config);
1170  if(tmpstr == "")
1171  {
1172  tmpstr = MAGAOX_configRelPath;
1173  }
1174  m_configDir = MagAOXPath + "/" + tmpstr;
1175  m_configPathGlobal = m_configDir + "/magaox.conf";
1176 
1177  //Set the calib path relative to MagAOXPath
1178  tmpstr = mx::sys::getEnv(MAGAOX_env_calib);
1179  if(tmpstr == "")
1180  {
1181  tmpstr = MAGAOX_calibRelPath;
1182  }
1183  m_calibDir = MagAOXPath + "/" + tmpstr;
1184 
1185 
1186 
1187  //Setup default log path
1188  tmpstr = MagAOXPath + "/" + MAGAOX_logRelPath;
1189 
1190  m_log.logPath(tmpstr);
1191 
1192  //Setup default sys path
1193  tmpstr = MagAOXPath + "/" + MAGAOX_sysRelPath;
1194  sysPath = tmpstr;
1195 
1196  //Setup default secrets path
1197  tmpstr = MagAOXPath + "/" + MAGAOX_secretsRelPath;
1198  secretsPath = tmpstr;
1199 
1200  //Setup default cpuset path
1201  tmpstr = mx::sys::getEnv(MAGAOX_env_cpuset);
1202  if(tmpstr != "")
1203  {
1204  m_cpusetPath = tmpstr;
1205  }
1206  //else we stick with the default
1207 
1208 
1209  if(m_configBase != "")
1210  {
1211  //We use mx::application's configPathUser for this components base config file
1212  m_configPathUser = m_configDir + "/" + m_configBase + ".conf";
1213  }
1214 
1215  //Parse CL just to get the "name".
1216  config.add("name","n", "name",argType::Required, "", "name", false, "string", "The name of the application, specifies config.");
1217 
1218  config.parseCommandLine(argc, argv, "name");
1219  config(m_configName, "name");
1220 
1221  if(m_configName == "")
1222  {
1223  m_configName = mx::ioutils::pathStem(invokedName);
1224  log<text_log>("Application name (-n --name) not set. Using argv[0].");
1225  }
1226 
1227  //We use mx::application's configPathLocal for this component's config file
1228  m_configPathLocal = m_configDir + "/" + m_configName + ".conf";
1229 
1230  //Now we can setup common INDI properties
1231  if( registerIndiPropertyNew( m_indiP_state, "fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0) < 0)
1232  {
1233  log<software_error>({__FILE__,__LINE__, "failed to register read only fsm_state property"});
1234  }
1235 
1236  m_indiP_state.add (pcf::IndiElement("state"));
1237 
1238  createStandardIndiRequestSw( m_indiP_clearFSMAlert, "fsm_clear_alert", "Clear FSM Alert", "FSM");
1239  if(registerIndiPropertyNew(m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert) < 0)
1240  {
1241  log<software_error>({__FILE__,__LINE__, "failed to register new fsm_alert property"});
1242  }
1243 
1244  return;
1245 
1246 }
1247 
1248 template<bool _useINDI>
1250 {
1251  //App stuff
1252  config.add("loopPause", "p", "loopPause", argType::Required, "", "loopPause", false, "unsigned long", "The main loop pause time in ns");
1253 
1254  config.add("ignore_git", "", "ignore-git", argType::True, "", "", false, "bool", "set to true to ignore git status");
1255 
1256  //Logger Stuff
1257  m_log.setupConfig(config);
1258 
1259  if( m_powerMgtEnabled)
1260  {
1261  if(_useINDI == false)
1262  {
1263  //If this condition obtains, we should not go on because it means we'll never leave power off!!!
1264  log<software_critical>({__FILE__,__LINE__, "power management is enabled but we are not using INDI"});
1265  m_shutdown = true;
1266  }
1267 
1268  //Power Management
1269  config.add("power.device", "", "power.device", argType::Required, "power", "device", false, "string", "Device controlling power for this app's device (INDI name).");
1270  config.add("power.channel", "", "power.channel", argType::Required, "power", "channel", false, "string", "Channel on device for this app's device (INDI name).");
1271  config.add("power.element", "", "power.element", argType::Required, "power", "element", false, "string", "INDI power state element name. Default is \"state\", only need to specify if different.");
1272  config.add("power.targetElement", "", "power.targetElement", argType::Required, "power", "targetElement", false, "string", "INDI power target element name. Default is \"target\", only need to specify if different.");
1273  config.add("power.powerOnWait", "", "power.powerOnWait", argType::Required, "power", "powerOnWait", false, "int", "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec.");
1274  }
1275 }
1276 
1277 template<bool _useINDI>
1279 {
1280  bool ig {false};
1281  config(ig, "ignore_git");
1282 
1283  if(!ig && m_gitAlert)
1284  {
1285  m_stateAlert = true;
1286  }
1287 
1288  //---------- Setup the logger ----------//
1289  m_log.logName(m_configName);
1290  m_log.loadConfig(config);
1291 
1292  //--------- Loop Pause Time --------//
1293  config(m_loopPause, "loopPause");
1294 
1295  //--------Power Management --------//
1296  if( m_powerMgtEnabled)
1297  {
1298  config(m_powerDevice, "power.device");
1299  config(m_powerChannel, "power.channel");
1300  config(m_powerElement, "power.element");
1301  config(m_powerTargetElement, "power.targetElement");
1302 
1303  if(m_powerDevice != "" && m_powerChannel != "")
1304  {
1305  log<text_log>("enabling power management: " + m_powerDevice + "." + m_powerChannel + "." + m_powerElement + "/" + m_powerTargetElement);
1306  if( registerIndiPropertySet( m_indiP_powerChannel,m_powerDevice, m_powerChannel, INDI_SETCALLBACK(m_indiP_powerChannel)) < 0)
1307  {
1308  log<software_error>({__FILE__,__LINE__, "failed to register set property"});
1309  }
1310  }
1311  else
1312  {
1313  log<text_log>("power management not configured!", logPrio::LOG_CRITICAL);
1314  m_shutdown = true;
1315  }
1316 
1317  config(m_powerOnWait, "power.powerOnWait");
1318  if(m_powerOnWait > 3600)
1319  {
1320  log<text_log>("powerOnWait longer than 1 hour. Setting to 0.", logPrio::LOG_ERROR);
1321  }
1322  }
1323 
1324 }
1325 
1326 template<bool _useINDI>
1328 {
1329  //This checks for unused config options and arguments, and logs them.
1330  //This will catch both bad options, and options we aren't actually using (debugging).
1331  for( auto it = config.m_targets.begin(); it != config.m_targets.end(); ++it )
1332  {
1333  if(it->second.used == false)
1334  {
1335  std::string msg = it->second.name;
1336  if(config.m_sources && it->second.sources.size() > 0) msg += " [" + it->second.sources[0] + "]";
1337  log<text_log>("Unused config target: " + msg, logPrio::LOG_WARNING);
1338  }
1339  }
1340 
1341  if(config.m_unusedConfigs.size() > 0)
1342  {
1343  for( auto it = config.m_unusedConfigs.begin(); it != config.m_unusedConfigs.end(); ++it )
1344  {
1345  if(it->second.used == true) continue;
1346 
1347  std::string msg = it->second.name;
1348  if(config.m_sources && it->second.sources.size() > 0 ) msg += " [" + it->second.sources[0] + "]";
1349 
1350  log<text_log>("Unrecognized config setting: " + msg, logPrio::LOG_CRITICAL);
1351  m_shutdown = true;
1352  }
1353 
1354  }
1355 
1356  if(config.nonOptions.size() > 0)
1357  {
1358  for(size_t n =0; n < config.nonOptions.size(); ++n)
1359  {
1360  log<text_log>("Unrecognized command line argument: " + config.nonOptions[n], logPrio::LOG_CRITICAL);
1361  }
1362  m_shutdown = true;
1363  }
1364 
1365 }
1366 
1367 
1368 
1369 template<bool _useINDI>
1371 {
1372  //----------------------------------------//
1373  // Check user
1374  //----------------------------------------//
1375  #ifndef XWC_DISABLE_USER_CHECK
1376  struct stat logstat;
1377 
1378  if( stat(m_log.logPath().c_str(), &logstat) < 0)
1379  {
1380  return log<text_log,-1>({"Can not stat the log path."}, logPrio::LOG_CRITICAL);
1381  }
1382 
1383  if( logstat.st_uid != geteuid() )
1384  {
1385  return log<text_log,-1>({"You are running this app as the wrong user."}, logPrio::LOG_CRITICAL);
1386  }
1387  #endif
1388 
1389  //----------------------------------------//
1390  // Get the PID Lock
1391  //----------------------------------------//
1392  if( lockPID() < 0 )
1393  {
1394  state(stateCodes::FAILURE);
1395  log<text_log>({"Failed to lock PID."}, logPrio::LOG_CRITICAL);
1396  //Return immediately, not safe to go on.
1397  return -1;
1398  }
1399 
1400  //----------------------------------------//
1401  // Begin the logger
1402  //----------------------------------------//
1403  m_log.logThreadStart();
1404 
1405  //Give up to 2 secs to make sure log thread has time to get started and try to open a file.
1406  int w = 0;
1407  while(m_log.logThreadRunning() == false && w < 20)
1408  {
1409  //Sleep for 100 msec
1410  std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>(100000000));
1411  ++w;
1412  }
1413 
1414  if(m_log.logThreadRunning() == false)
1415  {
1416  //We don't log this, because it won't be logged anyway.
1417  std::cerr << "\nCRITICAL: log thread not running. Exiting.\n\n";
1418  m_shutdown = 1;
1419  }
1420 
1421  //----------------------------------------//
1422 
1423  //Install signal handling
1424  if(m_shutdown == 0)
1425  {
1426  setSigTermHandler();
1427  }
1428 
1429  //We are not initialized, begin application startup.
1430  if( m_shutdown == 0 )
1431  {
1432  state(stateCodes::INITIALIZED);
1433  if(appStartup() < 0) m_shutdown = 1;
1434  }
1435 
1436  //====Begin INDI Communications
1437  if(m_useINDI && m_shutdown == 0) //if we're using INDI and not already dead, that is
1438  {
1439  if(startINDI() < 0)
1440  {
1441  state(stateCodes::FAILURE);
1442  m_shutdown = 1;
1443  }
1444  }
1445 
1446  //We have to wait for power status to become available
1447  if(m_powerMgtEnabled && m_shutdown == 0)
1448  {
1449  int nwaits = 0;
1450  while(m_powerState < 0 && !m_shutdown)
1451  {
1452  sleep(1);
1453  if(m_powerState < 0)
1454  {
1455  if(!stateLogged()) log<text_log>("waiting for power state");
1456  }
1457 
1458  ++nwaits;
1459  if(nwaits == 30)
1460  {
1461  log<text_log>("stalled waiting for power state", logPrio::LOG_ERROR);
1462  state(stateCodes::ERROR);
1463  }
1464  }
1465 
1466  if(m_powerState > 0)
1467  {
1468  state(stateCodes::POWERON);
1469  }
1470  else
1471  {
1472  m_powerOnCounter = 0;
1473  state(stateCodes::POWEROFF);
1474  if(onPowerOff() < 0)
1475  {
1476  m_shutdown = 1;
1477  }
1478  }
1479  }
1480 
1481  //This is the main event loop.
1482  /* Conditions on entry:
1483  * -- PID locked
1484  * -- Log thread running
1485  * -- Signal handling installed
1486  * -- appStartup() successful
1487  * -- INDI communications started successfully (if being used)
1488  * -- power state known (if being managed)
1489  */
1490  while( m_shutdown == 0)
1491  {
1492  //First check power state.
1493  if(m_powerMgtEnabled)
1494  {
1495  if(state() == stateCodes::POWEROFF)
1496  {
1497  if(m_powerState == 1)
1498  {
1499  m_powerOnCounter = 0;
1500  state(stateCodes::POWERON);
1501  }
1502  }
1503  else //Any other state
1504  {
1505  if(m_powerState == 0)
1506  {
1507  state(stateCodes::POWEROFF);
1508  if(onPowerOff() < 0)
1509  {
1510  m_shutdown = 1;
1511  continue;
1512  }
1513  }
1514  //We don't do anything if m_powerState is -1, which is a startup condition.
1515  }
1516  }
1517 
1518  //Only run appLogic if power is on, or we are not managing power.
1519  if( !m_powerMgtEnabled || m_powerState > 0 )
1520  {
1521  if( appLogic() < 0)
1522  {
1523  m_shutdown = 1;
1524  continue;
1525  }
1526  }
1527  else if(m_powerState == 0)
1528  {
1529  if( whilePowerOff() < 0)
1530  {
1531  m_shutdown = 1;
1532  continue;
1533  }
1534  }
1535 
1536  /** \todo Need a heartbeat update here.
1537  */
1538 
1539  if(m_useINDI)
1540  {
1541  //Checkup on the INDI properties we're monitoring.
1542  //This will make sure we are up-to-date if indiserver restarts without us.
1543  //And handles cases where we miss a Def becuase the other driver wasn't started up
1544  //when we sent our Get.
1545  sendGetPropertySetList(false); //Only does anything if it needs to be done.
1546  }
1547 
1548  // This is purely to make sure INDI is up to date in case
1549  // mutex was locked on last attempt.
1550  state( state() );
1551 
1552  //Pause loop unless shutdown is set
1553  if( m_shutdown == 0)
1554  {
1555  std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>(m_loopPause));
1556  }
1557  }
1558 
1559  appShutdown();
1560 
1561  state(stateCodes::SHUTDOWN);
1562 
1563  //Stop INDI communications
1564  if(m_indiDriver != nullptr)
1565  {
1566  pcf::IndiProperty ipSend;
1567  ipSend.setDevice(m_configName);
1568  try
1569  {
1570  m_indiDriver->sendDelProperty(ipSend);
1571  }
1572  catch(const std::exception & e)
1573  {
1574  log<software_error>({__FILE__, __LINE__, std::string("exception caught from sendDelProperty: ") + e.what()});
1575  }
1576 
1577  m_indiDriver->quitProcess();
1578  m_indiDriver->deactivate();
1579  log<indidriver_stop>();
1580  }
1581 
1582  unlockPID();
1583 
1584  sleep(1);
1585  return 0;
1586 }
1587 
1588 template<bool _useINDI>
1589 template<typename logT, int retval>
1590 int MagAOXApp<_useINDI>::log( const typename logT::messageT & msg,
1591  logPrioT level
1592  )
1593 {
1594  m_log.template log<logT>(msg, level);
1595  return retval;
1596 }
1597 
1598 template<bool _useINDI>
1599 template<typename logT, int retval>
1601 {
1602  m_log.template log<logT>(level);
1603  return retval;
1604 }
1605 
1606 template<bool _useINDI>
1608 {
1609  if( logHeader::logLevel( b ) <= logPrio::LOG_NOTICE )
1610  {
1611  logStdFormat(std::cerr, b);
1612  std::cerr << "\n";
1613  }
1614 
1615  if( logHeader::logLevel( b ) < logPrio::LOG_ERROR )
1616  {
1617  state(m_state, true); //For anything worse than error, we set the FSM state to alert
1618  }
1619 
1620  if(_useINDI && m_indiDriver)
1621  {
1622  pcf::IndiProperty msg;
1623  msg.setDevice(m_configName);
1624 
1625  std::stringstream logstdf;
1626  logMinStdFormat(logstdf, b);
1627 
1628  msg.setMessage(logstdf.str());
1629 
1630  //Set the INDI prop timespec to match the log entry
1631  timespecX ts = logHeader::timespec(b);
1632  timeval tv;
1633  tv.tv_sec = ts.time_s;
1634  tv.tv_usec = (long int) ( ((double) ts.time_ns)/1e3 );
1635 
1636  msg.setTimeStamp(pcf::TimeStamp(tv));
1637 
1638  try
1639  {
1640  m_indiDriver->sendMessage(msg);
1641  }
1642  catch(const std::exception & e)
1643  {
1644  log<software_error>({__FILE__, __LINE__, std::string("exception caught from sendMessage: ") + e.what()});
1645  }
1646  }
1647 }
1648 
1649 template<bool _useINDI>
1650 void MagAOXApp<_useINDI>::configLog( const std::string & name,
1651  const int & code,
1652  const std::string & value,
1653  const std::string & source
1654  )
1655 {
1656  m_log.template log<config_log>({name, code, value, source});
1657 }
1658 
1659 template<bool _useINDI>
1661 {
1662  struct sigaction act;
1663  sigset_t set;
1664 
1665  act.sa_sigaction = &MagAOXApp<_useINDI>::_handlerSigTerm;
1666  act.sa_flags = SA_SIGINFO;
1667  sigemptyset(&set);
1668  act.sa_mask = set;
1669 
1670  errno = 0;
1671  if( sigaction(SIGTERM, &act, 0) < 0 )
1672  {
1673  std::string logss = "Setting handler for SIGTERM failed. Errno says: ";
1674  logss += strerror(errno);
1675 
1676  log<software_error>({__FILE__, __LINE__, errno, 0, logss});
1677 
1678  return -1;
1679  }
1680 
1681  errno = 0;
1682  if( sigaction(SIGQUIT, &act, 0) < 0 )
1683  {
1684  std::string logss = "Setting handler for SIGQUIT failed. Errno says: ";
1685  logss += strerror(errno);
1686 
1687  log<software_error>({__FILE__, __LINE__, errno, 0,logss});
1688 
1689  return -1;
1690  }
1691 
1692  errno = 0;
1693  if( sigaction(SIGINT, &act, 0) < 0 )
1694  {
1695  std::string logss = "Setting handler for SIGINT failed. Errno says: ";
1696  logss += strerror(errno);
1697 
1698  log<software_error>({__FILE__, __LINE__, errno, 0, logss});
1699 
1700  return -1;
1701  }
1702 
1703  log<text_log>("Installed SIGTERM/SIGQUIT/SIGINT signal handler.", logPrio::LOG_DEBUG);
1704 
1705  return 0;
1706 }
1707 
1708 template<bool _useINDI>
1710  siginfo_t *siginf,
1711  void *ucont
1712  )
1713 {
1714  m_self->handlerSigTerm(signum, siginf, ucont);
1715 }
1716 
1717 template<bool _useINDI>
1719  siginfo_t *siginf __attribute__((unused)),
1720  void *ucont __attribute__((unused))
1721  )
1722 {
1723  m_shutdown = 1;
1724 
1725  std::string signame;
1726  switch(signum)
1727  {
1728  case SIGTERM:
1729  signame = "SIGTERM";
1730  break;
1731  case SIGINT:
1732  signame = "SIGINT";
1733  break;
1734  case SIGQUIT:
1735  signame = "SIGQUIT";
1736  break;
1737  default:
1738  signame = "OTHER";
1739  }
1740 
1741  std::string logss = "Caught signal ";
1742  logss += signame;
1743  logss += ". Shutting down.";
1744 
1745  std::cerr << "\n" << logss << std::endl;
1746  log<text_log>(logss);
1747 }
1748 
1749 ///Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
1750 void sigUsr1Handler( int signum,
1751  siginfo_t * siginf,
1752  void *ucont
1753  );
1754 
1755 
1756 template<bool _useINDI>
1758 {
1759  errno = 0;
1760  if(sys::th_seteuid(m_euidCalled) < 0)
1761  {
1762  std::string logss = "Setting effective user id to euidCalled (";
1763  logss += mx::ioutils::convertToString<int>(m_euidCalled);
1764  logss += ") failed. Errno says: ";
1765  logss += strerror(errno);
1766 
1767  log<software_error>({__FILE__, __LINE__, errno, 0, logss});
1768 
1769  return -1;
1770  }
1771 
1772  return 0;
1773 }
1774 
1775 template<bool _useINDI>
1777 {
1778  errno = 0;
1779  if(sys::th_seteuid(m_euidReal) < 0)
1780  {
1781  std::string logss = "Setting effective user id to euidReal (";
1782  logss += mx::ioutils::convertToString<int>(m_euidReal);
1783  logss += ") failed. Errno says: ";
1784  logss += strerror(errno);
1785 
1786  log<software_error>({__FILE__, __LINE__, errno, 0, logss});
1787 
1788  return -1;
1789  }
1790 
1791  return 0;
1792 
1793 }
1794 
1795 template<bool _useINDI>
1797 {
1798  m_pid = getpid();
1799 
1800  std::string statusDir = sysPath;
1801 
1802  //Get the maximum privileges available
1803  elevatedPrivileges elPriv(this);
1804 
1805  // Create statusDir root with read/write/search permissions for owner and group, and with read/search permissions for others.
1806  errno = 0;
1807  if( mkdir(statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 )
1808  {
1809  if( errno != EEXIST)
1810  {
1811  std::stringstream logss;
1812  logss << "Failed to create root of statusDir (" << statusDir << "). Errno says: " << strerror(errno);
1813  log<software_critical>({__FILE__, __LINE__, errno, 0, logss.str()});
1814  return -1;
1815  }
1816 
1817  }
1818 
1819  statusDir += "/";
1820  statusDir += m_configName;
1821 
1822  pidFileName = statusDir + "/pid";
1823 
1824  // Create statusDir with read/write/search permissions for owner and group, and with read/search permissions for others.
1825  errno = 0;
1826  if( mkdir(statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 )
1827  {
1828  if( errno != EEXIST)
1829  {
1830  std::stringstream logss;
1831  logss << "Failed to create statusDir (" << statusDir << "). Errno says: " << strerror(errno);
1832  log<software_critical>({__FILE__, __LINE__, errno, 0, logss.str()});
1833  return -1;
1834  }
1835 
1836  //If here, then we need to check the pid file.
1837 
1838  std::ifstream pidIn;
1839  pidIn.open( pidFileName );
1840 
1841  if(pidIn.good()) //PID file exists, now read its contents and compare to proc/<pid>/cmdline
1842  {
1843  //Read PID from file
1844  pid_t testPid;
1845  pidIn >> testPid;
1846  pidIn.close();
1847 
1848  //Get command line used to start this process from /proc
1849  std::stringstream procN;
1850  procN << "/proc/" << testPid << "/cmdline";
1851 
1852  std::ifstream procIn;
1853  std::string pidCmdLine;
1854 
1855  try
1856  {
1857  procIn.open(procN.str());
1858  if(procIn.good()) procIn >> pidCmdLine;
1859  procIn.close();
1860  }
1861  catch( ... )
1862  {
1863  log<software_critical>({__FILE__, __LINE__, 0, 0, "exception caught testing /proc/pid"});
1864  return -1;
1865  }
1866 
1867  //If pidCmdLine == "" at this point we just allow the rest of the
1868  //logic to run...
1869 
1870  //Search for invokedName in command line.
1871  size_t invokedPos = pidCmdLine.find( invokedName );
1872 
1873  //If invokedName found, then we check for configName.
1874  size_t configPos = std::string::npos;
1875  if(invokedPos != std::string::npos) configPos = pidCmdLine.find( m_configName );
1876 
1877  //Check if PID is already locked by this program+config combo:
1878  if( invokedPos != std::string::npos && configPos != std::string::npos)
1879  {
1880  //This means that this app already exists for this config, and we need to die.
1881  std::stringstream logss;
1882  logss << "PID already locked (" << testPid << "). Time to die.";
1883  std::cerr << logss.str() << std::endl;
1884 
1885  log<text_log>(logss.str(), logPrio::LOG_CRITICAL);
1886 
1887  return -1;
1888  }
1889  }
1890  else
1891  {
1892  //No PID File so we should just go on.
1893  pidIn.close();
1894  }
1895  }
1896 
1897  //Now write current PID to file and go on with life.
1898  std::ofstream pidOut;
1899  pidOut.open(pidFileName);
1900 
1901  if(!pidOut.good())
1902  {
1903  log<software_critical>({__FILE__, __LINE__, errno, 0, "could not open pid file for writing."});
1904  //euidReal();
1905  return -1;
1906  }
1907 
1908  pidOut << m_pid;
1909 
1910  pidOut.close();
1911 
1912  std::stringstream logss;
1913  logss << "PID (" << m_pid << ") locked.";
1914  log<text_log>(logss.str());
1915 
1916  //Go back to regular privileges
1917  /*if( euidReal() < 0 )
1918  {
1919  log<software_error>({__FILE__, __LINE__, 0, 0, "Seeting euid to real failed."});
1920  return -1;
1921  }*/
1922 
1923  return 0;
1924 }
1925 
1926 template<bool _useINDI>
1928 {
1929  {//scope for elPriv
1930 
1931  //Get the maximum privileges available
1932  elevatedPrivileges elPriv(this);
1933 
1934  if( ::remove(pidFileName.c_str()) < 0)
1935  {
1936  log<software_error>({__FILE__, __LINE__, errno, 0, std::string("Failed to remove PID file: ") + strerror(errno)});
1937  return -1;
1938  }
1939  }
1940 
1941  std::stringstream logss;
1942  logss << "PID (" << m_pid << ") unlocked.";
1943  log<text_log>(logss.str());
1944 
1945  return 0;
1946 }
1947 
1948 template<bool _useINDI>
1949 template<class thisPtr, class Function>
1950 int MagAOXApp<_useINDI>::threadStart( std::thread & thrd,
1951  bool & thrdInit,
1952  pid_t & tpid,
1953  pcf::IndiProperty & thProp,
1954  int thrdPrio,
1955  const std::string & cpuset,
1956  const std::string & thrdName,
1957  thisPtr * thrdThis,
1958  Function&& thrdStart
1959  )
1960 {
1961  thrdInit = true;
1962 
1963  tpid = 0;
1964 
1965  try
1966  {
1967  thrd = std::thread( thrdStart, thrdThis);
1968  }
1969  catch( const std::exception & e )
1970  {
1971  log<software_error>({__FILE__,__LINE__, std::string("Exception on " + thrdName + " thread start: ") + e.what()});
1972  return -1;
1973  }
1974  catch( ... )
1975  {
1976  log<software_error>({__FILE__,__LINE__, "Unkown exception on " + thrdName + " thread start"});
1977  return -1;
1978  }
1979 
1980  if(!thrd.joinable())
1981  {
1982  log<software_error>({__FILE__, __LINE__, thrdName + " thread did not start"});
1983  return -1;
1984  }
1985 
1986  //Now set the RT priority.
1987 
1988  if(thrdPrio < 0) thrdPrio = 0;
1989  if(thrdPrio > 99) thrdPrio = 99;
1990 
1991  sched_param sp;
1992  sp.sched_priority = thrdPrio;
1993 
1994  int rv = 0;
1995 
1996  {//scope for elPriv
1997  //Get the maximum privileges available
1998  elevatedPrivileges elPriv(this);
1999 
2000  //We set return value based on result from sched_setscheduler
2001  //But we make sure to restore privileges no matter what happens.
2002  errno = 0;
2003  if(thrdPrio > 0) rv = pthread_setschedparam(thrd.native_handle(), MAGAOX_RT_SCHED_POLICY, &sp);
2004  else rv = pthread_setschedparam(thrd.native_handle(), SCHED_OTHER, &sp);
2005  }
2006 
2007  if(rv < 0)
2008  {
2009  log<software_error>({__FILE__, __LINE__, errno, "Setting " + thrdName + " thread scheduler priority to " + std::to_string(thrdPrio) + " failed."});
2010  }
2011  else
2012  {
2013  log<text_log>(thrdName + " thread scheduler priority set to " + std::to_string(thrdPrio));
2014  }
2015 
2016  // Wait for tpid to be filled in, but only for one total second.
2017  if(tpid == 0)
2018  {
2019  for(int i=0;i<10;++i)
2020  {
2021  mx::sys::milliSleep(100);
2022  if(tpid!=0) break;
2023  }
2024  }
2025 
2026  if(tpid == 0)
2027  {
2028  return log<software_error,-1>({__FILE__, __LINE__, errno, "tpid for " + thrdName + " not set."});
2029  }
2030  else
2031  {
2032  log<text_log>(thrdName + " thread pid is " + std::to_string(tpid));
2033 
2034  if(_useINDI)
2035  {
2036  thProp = pcf::IndiProperty(pcf::IndiProperty::Number);
2037  thProp.setDevice(configName());
2038  thProp.setName(std::string("th-") + thrdName);
2039  thProp.setPerm(pcf::IndiProperty::ReadOnly);
2040  thProp.setState(pcf::IndiProperty::Idle);
2041  thProp.add(pcf::IndiElement("pid"));
2042  thProp["pid"] = tpid;
2043  thProp.add(pcf::IndiElement("prio"));
2044  thProp["prio"] = thrdPrio;
2045  registerIndiPropertyReadOnly(thProp);
2046  }
2047 
2048  if(cpuset != "")
2049  {
2050  elevatedPrivileges ep(this);
2051  std::string cpuFile = m_cpusetPath;
2052  cpuFile += "/" + cpuset;
2053  cpuFile += "/tasks";
2054  int wfd = open( cpuFile.c_str(), O_WRONLY);
2055  if(wfd < 0)
2056  {
2057  return log<software_error,-1>({__FILE__, __LINE__, errno, "error from open for " + cpuFile});
2058  }
2059 
2060  char pids[16];
2061  snprintf(pids, sizeof(pids), "%d", tpid);
2062 
2063  int w = write(wfd, pids,strnlen(pids, sizeof(pids)));
2064  if(w != (int) strlen(pids))
2065  {
2066  return log<software_error,-1>({__FILE__, __LINE__, errno, "error on write"});
2067  }
2068 
2069  close(wfd);
2070 
2071  log<text_log>("moved " + thrdName + " to cpuset " + cpuset, logPrio::LOG_NOTICE);
2072  }
2073  }
2074 
2075  thrdInit = false;
2076 
2077  return 0;
2078 
2079 }
2080 
2081 template<bool _useINDI>
2083 {
2084  return m_state;
2085 }
2086 
2087 template<bool _useINDI>
2089  bool stateAlert
2090  )
2091 {
2092  //Only log anything if it's a change
2093  if(m_state != s)
2094  {
2096  if(s == stateCodes::ERROR) lvl = logPrio::LOG_ERROR;
2097  if(s == stateCodes::FAILURE) lvl = logPrio::LOG_CRITICAL;
2098 
2099  log<state_change>( {m_state, s}, lvl );
2100 
2101  m_state = s;
2102  m_stateLogged = 0;
2103  }
2104 
2105  if(m_stateAlert != stateAlert && stateAlert == true)
2106  {
2107  m_stateAlert = stateAlert;
2108  log<text_log>("FSM alert set", logPrio::LOG_WARNING );
2109 
2110  }
2111 
2112  //Check to make sure INDI is up to date
2113  std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock); //Lock the mutex before conducting INDI communications.
2114 
2115  //Note this is called every execute loop to make sure we update eventually
2116  if(lock.owns_lock())
2117  {
2118  ///\todo move this to a function in stateCodes
2119  pcf::IndiProperty::PropertyStateType stst = INDI_IDLE;
2120 
2121  //If it's already in the "ALERT" state, then this can't take it out of it.
2122  if(m_stateAlert == true)
2123  {
2124  stst = INDI_ALERT;
2125  }
2126  else
2127  {
2128  if(m_state == stateCodes::READY) stst = INDI_OK;
2129  else if(m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING || m_state == stateCodes::CONFIGURING) stst = INDI_BUSY;
2130  else if( m_state < stateCodes::NODEVICE ) stst = INDI_ALERT;
2131  else if (m_state <= stateCodes::LOGGEDIN ) stst = INDI_IDLE;
2132  else if (m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN) stst = INDI_IDLE;
2133  }
2134 
2135  updateIfChanged(m_indiP_state, "state", stateCodes::codeText(m_state), stst);
2136  }
2137 }
2138 
2139 template<bool _useINDI>
2141 {
2142  if(m_stateLogged > 0)
2143  {
2144  ++m_stateLogged;
2145  return m_stateLogged - 1;
2146  }
2147  else
2148  {
2149  m_stateLogged = 1;
2150  return 0;
2151  }
2152 }
2153 
2154 template<bool _useINDI>
2156 {
2157  if(m_stateAlert == false) return 0;
2158  m_stateAlert = false;
2159  log<text_log>("FSM alert cleared", logPrio::LOG_WARNING );
2160 
2161  pcf::IndiProperty::PropertyStateType stst = INDI_IDLE;
2162 
2163  if(m_state == stateCodes::READY) stst = INDI_OK;
2164  else if(m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING || m_state == stateCodes::CONFIGURING) stst = INDI_BUSY;
2165  else if( m_state < stateCodes::NODEVICE ) stst = INDI_ALERT;
2166  else if (m_state <= stateCodes::LOGGEDIN ) stst = INDI_IDLE;
2167  else if (m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN) stst = INDI_IDLE;
2168 
2169  updateIfChanged(m_indiP_state, "state", stateCodes::codeText(m_state), stst);
2170 
2171  return 0;
2172 
2173 }
2174 
2175 /*-------------------------------------------------------------------------------------*/
2176 /* INDI Support */
2177 /*-------------------------------------------------------------------------------------*/
2178 
2179 template<bool _useINDI>
2180 int MagAOXApp<_useINDI>::createStandardIndiText( pcf::IndiProperty & prop,
2181  const std::string & propName,
2182  const std::string & label,
2183  const std::string & group
2184  )
2185 {
2186  prop = pcf::IndiProperty(pcf::IndiProperty::Text);
2187  prop.setDevice(configName());
2188  prop.setName(propName);
2189  prop.setPerm(pcf::IndiProperty::ReadWrite);
2190  prop.setState(pcf::IndiProperty::Idle);
2191  prop.add(pcf::IndiElement("current"));
2192  prop.add(pcf::IndiElement("target"));
2193 
2194  //Don't set "" just in case libcommon does something with defaults
2195  if(label != "")
2196  {
2197  prop.setLabel(label);
2198  }
2199 
2200  if(group != "")
2201  {
2202  prop.setGroup(group);
2203  }
2204 
2205  return 0;
2206 }
2207 
2208 template<bool _useINDI>
2209 int MagAOXApp<_useINDI>::createROIndiText( pcf::IndiProperty & prop,
2210  const std::string & propName,
2211  const std::string & elName,
2212  const std::string & propLabel,
2213  const std::string & propGroup,
2214  const std::string & elLabel
2215  )
2216 {
2217  prop = pcf::IndiProperty(pcf::IndiProperty::Text);
2218  prop.setDevice(configName());
2219  prop.setName(propName);
2220  prop.setPerm(pcf::IndiProperty::ReadOnly);
2221  prop.setState(pcf::IndiProperty::Idle);
2222 
2223  //Don't set "" just in case libcommon does something with defaults
2224  if(propLabel != "")
2225  {
2226  prop.setLabel(propLabel);
2227  }
2228 
2229  if(propGroup != "")
2230  {
2231  prop.setGroup(propGroup);
2232  }
2233 
2234  prop.add(pcf::IndiElement(elName));
2235 
2236  if(elLabel != "")
2237  {
2238  prop[elName].setLabel(elLabel);
2239  }
2240 
2241  return 0;
2242 }
2243 
2244 template<bool _useINDI>
2245 template<typename T>
2246 int MagAOXApp<_useINDI>::createStandardIndiNumber( pcf::IndiProperty & prop,
2247  const std::string & name,
2248  const T & min,
2249  const T & max,
2250  const T & step,
2251  const std::string & format,
2252  const std::string & label,
2253  const std::string & group
2254  )
2255 {
2256  prop = pcf::IndiProperty(pcf::IndiProperty::Number);
2257  prop.setDevice(configName());
2258  prop.setName(name);
2259  prop.setPerm(pcf::IndiProperty::ReadWrite);
2260  prop.setState(pcf::IndiProperty::Idle);
2261  prop.add(pcf::IndiElement("current"));
2262  prop["current"].setMin(min);
2263  prop["current"].setMax(max);
2264  prop["current"].setStep(step);
2265  if(format != "") //don't override defaults
2266  {
2267  prop["current"].setFormat(format);
2268  }
2269 
2270  prop.add(pcf::IndiElement("target"));
2271  prop["target"].setMin(min);
2272  prop["target"].setMax(max);
2273  prop["target"].setStep(step);
2274  if(format != "") //don't override defaults
2275  {
2276  prop["target"].setFormat(format);
2277  }
2278 
2279  //Don't set "" just in case libcommon does something with defaults
2280  if(label != "")
2281  {
2282  prop.setLabel(label);
2283  }
2284 
2285  if(group != "")
2286  {
2287  prop.setGroup(group);
2288  }
2289 
2290  return 0;
2291 }
2292 
2293 template<bool _useINDI>
2294 int MagAOXApp<_useINDI>::createROIndiNumber( pcf::IndiProperty & prop,
2295  const std::string & propName,
2296  const std::string & propLabel,
2297  const std::string & propGroup
2298  )
2299 {
2300  prop = pcf::IndiProperty(pcf::IndiProperty::Number);
2301  prop.setDevice(configName());
2302  prop.setName(propName);
2303  prop.setPerm(pcf::IndiProperty::ReadOnly);
2304  prop.setState(pcf::IndiProperty::Idle);
2305 
2306  //Don't set "" just in case libcommon does something with defaults
2307  if(propLabel != "")
2308  {
2309  prop.setLabel(propLabel);
2310  }
2311 
2312  if(propGroup != "")
2313  {
2314  prop.setGroup(propGroup);
2315  }
2316 
2317  return 0;
2318 }
2319 
2320 template<bool _useINDI>
2322  const std::string & name,
2323  const std::string & label,
2324  const std::string & group
2325  )
2326 {
2327  prop = pcf::IndiProperty(pcf::IndiProperty::Switch);
2328  prop.setDevice(configName());
2329  prop.setName(name);
2330  prop.setPerm(pcf::IndiProperty::ReadWrite);
2331  prop.setState(pcf::IndiProperty::Idle);
2332  prop.setRule(pcf::IndiProperty::AtMostOne);
2333 
2334  //Add the toggle element initialized to Off
2335  prop.add(pcf::IndiElement("toggle", pcf::IndiElement::Off));
2336 
2337  //Don't set "" just in case libcommon does something with defaults
2338  if(label != "")
2339  {
2340  prop.setLabel(label);
2341  }
2342 
2343  if(group != "")
2344  {
2345  prop.setGroup(group);
2346  }
2347 
2348  return 0;
2349 }
2350 
2351 template<bool _useINDI>
2353  const std::string & name,
2354  const std::string & label,
2355  const std::string & group
2356  )
2357 {
2358  prop = pcf::IndiProperty(pcf::IndiProperty::Switch);
2359  prop.setDevice(configName());
2360  prop.setName(name);
2361  prop.setPerm(pcf::IndiProperty::ReadWrite);
2362  prop.setState(pcf::IndiProperty::Idle);
2363  prop.setRule(pcf::IndiProperty::AtMostOne);
2364 
2365  //Add the toggle element initialized to Off
2366  prop.add(pcf::IndiElement("request", pcf::IndiElement::Off));
2367 
2368  //Don't set "" just in case libcommon does something with defaults
2369  if(label != "")
2370  {
2371  prop.setLabel(label);
2372  }
2373 
2374  if(group != "")
2375  {
2376  prop.setGroup(group);
2377  }
2378 
2379  return 0;
2380 }
2381 
2382 template<bool _useINDI>
2384  const std::string & name,
2385  const std::vector<std::string> & elements,
2386  const std::vector<std::string> & elementLabels,
2387  const std::string & label,
2388  const std::string & group
2389  )
2390 {
2391  if(elements.size() == 0)
2392  {
2393  return log<software_error,-1>({__FILE__,__LINE__, "elements vector has zero size"});
2394  }
2395 
2396  prop = pcf::IndiProperty(pcf::IndiProperty::Switch);
2397  prop.setDevice(configName());
2398  prop.setName(name);
2399  prop.setPerm(pcf::IndiProperty::ReadWrite);
2400  prop.setState(pcf::IndiProperty::Idle);
2401  prop.setRule(pcf::IndiProperty::OneOfMany);
2402 
2403  //Add the toggle element initialized to Off
2404  for(size_t n=0; n < elements.size(); ++n)
2405  {
2406  pcf::IndiElement elem = pcf::IndiElement(elements[n], pcf::IndiElement::Off);
2407  elem.setLabel(elementLabels[n]);
2408  prop.add(elem);
2409  }
2410 
2411  //Don't set "" just in case libcommon does something with defaults
2412  if(label != "")
2413  {
2414  prop.setLabel(label);
2415  }
2416 
2417  if(group != "")
2418  {
2419  prop.setGroup(group);
2420  }
2421 
2422  return 0;
2423 }
2424 
2425 template<bool _useINDI>
2427  const std::string & name,
2428  const std::vector<std::string> & elements,
2429  const std::string & label,
2430  const std::string & group
2431  )
2432 {
2433  return createStandardIndiSelectionSw(prop, name, elements, elements, label, group);
2434 }
2435 
2436 template<bool _useINDI>
2438 {
2439  if(!m_useINDI) return 0;
2440 
2441  callBackInsertResult result = m_indiNewCallBacks.insert(callBackValueType( prop.createUniqueKey(), {&prop, nullptr}));
2442 
2443  try
2444  {
2445  if(!result.second)
2446  {
2447  return log<software_error,-1>({__FILE__, __LINE__, "failed to insert INDI property: " + prop.createUniqueKey()});
2448  }
2449  }
2450  catch( std::exception & e)
2451  {
2452  return log<software_error, -1>({__FILE__, __LINE__, std::string("Exception caught: ") + e.what()});
2453  }
2454  catch(...)
2455  {
2456  return log<software_error, -1>({__FILE__, __LINE__, "Unknown exception caught."});
2457  }
2458 
2459  return 0;
2460 }
2461 
2462 template<bool _useINDI>
2464  const std::string & propName,
2465  const pcf::IndiProperty::Type & propType,
2466  const pcf::IndiProperty::PropertyPermType & propPerm,
2467  const pcf::IndiProperty::PropertyStateType & propState
2468  )
2469 {
2470  if(!m_useINDI) return 0;
2471 
2472  prop = pcf::IndiProperty (propType);
2473  prop.setDevice(m_configName);
2474  prop.setName(propName);
2475  prop.setPerm(propPerm);
2476  prop.setState( propState);
2477 
2478 
2479  callBackInsertResult result = m_indiNewCallBacks.insert(callBackValueType( propName, {&prop, nullptr}));
2480 
2481  try
2482  {
2483  if(!result.second)
2484  {
2485  return log<software_error,-1>({__FILE__, __LINE__, "failed to insert INDI property: " + prop.createUniqueKey()});
2486  }
2487  }
2488  catch( std::exception & e)
2489  {
2490  return log<software_error, -1>({__FILE__, __LINE__, std::string("Exception caught: ") + e.what()});
2491  }
2492  catch(...)
2493  {
2494  return log<software_error, -1>({__FILE__, __LINE__, "Unknown exception caught."});
2495  }
2496  return 0;
2497 }
2498 
2499 template<bool _useINDI>
2500 int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty & prop,
2501  int (*callBack)( void *, const pcf::IndiProperty &ipRecv)
2502  )
2503 {
2504  if(!m_useINDI) return 0;
2505 
2506  try
2507  {
2508  callBackInsertResult result = m_indiNewCallBacks.insert(callBackValueType( prop.createUniqueKey(), {&prop, callBack}));
2509 
2510  if(!result.second)
2511  {
2512  return log<software_error,-1>({__FILE__, __LINE__, "failed to insert INDI property: " + prop.createUniqueKey()});
2513  }
2514  }
2515  catch( std::exception & e)
2516  {
2517  return log<software_error, -1>({__FILE__, __LINE__, std::string("Exception caught: ") + e.what()});
2518  }
2519  catch(...)
2520  {
2521  return log<software_error, -1>({__FILE__, __LINE__, "Unknown exception caught."});
2522  }
2523 
2524  return 0;
2525 }
2526 
2527 template<bool _useINDI>
2528 int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty & prop,
2529  const std::string & propName,
2530  const pcf::IndiProperty::Type & propType,
2531  const pcf::IndiProperty::PropertyPermType & propPerm,
2532  const pcf::IndiProperty::PropertyStateType & propState,
2533  int (*callBack)( void *, const pcf::IndiProperty &ipRecv)
2534  )
2535 {
2536  if(!m_useINDI) return 0;
2537 
2538  prop = pcf::IndiProperty (propType);
2539  prop.setDevice(m_configName);
2540  prop.setName(propName);
2541  prop.setPerm(propPerm);
2542  prop.setState( propState);
2543 
2544  return registerIndiPropertyNew(prop, callBack);
2545 }
2546 
2547 template<bool _useINDI>
2548 int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty & prop,
2549  const std::string & propName,
2550  const pcf::IndiProperty::Type & propType,
2551  const pcf::IndiProperty::PropertyPermType & propPerm,
2552  const pcf::IndiProperty::PropertyStateType & propState,
2553  const pcf::IndiProperty::SwitchRuleType & propRule,
2554  int (*callBack)( void *, const pcf::IndiProperty &ipRecv)
2555  )
2556 {
2557  if(!m_useINDI) return 0;
2558 
2559  prop = pcf::IndiProperty (propType);
2560  prop.setDevice(m_configName);
2561  prop.setName(propName);
2562  prop.setPerm(propPerm);
2563  prop.setState( propState);
2564  prop.setRule( propRule);
2565  return registerIndiPropertyNew(prop, callBack);
2566 }
2567 
2568 template<bool _useINDI>
2569 int MagAOXApp<_useINDI>::registerIndiPropertySet( pcf::IndiProperty & prop,
2570  const std::string & devName,
2571  const std::string & propName,
2572  int (*callBack)( void *, const pcf::IndiProperty &ipRecv)
2573  )
2574 {
2575  if(!m_useINDI) return 0;
2576 
2577  prop = pcf::IndiProperty();
2578  prop.setDevice(devName);
2579  prop.setName(propName);
2580 
2581  callBackInsertResult result = m_indiSetCallBacks.insert(callBackValueType( prop.createUniqueKey(), {&prop, callBack}));
2582 
2583  try
2584  {
2585  if(!result.second)
2586  {
2587  return log<software_error,-1>({__FILE__, __LINE__, "failed to insert INDI property: " + prop.createUniqueKey()});
2588  }
2589  }
2590  catch( std::exception & e)
2591  {
2592  return log<software_error, -1>({__FILE__, __LINE__, std::string("Exception caught: ") + e.what()});
2593  }
2594  catch(...)
2595  {
2596  return log<software_error, -1>({__FILE__, __LINE__, "Unknown exception caught."});
2597  }
2598 
2599  return 0;
2600 }
2601 
2602 template<bool _useINDI>
2604 {
2605  if(!m_useINDI) return 0;
2606 
2607  ///\todo make driver FIFO path full configurable.
2608  std::string driverFIFOPath = MAGAOX_path;
2609  driverFIFOPath += "/";
2610  driverFIFOPath += MAGAOX_driverFIFORelPath;
2611 
2612  m_driverInName = driverFIFOPath + "/" + configName() + ".in";
2613  m_driverOutName = driverFIFOPath + "/" + configName() + ".out";
2614  m_driverCtrlName = driverFIFOPath + "/" + configName() + ".ctrl";
2615 
2616  //Get max permissions
2617  elevatedPrivileges elPriv(this);
2618 
2619  //Clear the file mode creation mask so mkfifo does what we want. Don't forget to restore it.
2620  mode_t prev = umask(0);
2621 
2622  errno = 0;
2623  if(mkfifo(m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) !=0)
2624  {
2625  if(errno != EEXIST)
2626  {
2627  umask(prev);
2628  log<software_critical>({__FILE__, __LINE__, errno, 0, "mkfifo failed"});
2629  log<text_log>("Failed to create input FIFO.", logPrio::LOG_CRITICAL);
2630  return -1;
2631  }
2632  }
2633 
2634  errno = 0;
2635  if(mkfifo(m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) !=0 )
2636  {
2637  if(errno != EEXIST)
2638  {
2639  umask(prev);
2640  //euidReal();
2641  log<software_critical>({__FILE__, __LINE__, errno, 0, "mkfifo failed"});
2642  log<text_log>("Failed to create ouput FIFO.", logPrio::LOG_CRITICAL);
2643  return -1;
2644  }
2645  }
2646 
2647  errno = 0;
2648  if(mkfifo(m_driverCtrlName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) !=0 )
2649  {
2650  if(errno != EEXIST)
2651  {
2652  umask(prev);
2653  //euidReal();
2654  log<software_critical>({__FILE__, __LINE__, errno, 0, "mkfifo failed"});
2655  log<text_log>("Failed to create ouput FIFO.", logPrio::LOG_CRITICAL);
2656  return -1;
2657  }
2658  }
2659 
2660  umask(prev);
2661  //euidReal();
2662  return 0;
2663 }
2664 
2665 template<bool _useINDI>
2667 {
2668  if(!m_useINDI) return 0;
2669 
2670 
2671  //===== Create the FIFOs for INDI communications ====
2672  if(createINDIFIFOS() < 0)
2673  {
2674  return -1;
2675  }
2676 
2677  //======= Instantiate the indiDriver
2678  try
2679  {
2680  if(m_indiDriver != nullptr)
2681  {
2682  m_indiDriver->quitProcess();
2683  m_indiDriver->deactivate();
2684  log<indidriver_stop>();
2685  delete m_indiDriver;
2686  m_indiDriver = nullptr;
2687  }
2688 
2689  m_indiDriver = new indiDriver<MagAOXApp>(this, m_configName, "0", "0");
2690  }
2691  catch(...)
2692  {
2693  log<software_critical>({__FILE__, __LINE__, 0, 0, "INDI Driver construction exception."});
2694  return -1;
2695  }
2696 
2697  //Check for INDI failure
2698  if(m_indiDriver == nullptr)
2699  {
2700  log<software_critical>({__FILE__, __LINE__, 0, 0, "INDI Driver construction failed."});
2701  return -1;
2702  }
2703 
2704  //Check for INDI failure to open the FIFOs
2705  if(m_indiDriver->good() == false)
2706  {
2707  log<software_critical>({__FILE__, __LINE__, 0, 0, "INDI Driver failed to open FIFOs."});
2708  delete m_indiDriver;
2709  m_indiDriver = nullptr;
2710  return -1;
2711  }
2712 
2713  //======= Now we start talkin'
2714  m_indiDriver->activate();
2715  log<indidriver_start>();
2716 
2717  sendGetPropertySetList(true);
2718 
2719  return 0;
2720 }
2721 
2722 template<bool _useINDI>
2724 {
2725  //Unless forced by all, we only do anything if allDefs are not received yet
2726  if(!all && m_allDefsReceived) return;
2727 
2728  callBackIterator it = m_indiSetCallBacks.begin();
2729 
2730  int nowFalse = 0;
2731  while(it != m_indiSetCallBacks.end() )
2732  {
2733  if(all || it->second.m_defReceived == false)
2734  {
2735  if( it->second.property )
2736  {
2737  try
2738  {
2739  m_indiDriver->sendGetProperties( *(it->second.property) );
2740  }
2741  catch(const std::exception & e)
2742  {
2743  log<software_error>({__FILE__, __LINE__, "exception caught from sendGetProperties for " +
2744  it->second.property->getName() + ": " + e.what()});
2745  }
2746  }
2747 
2748  it->second.m_defReceived = false;
2749  ++nowFalse;
2750 
2751  }
2752  ++it;
2753  }
2754  if(nowFalse != 0) m_allDefsReceived = false;
2755  if(nowFalse == 0) m_allDefsReceived = true;
2756 }
2757 
2758 template<bool _useINDI>
2759 void MagAOXApp<_useINDI>::handleDefProperty( const pcf::IndiProperty &ipRecv )
2760 {
2761  handleSetProperty(ipRecv); //We have the same response to both Def and Set.
2762 }
2763 
2764 template<bool _useINDI>
2765 void MagAOXApp<_useINDI>::handleGetProperties( const pcf::IndiProperty &ipRecv )
2766 {
2767  if(!m_useINDI) return;
2768  if(m_indiDriver == nullptr) return;
2769 
2770  //Ignore if not our device
2771  if (ipRecv.hasValidDevice() && ipRecv.getDevice() != m_indiDriver->getName())
2772  {
2773  return;
2774  }
2775 
2776  //Send all properties if requested.
2777  if( !ipRecv.hasValidName() )
2778  {
2779  callBackIterator it = m_indiNewCallBacks.begin();
2780 
2781  while(it != m_indiNewCallBacks.end() )
2782  {
2783  if( it->second.property )
2784  {
2785  try
2786  {
2787  m_indiDriver->sendDefProperty( *(it->second.property) );
2788  }
2789  catch(const std::exception & e)
2790  {
2791  log<software_error>({__FILE__, __LINE__, "exception caught from sendDefProperty for " +
2792  it->second.property->getName() + ": " + e.what()});
2793  }
2794  }
2795  ++it;
2796  }
2797 
2798  //This is a possible INDI server restart, so we re-register for all notifications.
2799  sendGetPropertySetList(true);
2800 
2801  return;
2802  }
2803 
2804  //Check if we actually have this.
2805  if( m_indiNewCallBacks.count(ipRecv.createUniqueKey()) == 0)
2806  {
2807  return;
2808  }
2809 
2810  //Otherwise send just the requested property, if property is not null
2811  if(m_indiNewCallBacks[ ipRecv.createUniqueKey() ].property)
2812  {
2813  try
2814  {
2815  m_indiDriver->sendDefProperty( *(m_indiNewCallBacks[ ipRecv.createUniqueKey() ].property) );
2816  }
2817  catch(const std::exception & e)
2818  {
2819  log<software_error>({__FILE__, __LINE__, "exception caught from sendDefProperty for " +
2820  m_indiNewCallBacks[ ipRecv.createUniqueKey() ].property->getName() + ": " + e.what()});
2821  }
2822  }
2823  return;
2824 }
2825 
2826 template<bool _useINDI>
2827 void MagAOXApp<_useINDI>::handleNewProperty( const pcf::IndiProperty &ipRecv )
2828 {
2829  if(!m_useINDI) return;
2830  if(m_indiDriver == nullptr) return;
2831 
2832  //Check if this is a valid name for us.
2833  if( m_indiNewCallBacks.count(ipRecv.createUniqueKey()) == 0 )
2834  {
2835  log<software_debug>({__FILE__, __LINE__, "invalid NewProperty request for " + ipRecv.createUniqueKey()});
2836  return;
2837  }
2838 
2839  int (*callBack)(void *, const pcf::IndiProperty &) = m_indiNewCallBacks[ ipRecv.createUniqueKey() ].callBack;
2840 
2841  if(callBack) callBack( this, ipRecv);
2842 
2843  log<software_debug>({__FILE__, __LINE__, "NewProperty callback null for " + ipRecv.createUniqueKey()});
2844 
2845  return;
2846 }
2847 
2848 template<bool _useINDI>
2849 void MagAOXApp<_useINDI>::handleSetProperty( const pcf::IndiProperty &ipRecv )
2850 {
2851  if(!m_useINDI) return;
2852  if(m_indiDriver == nullptr) return;
2853 
2854  std::string key = ipRecv.createUniqueKey();
2855 
2856  //Check if this is valid
2857  if( m_indiSetCallBacks.count(key) > 0 )
2858  {
2859  m_indiSetCallBacks[ key ].m_defReceived = true; //record that we got this Def/Set
2860 
2861  //And call the callback
2862  int (*callBack)(void *, const pcf::IndiProperty &) = m_indiSetCallBacks[ key ].callBack;
2863  if(callBack) callBack( this, ipRecv);
2864 
2865  ///\todo log an error here because callBack should not be null
2866  }
2867  else
2868  {
2869  ///\todo log invalid SetProperty request.
2870  }
2871 
2872  return;
2873 }
2874 
2875 template<bool _useINDI>
2876 template<typename T>
2877 void MagAOXApp<_useINDI>::updateIfChanged( pcf::IndiProperty & p,
2878  const std::string & el,
2879  const T & newVal,
2880  pcf::IndiProperty::PropertyStateType ipState
2881  )
2882 {
2883  if(!_useINDI) return;
2884 
2885  if(!m_indiDriver) return;
2886 
2887  indi::updateIfChanged( p, el, newVal, m_indiDriver, ipState);
2888 }
2889 
2890 template<bool _useINDI>
2891 void MagAOXApp<_useINDI>::updateIfChanged( pcf::IndiProperty & p,
2892  const std::string & el,
2893  const char * newVal,
2894  pcf::IndiProperty::PropertyStateType ipState
2895  )
2896 {
2897  updateIfChanged<std::string>(p,el, std::string(newVal), ipState);
2898 }
2899 
2900 template<bool _useINDI>
2902  const std::string & el,
2903  const pcf::IndiElement::SwitchStateType & newVal,
2904  pcf::IndiProperty::PropertyStateType ipState
2905  )
2906 {
2907  if(!_useINDI) return;
2908 
2909  if(!m_indiDriver) return;
2910 
2911  indi::updateSwitchIfChanged( p, el, newVal, m_indiDriver, ipState);
2912 }
2913 
2914 template<bool _useINDI>
2915 template<typename T>
2916 void MagAOXApp<_useINDI>::updateIfChanged( pcf::IndiProperty & p,
2917  const std::string & el,
2918  const std::vector<T> & newVals,
2919  pcf::IndiProperty::PropertyStateType ipState
2920  )
2921 {
2922  if(!_useINDI) return;
2923 
2924  if(!m_indiDriver) return;
2925 
2926  std::vector<std::string> descriptors(newVals.size(), el);
2927  for (size_t index = 0; index < newVals.size(); ++index)
2928  {
2929  descriptors[index] += std::to_string(index);
2930  }
2931  indi::updateIfChanged(p, descriptors, newVals, m_indiDriver);
2932 }
2933 
2934 template<bool _useINDI>
2935 template<typename T>
2936 void MagAOXApp<_useINDI>::updateIfChanged( pcf::IndiProperty & p,
2937  const std::vector<std::string> & els,
2938  const std::vector<T> & newVals
2939  )
2940 {
2941  if(!_useINDI) return;
2942 
2943  if(!m_indiDriver) return;
2944 
2945  indi::updateIfChanged(p, els, newVals, m_indiDriver);
2946 }
2947 
2948 
2949 template<bool _useINDI>
2950 template<typename T>
2951 int MagAOXApp<_useINDI>::indiTargetUpdate( pcf::IndiProperty & localProperty,
2952  T & localTarget,
2953  const pcf::IndiProperty & remoteProperty,
2954  bool setBusy
2955  )
2956 {
2957  if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey())
2958  {
2959  return log<text_log,-1>("INDI property names do not match", logPrio::LOG_ERROR);
2960  }
2961 
2962  if( ! (remoteProperty.find("target") || remoteProperty.find("current") ) )
2963  {
2964  return log<text_log,-1>("no target or current element in INDI property", logPrio::LOG_ERROR);
2965  }
2966 
2967  bool set = false;
2968 
2969  if( remoteProperty.find("target") )
2970  {
2971  localTarget = remoteProperty["target"].get<T>();
2972  set = true;
2973  }
2974 
2975  if( !set )
2976  {
2977  if( remoteProperty.find("current") )
2978  {
2979  localTarget = remoteProperty["current"].get<T>();
2980  set = true;
2981  }
2982  }
2983 
2984  if( !set )
2985  {
2986  return log<text_log,-1>("no non-empty value found in INDI property", logPrio::LOG_ERROR);
2987  }
2988 
2989  if(setBusy)
2990  {
2991  updateIfChanged(localProperty, "target", localTarget, INDI_BUSY);
2992  }
2993  else
2994  {
2995  updateIfChanged(localProperty, "target", localTarget);
2996  }
2997 
2998  return 0;
2999 }
3000 
3001 
3002 
3003 
3004 
3005 
3006 
3007 
3008 
3009 /// \todo move propType to an INDI utils file, and document.
3010 
3011 template<typename T>
3012 pcf::IndiProperty::Type propType()
3013 {
3014  return pcf::IndiProperty::Unknown;
3015 }
3016 
3017 template<>
3018 pcf::IndiProperty::Type propType<char *>();
3019 
3020 template<>
3021 pcf::IndiProperty::Type propType<std::string>();
3022 
3023 template<>
3024 pcf::IndiProperty::Type propType<int>();
3025 
3026 template<>
3027 pcf::IndiProperty::Type propType<double>();
3028 
3029 template<bool _useINDI>
3030 template<typename T>
3031 int MagAOXApp<_useINDI>::sendNewProperty( const pcf::IndiProperty & ipSend,
3032  const std::string & el,
3033  const T & newVal
3034  )
3035 {
3036  if(!_useINDI) return 0;
3037 
3038  if(!m_indiDriver)
3039  {
3040  log<software_error>({__FILE__, __LINE__, "INDI communications not initialized."});
3041  return -1;
3042  }
3043  pcf::IndiProperty ipToSend = ipSend;
3044 
3045  try
3046  {
3047  ipToSend[el].setValue(newVal);
3048  }
3049  catch(...)
3050  {
3051  log<software_error>({__FILE__, __LINE__, "Exception caught setting " + ipSend.createUniqueKey() + "." + el});
3052  return -1;
3053  }
3054 
3055  int rv = m_indiDriver->sendNewProperty(ipToSend);
3056  if(rv < 0)
3057  {
3058  log<software_error>({__FILE__, __LINE__});
3059  return -1;
3060  }
3061 
3062  return 0;
3063 }
3064 
3065 template<bool _useINDI>
3066 int MagAOXApp<_useINDI>::sendNewProperty( const pcf::IndiProperty & ipSend )
3067 {
3068  if(!_useINDI) return 0;
3069 
3070  if(!m_indiDriver)
3071  {
3072  return log<software_error, -1>({__FILE__, __LINE__, "INDI communications not initialized."});
3073  }
3074 
3075  if(m_indiDriver->sendNewProperty(ipSend) < 0)
3076  {
3077  return log<software_error, -1>({__FILE__, __LINE__});
3078  }
3079 
3080  return 0;
3081 }
3082 
3083 template<bool _useINDI>
3084 int MagAOXApp<_useINDI>::sendNewStandardIndiToggle( const std::string & device,
3085  const std::string & property,
3086  bool onoff
3087  )
3088 {
3089  if(!_useINDI) return 0;
3090 
3091  pcf::IndiProperty ipSend(pcf::IndiProperty::Switch);
3092 
3093  try
3094  {
3095  ipSend.setDevice(device);
3096  ipSend.setName(property);
3097  ipSend.add(pcf::IndiElement("toggle"));
3098  }
3099  catch(std::exception & e)
3100  {
3101  return log<software_error,-1>({__FILE__,__LINE__, std::string("exception: ") + e.what()});
3102  }
3103 
3104  if(onoff == false)
3105  {
3106  ipSend["toggle"].setSwitchState(pcf::IndiElement::Off);
3107  }
3108  else
3109  {
3110  ipSend["toggle"].setSwitchState(pcf::IndiElement::On);
3111  }
3112 
3113  if(sendNewProperty(ipSend) < 0)
3114  {
3115  return log<software_error,-1>({__FILE__,__LINE__, "sendNewProperty failed for " + device + "." + property});
3116  }
3117 
3118  return 0;
3119 }
3120 
3121 template<bool _useINDI>
3123  const pcf::IndiProperty &ipRecv
3124  )
3125 {
3126  return static_cast<MagAOXApp<_useINDI> *>(app)->newCallBack_clearFSMAlert(ipRecv);
3127 }
3128 
3129 template<bool _useINDI>
3131 {
3132 
3133  if(ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey())
3134  {
3135  return log<software_error, -1>({__FILE__, __LINE__, "wrong indi property received"});
3136  }
3137 
3138  if(!ipRecv.find("request")) return 0;
3139 
3140  if(ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3141  {
3142  clearFSMAlert();
3143  updateSwitchIfChanged(m_indiP_clearFSMAlert, "request", pcf::IndiElement::Off, INDI_IDLE);
3144  }
3145 
3146 
3147  return 0;
3148 }
3149 
3150 template<bool _useINDI>
3152 {
3153  return 0;
3154 }
3155 
3156 template<bool _useINDI>
3158 {
3159  return 0;
3160 }
3161 
3162 template<bool _useINDI>
3164 {
3165  if(!m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0) return true;
3166 
3167  if(m_powerOnCounter*m_loopPause > ((double) m_powerOnWait)*1e9)
3168  {
3169  return true;
3170  }
3171  else
3172  {
3173  ++m_powerOnCounter;
3174  return false;
3175  }
3176 }
3177 
3178 template<bool _useINDI>
3180 {
3181  if(!m_powerMgtEnabled) return 1;
3182 
3183  return m_powerState;
3184 }
3185 
3186 template<bool _useINDI>
3188 {
3189  if(!m_powerMgtEnabled) return 1;
3190 
3191  return m_powerTargetState;
3192 }
3193 
3194 template<bool _useINDI>
3195 INDI_SETCALLBACK_DEFN( MagAOXApp<_useINDI>, m_indiP_powerChannel)(const pcf::IndiProperty &ipRecv)
3196 {
3197  std::string ps;
3198 
3199  if( ipRecv.find(m_powerElement))
3200  {
3201  ps = ipRecv[m_powerElement].get<std::string>();
3202 
3203  if(ps == "On")
3204  {
3205  m_powerState = 1;
3206  }
3207  else if (ps == "Off")
3208  {
3209  m_powerState = 0;
3210  }
3211  else
3212  {
3213  m_powerState = -1;
3214  }
3215  }
3216 
3217  if( ipRecv.find(m_powerTargetElement))
3218  {
3219  ps = ipRecv[m_powerTargetElement].get<std::string>();
3220 
3221  if(ps == "On")
3222  {
3223  m_powerTargetState = 1;
3224  }
3225  else if (ps == "Off")
3226  {
3227  m_powerTargetState = 0;
3228  }
3229  else
3230  {
3231  m_powerTargetState = -1;
3232  }
3233  }
3234 
3235  return 0;
3236 }
3237 
3238 
3239 
3240 template<bool _useINDI>
3242 {
3243  return m_configName;
3244 }
3245 
3246 template<bool _useINDI>
3248 {
3249  return m_configDir;
3250 }
3251 
3252 template<bool _useINDI>
3254 {
3255  return m_driverInName;
3256 }
3257 
3258 template<bool _useINDI>
3260 {
3261  return m_driverOutName;
3262 }
3263 
3264 template<bool _useINDI>
3266 {
3267  return m_driverCtrlName;
3268 }
3269 
3270 extern template class MagAOXApp<true>;
3271 extern template class MagAOXApp<false>;
3272 
3273 } //namespace app
3274 } //namespace MagAOX
3275 
3276 #endif //app_MagAOXApp_hpp
Internal class to manage setuid privilege escalation with RAII.
Definition: MagAOXApp.hpp:325
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
void handleDefProperty(const pcf::IndiProperty &ipRecv)
Handler for the DEF INDI properties notification.
Definition: MagAOXApp.hpp:2759
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
Definition: MagAOXApp.hpp:2877
void handleSetProperty(const pcf::IndiProperty &ipRecv)
Handler for the set INDI property request.
Definition: MagAOXApp.hpp:2849
int sendNewStandardIndiToggle(const std::string &device, const std::string &property, bool onoff)
Send a new property commmand for a standard toggle switch.
Definition: MagAOXApp.hpp:3084
std::string m_configName
The name of the configuration file (minus .conf).
Definition: MagAOXApp.hpp:88
virtual int onPowerOff()
This method is called when the change to poweroff is detected.
Definition: MagAOXApp.hpp:3151
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
Definition: MagAOXApp.hpp:2352
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
int registerIndiPropertyNew(pcf::IndiProperty &prop, const std::string &propName, const pcf::IndiProperty::Type &propType, const pcf::IndiProperty::PropertyPermType &propPerm, const pcf::IndiProperty::PropertyStateType &propState, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
Definition: MagAOXApp.hpp:2321
void handlerSigTerm(int signum, siginfo_t *siginf, void *ucont)
Handles SIGTERM, SIGQUIT, and SIGINT. Sets m_shutdown to 1 and logs the signal.
Definition: MagAOXApp.hpp:1718
std::pair< callBackIterator, bool > callBackInsertResult
Return type of insert on the indiCallBack map.
Definition: MagAOXApp.hpp:561
std::string driverCtrlName()
Get the INDI control FIFO file name.
Definition: MagAOXApp.hpp:3265
void updateIfChanged(pcf::IndiProperty &p, const std::vector< std::string > &els, const std::vector< T > &newVals)
Update an INDI property if values have changed.
Definition: MagAOXApp.hpp:2936
int createStandardIndiNumber(pcf::IndiProperty &prop, const std::string &name, const T &min, const T &max, const T &step, const std::string &format, const std::string &label="", const std::string &group="")
Create a standard R/W INDI Number property with target and current elements.
Definition: MagAOXApp.hpp:2246
INDI_SETCALLBACK_DECL(MagAOXApp, m_indiP_powerChannel)
int powerState()
Returns the current power state.
Definition: MagAOXApp.hpp:3179
std::string pidFileName
The name of the PID file.
Definition: MagAOXApp.hpp:392
void sendGetPropertySetList(bool all=false)
Definition: MagAOXApp.hpp:2723
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const char *newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
Definition: MagAOXApp.hpp:2891
int setEuidReal()
Set the effective user ID to the real value, i.e. the file owner.
Definition: MagAOXApp.hpp:1776
pcf::IndiProperty m_indiP_powerChannel
INDI property used to communicate power state.
Definition: MagAOXApp.hpp:998
int createINDIFIFOS()
Create the INDI FIFOs.
Definition: MagAOXApp.hpp:2603
int createStandardIndiSelectionSw(pcf::IndiProperty &prop, const std::string &name, const std::vector< std::string > &elements, const std::string &label="", const std::string &group="")
Create a standard R/W INDI selection (one of many) switch with vector of elements using the element s...
Definition: MagAOXApp.hpp:2426
std::string m_powerDevice
The INDI device name of the power controller.
Definition: MagAOXApp.hpp:984
int sendNewProperty(const pcf::IndiProperty &ipSend)
Send a newProperty command to another device (using the INDI Client interface)
Definition: MagAOXApp.hpp:3066
void state(const stateCodes::stateCodeT &s, bool stateAlert=false)
Set the current state code.
Definition: MagAOXApp.hpp:2088
void handleNewProperty(const pcf::IndiProperty &ipRecv)
Handler for the new INDI property request.
Definition: MagAOXApp.hpp:2827
int powerStateTarget()
Returns the target power state.
Definition: MagAOXApp.hpp:3187
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.
Definition: MagAOXApp.hpp:2901
std::pair< std::string, indiCallBack > callBackValueType
Value type of the indiCallBack map.
Definition: MagAOXApp.hpp:555
std::string m_calibDir
The path to calibration files for MagAOX.
Definition: MagAOXApp.hpp:94
std::string configDir()
Get the config directory.
Definition: MagAOXApp.hpp:3247
static int st_newCallBack_clearFSMAlert(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for requesting to clear the FSM alert.
Definition: MagAOXApp.hpp:3122
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2140
int newCallBack_clearFSMAlert(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the FSM Alert Clear request.
Definition: MagAOXApp.hpp:3130
int lockPID()
Attempt to lock the PID by writing it to a file. Fails if a process is already running with the same ...
Definition: MagAOXApp.hpp:1796
MagAOXApp(const std::string &git_sha1, const bool git_modified)
Public c'tor. Handles uid, logs git repo status, and initializes static members.
Definition: MagAOXApp.hpp:1094
void handleGetProperties(const pcf::IndiProperty &ipRecv)
Handler for the get INDI properties request.
Definition: MagAOXApp.hpp:2765
std::string m_configDir
The path to configuration files for MagAOX.
Definition: MagAOXApp.hpp:90
MagAOXApp()=delete
Default c'tor is deleted.
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
Definition: MagAOXApp.hpp:2294
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2437
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const std::vector< T > &newVals, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property if values have changed.
Definition: MagAOXApp.hpp:2916
std::string driverOutName()
Get the INDI output FIFO file name.
Definition: MagAOXApp.hpp:3259
pcf::IndiProperty m_indiP_clearFSMAlert
indi Property to clear an FSM alert.
Definition: MagAOXApp.hpp:948
virtual int whilePowerOff()
This method is called while the power is off, once per FSM loop.
Definition: MagAOXApp.hpp:3157
bool powerOnWaitElapsed()
This method tests whether the power on wait time has elapsed.
Definition: MagAOXApp.hpp:3163
logger::logManager< MagAOXApp< _useINDI >, logFileRaw > logManagerT
The log manager type.
Definition: MagAOXApp.hpp:82
std::string m_powerChannel
The INDI property name of the channel controlling this device's power.
Definition: MagAOXApp.hpp:985
int createROIndiText(pcf::IndiProperty &prop, const std::string &propName, const std::string &elName, const std::string &propLabel="", const std::string &propGroup="", const std::string &elLabel="")
Create a standard ReadOnly INDI Text property, with at least one element.
Definition: MagAOXApp.hpp:2209
std::string secretsPath
Path to the secrets directory, where passwords, etc, are stored.
Definition: MagAOXApp.hpp:98
int clearFSMAlert()
Clear the FSM alert state.
Definition: MagAOXApp.hpp:2155
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
std::string m_configBase
The name of a base config class for this app (minus .conf).
Definition: MagAOXApp.hpp:92
std::string configName()
Get the config name.
Definition: MagAOXApp.hpp:3241
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.
Definition: MagAOXApp.hpp:2383
std::string m_driverOutName
Full path name of the INDI driver output FIFO.
Definition: MagAOXApp.hpp:582
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
Definition: MagAOXApp.hpp:1950
std::string sysPath
The path to the system directory, for PID file, etc.
Definition: MagAOXApp.hpp:96
int startINDI()
Start INDI Communications.
Definition: MagAOXApp.hpp:2666
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)
Definition: MagAOXApp.hpp:3031
int createStandardIndiText(pcf::IndiProperty &prop, const std::string &propName, const std::string &label="", const std::string &group="")
Create a standard R/W INDI Text property with target and current elements.
Definition: MagAOXApp.hpp:2180
~MagAOXApp() noexcept(true)
Definition: MagAOXApp.hpp:1137
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.
Definition: MagAOXApp.hpp:2569
int registerIndiPropertyNew(pcf::IndiProperty &prop, const std::string &propName, const pcf::IndiProperty::Type &propType, const pcf::IndiProperty::PropertyPermType &propPerm, const pcf::IndiProperty::PropertyStateType &propState, const pcf::IndiProperty::SwitchRuleType &propRule, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for,...
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop, const std::string &propName, const pcf::IndiProperty::Type &propType, const pcf::IndiProperty::PropertyPermType &propPerm, const pcf::IndiProperty::PropertyStateType &propState)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2463
std::string driverInName()
Get the INDI input FIFO file name.
Definition: MagAOXApp.hpp:3253
int indiTargetUpdate(pcf::IndiProperty &localProperty, T &localTarget, const pcf::IndiProperty &remoteProperty, bool setBusy=true)
Get the target element value from an new property.
Definition: MagAOXApp.hpp:2951
int unlockPID()
Remove the PID file.
Definition: MagAOXApp.hpp:1927
std::string MagAOXPath
The base path of the MagAO-X system.
Definition: MagAOXApp.hpp:86
std::string m_driverCtrlName
Full path name of the INDI driver control FIFO.
Definition: MagAOXApp.hpp:587
pcf::IndiProperty m_indiP_state
indi Property to report the application state.
Definition: MagAOXApp.hpp:945
std::unordered_map< std::string, indiCallBack > m_indiSetCallBacks
Map to hold the SetProperty indiCallBacks for this App, with fast lookup by property name.
Definition: MagAOXApp.hpp:572
int setEuidCalled()
Set the effective user ID to the called value, i.e. the highest possible.
Definition: MagAOXApp.hpp:1757
std::unordered_map< std::string, indiCallBack >::iterator callBackIterator
Iterator type of the indiCallBack map.
Definition: MagAOXApp.hpp:558
std::string m_driverInName
Full path name of the INDI driver input FIFO.
Definition: MagAOXApp.hpp:579
std::unordered_map< std::string, indiCallBack > m_indiNewCallBacks
Map to hold the NewProperty indiCallBacks for this App, with fast lookup by property name.
Definition: MagAOXApp.hpp:567
A class to manage raw binary log files.
Definition: logFileRaw.hpp:42
#define MAGAOX_RT_SCHED_POLICY
The real-time scheduling policy.
Definition: config.hpp:21
#define MAGAOX_default_loopPause
The default application loopPause.
Definition: defaults.hpp:49
#define MAGAOX_calibRelPath
The relative path to the calibration files.
Definition: paths.hpp:36
#define MAGAOX_driverFIFORelPath
The relative path to the INDI driver FIFOs.
Definition: paths.hpp:85
#define MAGAOX_configRelPath
The relative path to the configuration files.
Definition: paths.hpp:29
#define MAGAOX_logRelPath
The relative path to the log directory.
Definition: paths.hpp:50
#define MAGAOX_sysRelPath
The relative path to the system directory.
Definition: paths.hpp:64
#define MAGAOX_path
The path to the MagAO-X system files.
Definition: paths.hpp:22
#define MAGAOX_secretsRelPath
The relative path to the secrets directory. Used for storing passwords, etc.
Definition: paths.hpp:71
#define MAGAOX_cpusetPath
The absolute path the the cpuset mount point.
Definition: paths.hpp:99
#define MAGAOX_env_path
Environment variable setting the MagAO-X path.
Definition: environment.hpp:20
#define MAGAOX_env_calib
Environment variable setting the relative calib path.
Definition: environment.hpp:30
#define MAGAOX_env_config
Environment variable setting the relative config path.
Definition: environment.hpp:25
#define MAGAOX_env_cpuset
Environment variable setting the relative calib path.
Definition: environment.hpp:35
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
Definition: indiMacros.hpp:118
#define INDI_SETCALLBACK(prop)
Get the name of the static callback wrapper for a set property.
Definition: indiMacros.hpp:216
int8_t logPrioT
The type of the log priority code.
Definition: logDefs.hpp:21
std::shared_ptr< char > bufferPtrT
The log entry buffer smart pointer.
Definition: logHeader.hpp:58
int th_seteuid(uid_t euid)
Sets the effective user id of the calling thread, rather than the whole process.
Definition: thSetuid.cpp:18
MagAO-X INDI Driver Wrapper.
Macros for INDI.
MagAO-X INDI Utilities.
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_BUSY
Definition: indiUtils.hpp:30
#define INDI_ALERT
Definition: indiUtils.hpp:31
#define INDI_OK
Definition: indiUtils.hpp:29
GeneratorWrapper< T > value(T &&value)
Definition: catch.hpp:4001
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:206
pcf::IndiProperty::Type propType< int >()
Definition: MagAOXApp.cpp:31
std::stringstream msg
const pcf::IndiProperty & ipRecv
pcf::IndiProperty::Type propType< char * >()
Definition: MagAOXApp.cpp:19
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
Definition: MagAOXApp.cpp:42
pcf::IndiProperty::Type propType< double >()
Definition: MagAOXApp.cpp:37
pcf::IndiProperty::Type propType()
Definition: MagAOXApp.hpp:3012
Definition: dm.hpp:24
constexpr static logPrioT LOG_DEBUG
Used for debugging.
Definition: logPriority.hpp:52
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
Definition: logPriority.hpp:49
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
constexpr static logPrioT LOG_DEFAULT
Used to denote "use the default level for this log type".
Definition: logPriority.hpp:61
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
MagAO-X Application States.
Structure to hold the call-back details for handling INDI communications.
Definition: MagAOXApp.hpp:545
int16_t stateCodeT
The type of the state code.
Definition: stateCodes.hpp:31
The type of the input message.
Definition: git_state.hpp:36
The standard MagAOX log manager, used for both process logs and telemetry streams.
Definition: logManager.hpp:74
Software ERR log entry.
A simple text log, a string-type log.
Definition: text_log.hpp:24
A fixed-width timespec structure.
Definition: timespecX.hpp:35
nanosecT time_ns
Nanoseconds.
Definition: timespecX.hpp:37
secT time_s
Time since the Unix epoch.
Definition: timespecX.hpp:36