7#ifndef app_MagAOXApp_hpp
8#define app_MagAOXApp_hpp
12#include <sys/syscall.h>
20#include <unordered_map>
22#include <mx/mxlib.hpp>
23#include <mx/app/application.hpp>
24#include <mx/sys/environment.hpp>
25#include <mx/sys/timeUtils.hpp>
26#include <mx/ioutils/fileUtils.hpp>
28#include "../common/environment.hpp"
29#include "../common/paths.hpp"
30#include "../common/defaults.hpp"
31#include "../common/config.hpp"
33#include "../logger/logFileRaw.hpp"
34#include "../logger/logManager.hpp"
36#include "../sys/thSetuid.hpp"
43using namespace mx::app;
52namespace MagAOXAppTest
55#ifdef XWCTEST_NAMESPACE
62#ifdef XWCTEST_NAMESPACE
74#ifdef XWCTEST_NAMESPACE
152template <
bool _useINDI = true>
156 #ifdef XWCTEST_NAMESPACE
157 friend struct libXWCTest::appTest::MagAOXAppTest::XWCTEST_NAMESPACE::MagAOXApp_test;
219 const bool git_modified
232 virtual
void setDefaults(
int argc,
244 virtual
void setupBasicConfig();
254 virtual
void loadBasicConfig();
260 virtual
void checkConfig();
290 virtual
int execute();
303 virtual
int appStartup() = 0;
314 virtual
int appLogic() = 0;
319 virtual
int appShutdown() = 0;
336 template <typename logT,
int retval = 0>
337 static
int log( const typename logT::messageT &msg,
338 logPrioT level = logPrio::LOG_DEFAULT
349 template <typename logT,
int retval = 0>
350 static
int log(
logPrioT level = logPrio::LOG_DEFAULT
366 static
void configLog( const std::
string &name,
368 const std::
string &value,
369 const std::
string &source
383 int setSigTermHandler();
386 static
void _handlerSigTerm(
int signum,
392 void handlerSigTerm(
int signum,
417 bool m_elevated{
false };
537 template <
class thisPtr,
class Function>
542 pcf::IndiProperty &thProp,
544 const std::string &cpuset,
545 const std::string &thrdName,
561 bool m_stateAlert{
false };
564 bool m_gitAlert{
false };
566 int m_stateLogged{ 0 };
582 bool stateAlert =
false
636 constexpr static bool m_useINDI = _useINDI;
650 pcf::IndiProperty *
property{ 0 };
651 int ( *callBack )(
void *,
const pcf::IndiProperty & ){ 0 };
654 bool m_defReceived{
false };
681 bool m_allDefsReceived{
false };
701 const std::string &propName,
702 const std::string &label =
"",
704 const std::string &group =
""
714 const std::string &propName,
715 const std::string &elName,
716 const std::string &propLabel =
"",
717 const std::string &propGroup =
"",
718 const std::string &elLabel =
""
726 template <
typename T>
728 const std::string &name,
735 const std::string &format,
738 const std::string &label =
"",
740 const std::string &group =
""
749 const std::string &propName,
750 const std::string &propLabel =
"",
752 const std::string &propGroup =
""
762 const std::string &name,
763 const std::string &label =
"",
765 const std::string &group =
""
775 const std::string &name,
776 const std::string &label =
"",
778 const std::string &group =
""
788 const std::string &name,
789 const std::vector<std::string> &elements,
791 const std::vector<std::string> &elementLabels,
793 const std::string &label =
"",
795 const std::string &group =
""
806 const std::string &name,
807 const std::vector<std::string> &elements,
809 const std::string &label =
"",
811 const std::string &group =
""
834 const std::string &propName,
836 const pcf::IndiProperty::Type &propType,
838 const pcf::IndiProperty::PropertyPermType &propPerm,
841 const pcf::IndiProperty::PropertyStateType &propState
854 int ( * )(
void *,
const pcf::IndiProperty & )
866 pcf::IndiProperty &prop,
867 const std::string &propName,
868 const pcf::IndiProperty::Type &propType,
869 const pcf::IndiProperty::PropertyPermType &propPerm,
870 const pcf::IndiProperty::PropertyStateType &propState,
871 int ( * )(
void *,
const pcf::IndiProperty & )
882 pcf::IndiProperty &prop,
883 const std::string &propName,
884 const pcf::IndiProperty::Type &propType,
885 const pcf::IndiProperty::PropertyPermType &propPerm,
886 const pcf::IndiProperty::PropertyStateType &propState,
887 const pcf::IndiProperty::SwitchRuleType &propRule,
888 int ( * )(
void *,
const pcf::IndiProperty & )
899 pcf::IndiProperty &prop,
900 const std::string &devName,
901 const std::string &propName,
902 int ( * )(
void *,
const pcf::IndiProperty & )
963 template <
typename T>
965 const std::string &el,
967 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
982 const std::string &el,
984 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
993 const std::string &el,
994 const pcf::IndiElement::SwitchStateType &newVal,
995 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
1009 template <
typename T>
1011 pcf::IndiProperty &p,
1012 const std::string &el,
1013 const std::vector<T> &newVals,
1014 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok
1026 template <
typename T>
1028 const std::vector<std::string> &els,
1029 const std::vector<T> &newVals,
1030 pcf::IndiProperty::PropertyStateType newState =
1031 pcf::IndiProperty::Ok
1034 template <
typename T>
1036 const std::vector<const char *> &els,
1037 const std::vector<T> &newVals,
1038 pcf::IndiProperty::PropertyStateType newState =
1039 pcf::IndiProperty::Ok
1047 template <
typename T>
1050 const pcf::IndiProperty &remoteProperty,
1060 template <
typename T>
1062 const std::string &el,
1079 const std::string &property,
1096 const pcf::IndiProperty &ipRecv
1123 bool m_powerMgtEnabled{
false };
1130 std::string m_powerElement{
"state" };
1131 std::string m_powerTargetElement{
"target" };
1133 unsigned long m_powerOnWait{ 0 };
1136 int m_powerOnCounter{ -1 };
1140 int m_powerState{ -1 };
1141 int m_powerTargetState{ -1 };
1273template <
bool _useINDI>
1277template <
bool _useINDI>
1280template <
bool _useINDI>
1283 if( m_self !=
nullptr )
1285 throw std::logic_error(
"Attempt to instantiate 2nd MagAOXApp");
1291 getresuid( &m_euidReal, &m_euidCalled, &m_suid );
1294 m_log.parent(
this );
1297 config.m_sources =
true;
1298 config.configLog = configLog;
1310 if( MXLIB_UNCOMP_REPO_MODIFIED )
1316 log<git_state>(
git_state::messageT(
"mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED ), gl );
1319template <
bool _useINDI>
1323 delete m_indiDriver;
1324 m_log.parent(
nullptr );
1330template <
bool _useINDI>
1339 m_basePath = tmpstr;
1352 m_configDir = m_basePath +
"/" + tmpstr;
1353 m_configPathGlobal = m_configDir +
"/magaox.conf";
1361 m_calibDir = m_basePath +
"/" + tmpstr;
1369 m_log.logPath( m_basePath +
"/" + tmpstr );
1377 m_sysPath = m_basePath +
"/" + tmpstr;
1385 m_secretsPath = m_basePath +
"/" + tmpstr;
1391 m_cpusetPath = tmpstr;
1395 if( m_configBase !=
"" )
1398 m_configPathUser = m_configDir +
"/" + m_configBase +
".conf";
1410 "The name of the application and its device name in INDI (if used), specifies the config file in the XWC config directory." );
1412 config.parseCommandLine( argc, argv,
"name" );
1413 config( m_configName,
"name" );
1415 if( m_configName ==
"" )
1417 m_configName = mx::ioutils::pathStem( invokedName );
1420 log<text_log>(
"Configuration Error: Application name (-n --name) not set." );
1426 m_configPathLocal = m_configDir +
"/" + m_configName +
".conf";
1429 if( registerIndiPropertyNew(
1430 m_indiP_state,
"fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0 ) <
1433 log<software_error>( { __FILE__, __LINE__,
"failed to register read only fsm_state property" } );
1436 m_indiP_state.add( pcf::IndiElement(
"state" ) );
1438 createStandardIndiRequestSw( m_indiP_clearFSMAlert,
"fsm_clear_alert",
"Clear FSM Alert",
"FSM" );
1439 if( registerIndiPropertyNew( m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert ) < 0 )
1441 log<software_error>( { __FILE__, __LINE__,
"failed to register new fsm_alert property" } );
1447template <
bool _useINDI>
1451 config.add(
"config.validate",
1459 "Validate the configuration. App will exit after loading the configuration, but before entering the event loop. Errors from configuratin processing will be shown. Always safe to run." );
1462 config.add(
"loopPause",
1470 "The main loop pause time in ns" );
1473 "ignore_git",
"",
"ignore-git", argType::True,
"",
"",
false,
"bool",
"set to true to ignore git "
1474 "status to prevent the fsm_alert" );
1477 m_log.setupConfig( config );
1479 if( m_powerMgtEnabled )
1481 if( _useINDI ==
false )
1484 log<software_critical>( { __FILE__, __LINE__,
"power management is enabled but we are not using INDI" } );
1489 config.add(
"power.device",
1497 "Device controlling power for this app's device (INDI name)." );
1499 config.add(
"power.channel",
1507 "Channel on device for this app's device (INDI name)." );
1509 config.add(
"power.element",
1517 "INDI power state element name. Default is \"state\", only need to specify if different." );
1519 config.add(
"power.targetElement",
1521 "power.targetElement",
1527 "INDI power target element name. Default is \"target\", only need to specify if different." );
1529 config.add(
"power.powerOnWait",
1531 "power.powerOnWait",
1537 "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec." );
1542template <
bool _useINDI>
1547 config( ig,
"ignore_git" );
1549 if( !ig && m_gitAlert )
1551 m_stateAlert =
true;
1555 if(config.isSet(
"config.validate"))
1557 m_configOnly =
true;
1561 m_log.logName( m_configName );
1562 m_log.loadConfig( config );
1565 config( m_loopPause,
"loopPause" );
1568 if( m_powerMgtEnabled )
1570 config( m_powerDevice,
"power.device" );
1571 config( m_powerChannel,
"power.channel" );
1572 config( m_powerElement,
"power.element" );
1573 config( m_powerTargetElement,
"power.targetElement" );
1575 if( m_powerDevice !=
"" && m_powerChannel !=
"" )
1577 log<text_log>(
"enabling power management: " + m_powerDevice +
"." + m_powerChannel +
"." + m_powerElement +
1578 "/" + m_powerTargetElement );
1579 if( registerIndiPropertySet(
1580 m_indiP_powerChannel, m_powerDevice, m_powerChannel,
INDI_SETCALLBACK( m_indiP_powerChannel ) ) <
1583 log<software_error>( { __FILE__, __LINE__,
"failed to register set property" } );
1592 config( m_powerOnWait,
"power.powerOnWait" );
1593 if( m_powerOnWait > 3600 )
1595 log<text_log>(
"powerOnWait longer than 1 hour. Setting to 0.",
logPrio::LOG_ERROR );
1601template <
bool _useINDI>
1606 for(
auto it = config.m_targets.begin();
it != config.m_targets.end(); ++
it )
1608 if(
it->second.used ==
false )
1610 std::string
msg =
it->second.name;
1611 if( config.m_sources &&
it->second.sources.size() > 0 )
1613 msg +=
" [" +
it->second.sources[0] +
"]";
1621 if( config.m_unusedConfigs.size() > 0 )
1623 for(
auto it = config.m_unusedConfigs.begin();
it != config.m_unusedConfigs.end(); ++
it )
1625 if(
it->second.used ==
true )
1630 std::string
msg =
it->second.name;
1631 if( config.m_sources &&
it->second.sources.size() > 0 )
1633 msg +=
" [" +
it->second.sources[0] +
"]";
1642 if( config.nonOptions.size() > 0 )
1644 for(
size_t n = 0; n < config.nonOptions.size(); ++n )
1646 log<text_log>(
"Unrecognized command line argument: " + config.nonOptions[n],
logPrio::LOG_CRITICAL );
1653 if(m_shutdown ==
true)
1655 std::cerr <<
"\nThere were configuration errors.\n\n";
1659 std::cerr <<
"\nConfiguration is valid.\n\n";
1662 else if(m_shutdown ==
true)
1668template <
bool _useINDI>
1675 #ifndef XWC_DISABLE_USER_CHECK
1677 struct stat logstat;
1679 if( stat( m_log.logPath().c_str(), &logstat ) < 0 )
1681 state( stateCodes::FAILURE );
1682 std::cerr <<
"\nCRITICAL: Can not stat the log path.\n\n";
1687 #ifdef XWCTEST_MAGAOXAPP_EXEC_WRONG_USER
1688 logstat.st_uid = geteuid()+1;
1691 if( logstat.st_uid != geteuid() )
1693 state( stateCodes::FAILURE );
1694 std::cerr <<
"\nCRITICAL: You are running this app as the wrong user.\n\n";
1701 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1702 int testTimesThrough = 0;
1710 state( stateCodes::FAILURE );
1713 std::cerr <<
"\nCRITICAL: Failed to lock PID. Exiting.\n\n";
1722 m_log.logThreadStart();
1725 #ifdef XWCTEST_MAGAOXAPP_EXEC_LOG_START
1726 m_log.logShutdown(
true);
1731 while( m_log.logThreadRunning() ==
false && w < 20 )
1734 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( 100000000 ) );
1738 if( m_log.logThreadRunning() ==
false )
1740 state( stateCodes::FAILURE );
1743 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1747 if( unlockPID() < 0 )
1749 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1758 if( m_shutdown == 0 )
1760 if( setSigTermHandler() < 0 )
1762 state( stateCodes::FAILURE );
1764 log<software_critical>( { __FILE__, __LINE__,
"error from setSigTermHandler()" } );
1768 if( unlockPID() < 0 )
1770 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1780 if( m_shutdown == 0 )
1782 state( stateCodes::INITIALIZED );
1784 if( appStartup() < 0 )
1786 state( stateCodes::FAILURE );
1788 log<software_critical>( { __FILE__, __LINE__,
"error from appStartup()" } );
1792 if( unlockPID() < 0 )
1794 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1802 if( m_useINDI && m_shutdown == 0 )
1804 if( startINDI() < 0 )
1806 state( stateCodes::FAILURE );
1808 log<software_critical>( { __FILE__, __LINE__,
"INDI failed to start." } );
1813 if( appShutdown() < 0 )
1815 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1818 if( unlockPID() < 0 )
1820 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1830 if( m_powerMgtEnabled && m_shutdown == 0 )
1833 while( m_powerState < 0 && !m_shutdown )
1836 if( m_powerState < 0 )
1838 if( !stateLogged() )
1840 log<text_log>(
"waiting for power state" );
1848 state( stateCodes::ERROR );
1853 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1858 if( m_powerState > 0 )
1860 state( stateCodes::POWERON );
1864 m_powerOnCounter = 0;
1865 state( stateCodes::POWEROFF );
1866 if( onPowerOff() < 0 )
1868 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1884 while( m_shutdown == 0 )
1887 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1888 if(testTimesThrough > 1)
1895 if( m_log.logThreadRunning() ==
false )
1897 state( stateCodes::FAILURE );
1900 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1908 if( m_powerMgtEnabled )
1910 if( state() == stateCodes::POWEROFF )
1912 if( m_powerState == 1 )
1914 m_powerOnCounter = 0;
1915 state( stateCodes::POWERON );
1920 if( m_powerState == 0 )
1922 state( stateCodes::POWEROFF );
1923 if( onPowerOff() < 0 )
1925 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1936 if( !m_powerMgtEnabled || m_powerState > 0 )
1938 if( appLogic() < 0 )
1940 log<software_error>( { __FILE__, __LINE__,
"error from appLogic()" } );
1946 else if( m_powerState == 0 )
1948 if( whilePowerOff() < 0 )
1950 log<software_error>( { __FILE__, __LINE__,
"error from whilePowerOff()" } );
1957 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1971 sendGetPropertySetList(
false );
1979 if( m_shutdown == 0 )
1981 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( m_loopPause ) );
1985 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1990 if( appShutdown() < 0 )
1993 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1996 state( stateCodes::SHUTDOWN );
1999 if( m_indiDriver !=
nullptr )
2001 pcf::IndiProperty ipSend;
2002 ipSend.setDevice( m_configName );
2005 m_indiDriver->sendDelProperty( ipSend );
2007 catch(
const std::exception &e )
2009 log<software_error>( { __FILE__, __LINE__, std::string(
"exception caught from"
2010 " sendDelProperty: " ) + e.what() } );
2013 m_indiDriver->quitProcess();
2014 m_indiDriver->deactivate();
2015 log<indidriver_stop>();
2018 if( unlockPID() < 0 )
2021 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
2028template <
bool _useINDI>
2029template <
typename logT,
int retval>
2032 m_log.template log<logT>(
msg, level );
2036template <
bool _useINDI>
2037template <
typename logT,
int retval>
2040 m_log.template log<logT>( level );
2044template <
bool _useINDI>
2055 state( m_state,
true );
2058 if( _useINDI && m_indiDriver )
2060 pcf::IndiProperty
msg;
2061 msg.setDevice( m_configName );
2063 std::stringstream logstdf;
2066 msg.setMessage( logstdf.str() );
2072 tv.tv_usec = (
long int)( ( (
double)ts.
time_ns ) / 1e3 );
2074 msg.setTimeStamp( pcf::TimeStamp( tv ) );
2078 m_indiDriver->sendMessage(
msg );
2080 catch(
const std::exception &e )
2082 log<software_error>(
2083 { __FILE__, __LINE__, std::string(
"exception caught from sendMessage: " ) + e.what() } );
2088template <
bool _useINDI>
2091 const std::string &value,
2092 const std::string &source )
2094 m_log.template log<config_log>( { name, code, value, source } );
2097template <
bool _useINDI>
2101 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_ERR
2105 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGTERM
2107 #define SIGTERM SIGKILL
2110 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGQUIT
2112 #define SIGQUIT SIGKILL
2115 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGINT
2117 #define SIGINT SIGKILL
2122 struct sigaction act;
2126 act.sa_flags = SA_SIGINFO;
2127 sigemptyset( &set );
2131 if( sigaction( SIGTERM, &act, 0 ) < 0 )
2133 std::string logss =
"Setting handler for SIGTERM failed. Errno says: ";
2134 logss += strerror( errno );
2136 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2142 if( sigaction( SIGQUIT, &act, 0 ) < 0 )
2144 std::string logss =
"Setting handler for SIGQUIT failed. Errno says: ";
2145 logss += strerror( errno );
2147 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2153 if( sigaction( SIGINT, &act, 0 ) < 0 )
2155 std::string logss =
"Setting handler for SIGINT failed. Errno says: ";
2156 logss += strerror( errno );
2158 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2163 log<text_log>(
"Installed SIGTERM/SIGQUIT/SIGINT signal handler.",
logPrio::LOG_DEBUG );
2168template <
bool _useINDI>
2174template <
bool _useINDI>
2176 siginfo_t *siginf __attribute__( ( unused ) ),
2177 void *ucont __attribute__( ( unused ) ) )
2181 std::string signame;
2185 signame =
"SIGTERM";
2191 signame =
"SIGQUIT";
2197 std::string logss =
"Caught signal ";
2199 logss +=
". Shutting down.";
2201 std::cerr <<
"\n" << logss << std::endl;
2202 log<text_log>( logss );
2206void sigUsr1Handler(
int signum, siginfo_t *siginf,
void *ucont );
2208template <
bool _useINDI>
2212 if( sys::th_seteuid( m_euidCalled ) < 0 )
2214 log<software_error>( { __FILE__, __LINE__, errno, 0, std::format(
"Setting effective user id to "
2215 "euidCalled ({}) failed. "
2218 strerror( errno )) } );
2226template <
bool _useINDI>
2230 if( sys::th_seteuid( m_euidReal ) < 0 )
2232 log<software_error>( { __FILE__, __LINE__, errno, 0, std::format(
"Setting effective user id to "
2233 "euidReal ({}) failed. "
2236 strerror( errno )) } );
2244template <
bool _useINDI>
2249 std::string statusDir = m_sysPath;
2257 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2259 if( errno != EEXIST )
2261 return log<
software_critical, -1>( { __FILE__, __LINE__, errno, 0,
"Failed to create root of statusDir (" + statusDir +
"). "
2262 "Errno says: " + strerror( errno ) } );
2267 statusDir += m_configName;
2269 pidFileName = statusDir +
"/pid";
2274 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2276 if( errno != EEXIST )
2279 "Failed to create statusDir (" + statusDir +
"). "
2280 "Errno says: " + strerror( errno )} );
2285 std::ifstream pidIn;
2286 pidIn.open( pidFileName );
2296 std::stringstream procN;
2297 procN <<
"/proc/" << testPid <<
"/cmdline";
2299 std::ifstream procIn;
2300 std::string pidCmdLine;
2304 procIn.open( procN.str() );
2307 procIn >> pidCmdLine;
2313 log<
software_critical, -1>( { __FILE__, __LINE__, 0, 0,
"exception caught testing /proc/pid" } );
2320 size_t invokedPos = pidCmdLine.find( invokedName );
2323 size_t configPos = std::string::npos;
2324 if( invokedPos != std::string::npos )
2326 configPos = pidCmdLine.find( m_configName );
2330 #ifdef XWCTEST_MAGAOXAPP_PID_LOCKED
2337 if( invokedPos != std::string::npos && configPos != std::string::npos )
2340 std::cerr <<
"PID already locked (" + std::to_string(testPid) +
"). Time to die." << std::endl;
2342 return log<
text_log, -1>(
"PID already locked (" + std::to_string(testPid) +
"). Time to die." );
2353 std::ofstream pidOut;
2354 pidOut.open( pidFileName );
2357 #ifdef XWCTEST_MAGAOXAPP_PID_WRITE_FAIL
2362 if( !(pidOut << m_pid) )
2364 return log<
software_critical, -1>( { __FILE__, __LINE__, errno, 0,
"failed to write to pid file." } );
2369 return log<text_log, 0>(
"PID (" + std::to_string(m_pid) +
") locked." );
2372template <
bool _useINDI>
2376 #ifdef XWCTEST_MAGAOXAPP_PID_UNLOCK_ERR
2385 if( ::remove( pidFileName.c_str() ) < 0 )
2387 log<software_error>(
2388 { __FILE__, __LINE__, errno, 0, std::string(
"Failed to remove PID file: " ) + strerror( errno ) } );
2393 return log<text_log, 0>(
"PID (" + std::to_string(m_pid) +
") unlocked." );
2397template <
bool _useINDI>
2398template <
class thisPtr,
class Function>
2402 pcf::IndiProperty &thProp,
2404 const std::string &cpuset,
2405 const std::string &thrdName,
2407 Function &&thrdStart )
2415 thrd = std::thread( thrdStart, thrdThis );
2417 catch(
const std::exception &e )
2419 log<software_error>(
2420 { __FILE__, __LINE__, std::string(
"Exception on " + thrdName +
" thread start: " ) + e.what() } );
2425 log<software_error>( { __FILE__, __LINE__,
"Unkown exception on " + thrdName +
" thread start" } );
2429 if( !thrd.joinable() )
2431 log<software_error>( { __FILE__, __LINE__, thrdName +
" thread did not start" } );
2443 sp.sched_priority = thrdPrio;
2457 rv = pthread_setschedparam( thrd.native_handle(), SCHED_OTHER, &sp );
2462 log<software_error>(
2466 "Setting " + thrdName +
" thread scheduler priority to " + std::to_string( thrdPrio ) +
" failed." } );
2470 log<text_log>( thrdName +
" thread scheduler priority set to " + std::to_string( thrdPrio ) );
2476 for(
int i = 0; i < 10; ++i )
2478 mx::sys::milliSleep( 100 );
2486 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"tpid for " + thrdName +
" not set." } );
2490 log<text_log>( thrdName +
" thread pid is " + std::to_string( tpid ) );
2494 thProp = pcf::IndiProperty( pcf::IndiProperty::Number );
2495 thProp.setDevice( configName() );
2496 thProp.setName( std::string(
"th-" ) + thrdName );
2497 thProp.setPerm( pcf::IndiProperty::ReadOnly );
2498 thProp.setState( pcf::IndiProperty::Idle );
2499 thProp.add( pcf::IndiElement(
"pid" ) );
2500 thProp[
"pid"] = tpid;
2501 thProp.add( pcf::IndiElement(
"prio" ) );
2502 thProp[
"prio"] = thrdPrio;
2503 registerIndiPropertyReadOnly( thProp );
2509 std::string cpuFile = m_cpusetPath;
2510 cpuFile +=
"/" + cpuset;
2511 cpuFile +=
"/tasks";
2512 int wfd = open( cpuFile.c_str(), O_WRONLY );
2515 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error from open for " + cpuFile } );
2519 snprintf( pids,
sizeof( pids ),
"%d", tpid );
2521 int w = write( wfd, pids, strnlen( pids,
sizeof( pids ) ) );
2522 if( w != (
int)strnlen( pids,
sizeof(pids) ) )
2524 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error on write" } );
2538template <
bool _useINDI>
2544template <
bool _useINDI>
2551 if( s == stateCodes::ERROR )
2553 if( s == stateCodes::FAILURE )
2556 log<state_change>( { m_state, s }, lvl );
2562 if( m_stateAlert != stateAlert && stateAlert ==
true )
2564 m_stateAlert = stateAlert;
2569 std::unique_lock<std::mutex>
lock( m_indiMutex,
2573 if(
lock.owns_lock() )
2576 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2579 if( m_stateAlert ==
true )
2585 if( m_state == stateCodes::READY )
2587 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING ||
2588 m_state == stateCodes::CONFIGURING )
2590 else if( m_state < stateCodes::NODEVICE )
2592 else if( m_state <= stateCodes::LOGGEDIN )
2594 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2598 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2602template <
bool _useINDI>
2605 return m_stateAlert;
2608template <
bool _useINDI>
2614template <
bool _useINDI>
2617 if( m_stateLogged > 0 )
2620 return m_stateLogged - 1;
2629template <
bool _useINDI>
2632 if( m_stateAlert ==
false )
2637 m_stateAlert =
false;
2641 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2643 if( m_state == stateCodes::READY )
2647 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING || m_state == stateCodes::CONFIGURING )
2651 else if( m_state < stateCodes::NODEVICE )
2655 else if( m_state <= stateCodes::LOGGEDIN )
2659 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2664 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2673template <
bool _useINDI>
2675 const std::string &propName,
2676 const std::string &label,
2677 const std::string &group )
2679 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2680 prop.setDevice( configName() );
2681 prop.setName( propName );
2682 prop.setPerm( pcf::IndiProperty::ReadWrite );
2683 prop.setState( pcf::IndiProperty::Idle );
2684 prop.add( pcf::IndiElement(
"current" ) );
2685 prop.add( pcf::IndiElement(
"target" ) );
2690 prop.setLabel( label );
2695 prop.setGroup( group );
2701template <
bool _useINDI>
2703 const std::string &propName,
2704 const std::string &elName,
2705 const std::string &propLabel,
2706 const std::string &propGroup,
2707 const std::string &elLabel )
2709 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2710 prop.setDevice( configName() );
2711 prop.setName( propName );
2712 prop.setPerm( pcf::IndiProperty::ReadOnly );
2713 prop.setState( pcf::IndiProperty::Idle );
2716 if( propLabel !=
"" )
2718 prop.setLabel( propLabel );
2721 if( propGroup !=
"" )
2723 prop.setGroup( propGroup );
2726 prop.add( pcf::IndiElement( elName ) );
2730 prop[elName].setLabel( elLabel );
2736template <
bool _useINDI>
2737template <
typename T>
2739 const std::string &name,
2743 const std::string &format,
2744 const std::string &label,
2745 const std::string &group )
2747 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2748 prop.setDevice( configName() );
2749 prop.setName( name );
2750 prop.setPerm( pcf::IndiProperty::ReadWrite );
2751 prop.setState( pcf::IndiProperty::Idle );
2752 prop.add( pcf::IndiElement(
"current" ) );
2753 prop[
"current"].setMin( min );
2754 prop[
"current"].setMax( max );
2755 prop[
"current"].setStep( step );
2758 prop[
"current"].setFormat( format );
2761 prop.add( pcf::IndiElement(
"target" ) );
2762 prop[
"target"].setMin( min );
2763 prop[
"target"].setMax( max );
2764 prop[
"target"].setStep( step );
2767 prop[
"target"].setFormat( format );
2773 prop.setLabel( label );
2778 prop.setGroup( group );
2784template <
bool _useINDI>
2786 const std::string &propName,
2787 const std::string &propLabel,
2788 const std::string &propGroup )
2790 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2791 prop.setDevice( configName() );
2792 prop.setName( propName );
2793 prop.setPerm( pcf::IndiProperty::ReadOnly );
2794 prop.setState( pcf::IndiProperty::Idle );
2797 if( propLabel !=
"" )
2799 prop.setLabel( propLabel );
2802 if( propGroup !=
"" )
2804 prop.setGroup( propGroup );
2810template <
bool _useINDI>
2812 const std::string &name,
2813 const std::string &label,
2814 const std::string &group )
2816 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2817 prop.setDevice( configName() );
2818 prop.setName( name );
2819 prop.setPerm( pcf::IndiProperty::ReadWrite );
2820 prop.setState( pcf::IndiProperty::Idle );
2821 prop.setRule( pcf::IndiProperty::AtMostOne );
2824 prop.add( pcf::IndiElement(
"toggle", pcf::IndiElement::Off ) );
2829 prop.setLabel( label );
2834 prop.setGroup( group );
2840template <
bool _useINDI>
2842 const std::string &name,
2843 const std::string &label,
2844 const std::string &group )
2846 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2847 prop.setDevice( configName() );
2848 prop.setName( name );
2849 prop.setPerm( pcf::IndiProperty::ReadWrite );
2850 prop.setState( pcf::IndiProperty::Idle );
2851 prop.setRule( pcf::IndiProperty::AtMostOne );
2854 prop.add( pcf::IndiElement(
"request", pcf::IndiElement::Off ) );
2859 prop.setLabel( label );
2864 prop.setGroup( group );
2870template <
bool _useINDI>
2872 const std::string &name,
2873 const std::vector<std::string> &elements,
2874 const std::vector<std::string> &elementLabels,
2875 const std::string &label,
2876 const std::string &group )
2878 if( elements.size() == 0 )
2880 return log<
software_error, -1>( { __FILE__, __LINE__,
"elements vector has zero size" } );
2883 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2884 prop.setDevice( configName() );
2885 prop.setName( name );
2886 prop.setPerm( pcf::IndiProperty::ReadWrite );
2887 prop.setState( pcf::IndiProperty::Idle );
2888 prop.setRule( pcf::IndiProperty::OneOfMany );
2891 for(
size_t n = 0; n < elements.size(); ++n )
2893 pcf::IndiElement elem = pcf::IndiElement( elements[n], pcf::IndiElement::Off );
2894 if(elementLabels[n] !=
"")
2896 elem.setLabel( elementLabels[n] );
2904 prop.setLabel( label );
2909 prop.setGroup( group );
2915template <
bool _useINDI>
2917 const std::string &name,
2918 const std::vector<std::string> &elements,
2919 const std::string &label,
2920 const std::string &group )
2922 return createStandardIndiSelectionSw( prop, name, elements, elements, label, group );
2925template <
bool _useINDI>
2932 m_indiNewCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, nullptr } ) );
2936 if( !result.second )
2939 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2942 catch( std::exception &e )
2944 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2948 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2954template <
bool _useINDI>
2956 const std::string &propName,
2957 const pcf::IndiProperty::Type &propType,
2958 const pcf::IndiProperty::PropertyPermType &propPerm,
2959 const pcf::IndiProperty::PropertyStateType &propState )
2964 prop = pcf::IndiProperty( propType );
2965 prop.setDevice( m_configName );
2966 prop.setName( propName );
2967 prop.setPerm( propPerm );
2968 prop.setState( propState );
2974 if( !result.second )
2977 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2980 catch( std::exception &e )
2982 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2986 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2991template <
bool _useINDI>
2993 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3000 callBackInsertResult result =
3001 m_indiNewCallBacks.insert( callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
3003 if( !result.second )
3006 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3009 catch( std::exception &e )
3011 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3015 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3021template <
bool _useINDI>
3022int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
3023 const std::string &propName,
3024 const pcf::IndiProperty::Type &propType,
3025 const pcf::IndiProperty::PropertyPermType &propPerm,
3026 const pcf::IndiProperty::PropertyStateType &propState,
3027 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3032 prop = pcf::IndiProperty( propType );
3033 prop.setDevice( m_configName );
3034 prop.setName( propName );
3035 prop.setPerm( propPerm );
3036 prop.setState( propState );
3038 return registerIndiPropertyNew( prop, callBack );
3041template <
bool _useINDI>
3042int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
3043 const std::string &propName,
3044 const pcf::IndiProperty::Type &propType,
3045 const pcf::IndiProperty::PropertyPermType &propPerm,
3046 const pcf::IndiProperty::PropertyStateType &propState,
3047 const pcf::IndiProperty::SwitchRuleType &propRule,
3048 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3053 prop = pcf::IndiProperty( propType );
3054 prop.setDevice( m_configName );
3055 prop.setName( propName );
3056 prop.setPerm( propPerm );
3057 prop.setState( propState );
3058 prop.setRule( propRule );
3059 return registerIndiPropertyNew( prop, callBack );
3062template <
bool _useINDI>
3064 const std::string &devName,
3065 const std::string &propName,
3066 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3071 prop = pcf::IndiProperty();
3072 prop.setDevice( devName );
3073 prop.setName( propName );
3076 m_indiSetCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
3080 if( !result.second )
3083 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3086 catch( std::exception &e )
3088 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3092 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3098template <
bool _useINDI>
3107 std::string driverFIFOPath = m_basePath;
3108 driverFIFOPath +=
"/";
3111 m_driverInName = driverFIFOPath +
"/" + configName() +
".in";
3112 m_driverOutName = driverFIFOPath +
"/" + configName() +
".out";
3113 m_driverCtrlName = driverFIFOPath +
"/" + configName() +
".ctrl";
3119 mode_t prev = umask( 0 );
3122 if( mkfifo( m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3124 if( errno != EEXIST )
3127 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3134 if( mkfifo( m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3136 if( errno != EEXIST )
3140 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3147 if( mkfifo( m_driverCtrlName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3149 if( errno != EEXIST )
3153 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3164template <
bool _useINDI>
3171 if( createINDIFIFOS() < 0 )
3179 if( m_indiDriver !=
nullptr )
3181 m_indiDriver->quitProcess();
3182 m_indiDriver->deactivate();
3183 log<indidriver_stop>();
3184 delete m_indiDriver;
3185 m_indiDriver =
nullptr;
3192 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction exception." } );
3197 if( m_indiDriver ==
nullptr )
3199 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction failed." } );
3204 if( m_indiDriver->good() ==
false )
3206 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver failed to open FIFOs." } );
3207 delete m_indiDriver;
3208 m_indiDriver =
nullptr;
3213 m_indiDriver->activate();
3214 log<indidriver_start>();
3216 sendGetPropertySetList(
true );
3221template <
bool _useINDI>
3225 if( !all && m_allDefsReceived )
3231 while(
it != m_indiSetCallBacks.end() )
3233 if( all ||
it->second.m_defReceived ==
false )
3235 if(
it->second.property )
3239 m_indiDriver->sendGetProperties( *(
it->second.property ) );
3241 catch(
const std::exception &e )
3243 log<software_error>( { __FILE__,
3245 "exception caught from sendGetProperties for " +
3246 it->second.property->getName() +
": " + e.what() } );
3250 it->second.m_defReceived =
false;
3256 m_allDefsReceived =
false;
3258 m_allDefsReceived =
true;
3261template <
bool _useINDI>
3264 handleSetProperty(
ipRecv );
3267template <
bool _useINDI>
3272 if( m_indiDriver ==
nullptr )
3276 if(
ipRecv.hasValidDevice() &&
ipRecv.getDevice() != m_indiDriver->getName() )
3282 if( !
ipRecv.hasValidName() )
3286 while(
it != m_indiNewCallBacks.end() )
3288 if(
it->second.property )
3292 m_indiDriver->sendDefProperty( *(
it->second.property ) );
3294 catch(
const std::exception &e )
3296 log<software_error>( { __FILE__,
3298 "exception caught from sendDefProperty for " +
3299 it->second.property->getName() +
": " + e.what() } );
3306 sendGetPropertySetList(
true );
3312 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3318 if( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property )
3322 m_indiDriver->sendDefProperty( *( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property ) );
3324 catch(
const std::exception &e )
3326 log<software_error>( { __FILE__,
3328 "exception caught from sendDefProperty for " +
3329 m_indiNewCallBacks[
ipRecv.createUniqueKey()].property->getName() +
": " +
3336template <
bool _useINDI>
3341 if( m_indiDriver ==
nullptr )
3345 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3347 log<software_debug>( { __FILE__, __LINE__,
"invalid NewProperty request for " +
ipRecv.createUniqueKey() } );
3351 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiNewCallBacks[
ipRecv.createUniqueKey()].callBack;
3354 callBack(
this,
ipRecv );
3356 log<software_debug>( { __FILE__, __LINE__,
"NewProperty callback null for " +
ipRecv.createUniqueKey() } );
3361template <
bool _useINDI>
3366 if( m_indiDriver ==
nullptr )
3369 std::string key =
ipRecv.createUniqueKey();
3372 if( m_indiSetCallBacks.count( key ) > 0 )
3374 m_indiSetCallBacks[key].m_defReceived =
true;
3377 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiSetCallBacks[key].callBack;
3379 callBack(
this,
ipRecv );
3391template <
bool _useINDI>
3392template <
typename T>
3394 const std::string &el,
3396 pcf::IndiProperty::PropertyStateType ipState )
3404 indi::updateIfChanged( p, el, newVal, m_indiDriver, ipState );
3407template <
bool _useINDI>
3409 const std::string &el,
3411 pcf::IndiProperty::PropertyStateType ipState )
3413 updateIfChanged<std::string>( p, el, std::string( newVal ), ipState );
3416template <
bool _useINDI>
3418 const std::string &el,
3419 const pcf::IndiElement::SwitchStateType &newVal,
3420 pcf::IndiProperty::PropertyStateType ipState )
3428 indi::updateSwitchIfChanged( p, el, newVal, m_indiDriver, ipState );
3431template <
bool _useINDI>
3432template <
typename T>
3434 const std::string &el,
3435 const std::vector<T> &newVals,
3436 pcf::IndiProperty::PropertyStateType ipState )
3444 std::vector<std::string> descriptors( newVals.size(), el );
3445 for(
size_t index = 0; index < newVals.size(); ++index )
3447 descriptors[index] += std::to_string( index );
3449 indi::updateIfChanged( p, descriptors, newVals, m_indiDriver );
3452template <
bool _useINDI>
3453template <
typename T>
3455 const std::vector<std::string> &els,
3456 const std::vector<T> &newVals,
3457 pcf::IndiProperty::PropertyStateType newState )
3465 indi::updateIfChanged( p, els, newVals, m_indiDriver, newState );
3468template <
bool _useINDI>
3469template <
typename T>
3471 const std::vector<const char *> &els,
3472 const std::vector<T> &newVals,
3473 pcf::IndiProperty::PropertyStateType newState )
3485 indi::updatesIfChanged( p, els, newVals, m_indiDriver, newState );
3488template <
bool _useINDI>
3489template <
typename T>
3492 const pcf::IndiProperty &remoteProperty,
3495 if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey() )
3500 if( !( remoteProperty.find(
"target" ) || remoteProperty.find(
"current" ) ) )
3507 if( remoteProperty.find(
"target" ) )
3509 localTarget = remoteProperty[
"target"].get<T>();
3515 if( remoteProperty.find(
"current" ) )
3517 localTarget = remoteProperty[
"current"].get<T>();
3529 updateIfChanged( localProperty,
"target", localTarget,
INDI_BUSY );
3533 updateIfChanged( localProperty,
"target", localTarget );
3539template <
bool _useINDI>
3540template <
typename T>
3550 log<software_error>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3553 pcf::IndiProperty ipToSend = ipSend;
3557 ipToSend[el].setValue( newVal );
3561 log<software_error>(
3562 { __FILE__, __LINE__,
"Exception caught setting " + ipSend.createUniqueKey() +
"." + el } );
3569 log<software_error>( { __FILE__, __LINE__ } );
3576template <
bool _useINDI>
3586 return log<
software_error, -1>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3589 if( m_indiDriver->sendNewProperty( ipSend ) < 0 )
3597template <
bool _useINDI>
3603 pcf::IndiProperty ipSend( pcf::IndiProperty::Switch );
3607 ipSend.setDevice( device );
3608 ipSend.setName( property );
3609 ipSend.add( pcf::IndiElement(
"toggle" ) );
3611 catch( std::exception &e )
3613 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"exception: " ) + e.what() } );
3616 if( onoff ==
false )
3618 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::Off );
3622 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::On );
3625 if( sendNewProperty( ipSend ) < 0 )
3628 { __FILE__, __LINE__,
"sendNewProperty failed for " + device +
"." +
property } );
3634template <
bool _useINDI>
3640template <
bool _useINDI>
3644 if(
ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey() )
3646 return log<
software_error, -1>( { __FILE__, __LINE__,
"wrong indi property received" } );
3649 if(
ipRecv.find(
"request" ) )
3651 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
3654 updateSwitchIfChanged( m_indiP_clearFSMAlert,
"request", pcf::IndiElement::Off,
INDI_IDLE );
3661template <
bool _useINDI>
3667template <
bool _useINDI>
3673template <
bool _useINDI>
3676 if( !m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0 )
3681 if( m_powerOnCounter * m_loopPause > ( (
double)m_powerOnWait ) * 1e9 )
3692template <
bool _useINDI>
3695 if( !m_powerMgtEnabled )
3700 return m_powerState;
3703template <
bool _useINDI>
3706 if( !m_powerMgtEnabled )
3711 return m_powerTargetState;
3714template <
bool _useINDI>
3722 ps =
ipRecv[m_powerElement].get<std::string>();
3728 else if( ps ==
"Off" )
3740 ps =
ipRecv[m_powerTargetElement].get<std::string>();
3744 m_powerTargetState = 1;
3746 else if( ps ==
"Off" )
3748 m_powerTargetState = 0;
3752 m_powerTargetState = -1;
3759template <
bool _useINDI>
3765template <
bool _useINDI>
3768 return m_configName;
3771template <
bool _useINDI>
3777template <
bool _useINDI>
3780 return m_configBase;
3783template <
bool _useINDI>
3789template <
bool _useINDI>
3795template <
bool _useINDI>
3798 return m_secretsPath;
3801template <
bool _useINDI>
3804 return m_cpusetPath;
3807template <
bool _useINDI>
3813template <
bool _useINDI>
3819template <
bool _useINDI>
3822 return m_driverInName;
3825template <
bool _useINDI>
3828 return m_driverOutName;
3831template <
bool _useINDI>
3834 return m_driverCtrlName;
3837#ifdef XWCTEST_NAMESPACE
3842extern template class MagAOXApp<true>;
3843extern template class MagAOXApp<false>;
3863#define XWCAPP_THREAD_START( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart ) \
3864 if( threadStart( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, this, thrdStart ) < 0 ) \
3866 log<software_error>( { __FILE__, __LINE__, "error from threadStart for " #thrdName } ); \
3877#define XWCAPP_THREAD_CHECK( thrdSt, thrdName ) \
3880 if( pthread_tryjoin_np( thrdSt.native_handle(), 0 ) == 0 ) \
3882 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3888 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3897#define XWCAPP_THREAD_STOP( thrdSt ) \
3898 if( thrdSt.joinable() ) \
3900 pthread_kill( thrdSt.native_handle(), SIGUSR1 ); \
Internal class to manage setuid privilege escalation with RAII.
elevatedPrivileges(MagAOXApp *app)
The base-class for XWCTk applications.
void handleDefProperty(const pcf::IndiProperty &ipRecv)
Handler for the DEF INDI properties notification.
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.
bool gitAlert()
Get the value of the git alert flag.
std::string basePath()
Get the.
void handleSetProperty(const pcf::IndiProperty &ipRecv)
Handler for the set INDI property request.
int sendNewStandardIndiToggle(const std::string &device, const std::string &property, bool onoff)
Send a new property commmand for a standard toggle switch.
std::string m_configName
The name of the configuration file (minus .conf).
virtual int onPowerOff()
This method is called when the change to poweroff is detected.
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.
stateCodes::stateCodeT state()
Get the current state code.
std::string configBase()
Get the config base file.
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.
void handlerSigTerm(int signum, siginfo_t *siginf, void *ucont)
Handles SIGTERM, SIGQUIT, and SIGINT. Sets m_shutdown to 1 and logs the signal.
std::string driverCtrlName()
Get the INDI control FIFO file name.
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.
INDI_SETCALLBACK_DECL(MagAOXApp, m_indiP_powerChannel)
unsigned long loopPause()
Get the loop pause time.
int powerState()
Returns the current power state.
std::string pidFileName
The name of the PID file.
void sendGetPropertySetList(bool all=false)
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.
std::unordered_map< std::string, indiCallBack > m_indiNewCallBacks
Map to hold the NewProperty indiCallBacks for this App, with fast lookup by property name.
int setEuidReal()
Set the effective user ID to the real value, i.e. the file owner.
void updateIfChanged(pcf::IndiProperty &p, const std::vector< std::string > &els, const std::vector< T > &newVals, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update an INDI property if values have changed.
pcf::IndiProperty m_indiP_powerChannel
INDI property used to communicate power state.
std::string m_secretsPath
Path to the secrets directory, where passwords, etc, are stored.
std::unordered_map< std::string, indiCallBack >::iterator callBackIterator
Iterator type of the indiCallBack map.
int createINDIFIFOS()
Create the INDI FIFOs.
logger::logManager< MagAOXApp< _useINDI >, logFileRaw< verboseT > > logManagerT
The log manager type.
int shutdown()
Get the value of the shutdown flag.
int createStandardIndiSelectionSw(pcf::IndiProperty &prop, const std::string &name, const std::vector< std::string > &elements, const std::string &label="", const std::string &group="")
std::string m_powerDevice
The INDI device name of the power controller.
int sendNewProperty(const pcf::IndiProperty &ipSend)
Send a newProperty command to another device (using the INDI Client interface)
void state(const stateCodes::stateCodeT &s, bool stateAlert=false)
Set the current state code.
void handleNewProperty(const pcf::IndiProperty &ipRecv)
Handler for the new INDI property request.
int powerStateTarget()
Returns the target power state.
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.
std::pair< std::string, indiCallBack > callBackValueType
Value type of the indiCallBack map.
std::string m_calibDir
The path to calibration files for MagAOX.
std::string configDir()
Get the config directory.
std::string secretsPath()
Get the secrets path.
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.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
int newCallBack_clearFSMAlert(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the FSM Alert Clear request.
MagAOXApp(const std::string &git_sha1, const bool git_modified)
Public c'tor. Handles uid, logs git repo status, and initializes static members.
void handleGetProperties(const pcf::IndiProperty &ipRecv)
Handler for the get INDI properties request.
std::string m_configDir
The path to configuration files for MagAOX.
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.
XWC_DEFAULT_VERBOSITY verboseT
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
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.
std::string driverOutName()
Get the INDI output FIFO file name.
std::pair< callBackIterator, bool > callBackInsertResult
Return type of insert on the indiCallBack map.
pcf::IndiProperty m_indiP_clearFSMAlert
indi Property to clear an FSM alert.
virtual int whilePowerOff()
This method is called while the power is off, once per FSM loop.
std::string m_basePath
The base path of the MagAO-X system.
bool powerOnWaitElapsed()
This method tests whether the power on wait time has elapsed.
std::string m_powerChannel
The INDI property name of the channel controlling this device's power.
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.
int clearFSMAlert()
Clear the FSM alert state.
std::string cpusetPath()
Get the cpuset path.
std::mutex m_indiMutex
Mutex for locking INDI communications.
std::string m_configBase
The name of a base config class for this app (minus .conf).
std::string configName()
Get the config name.
int createStandardIndiSelectionSw(pcf::IndiProperty &prop, const std::string &name, const std::vector< std::string > &elements, const std::vector< std::string > &elementLabels, const std::string &label="", const std::string &group="")
Create a standard R/W INDI selection (one of many) switch with vector of elements and element labels.
std::string m_driverOutName
Full path name of the INDI driver output FIFO.
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.
int startINDI()
Start INDI Communications.
std::string m_sysPath
The path to the system directory, for PID file, etc.
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
int 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.
~MagAOXApp() noexcept(true)
void updatesIfChanged(pcf::IndiProperty &p, const std::vector< const char * > &els, const std::vector< T > &newVals, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
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.
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.
std::string sysPath()
Get the system path.
std::string driverInName()
Get the INDI input FIFO file name.
int indiTargetUpdate(pcf::IndiProperty &localProperty, T &localTarget, const pcf::IndiProperty &remoteProperty, bool setBusy=true)
Get the target element value from an new property.
int unlockPID()
Remove the PID file.
std::string calibDir()
Get the calibration directory.
std::string m_driverCtrlName
Full path name of the INDI driver control FIFO.
bool stateAlert()
Get the value of the state alert flag.
pcf::IndiProperty m_indiP_state
indi Property to report the application state.
int setEuidCalled()
Set the effective user ID to the called value, i.e. the highest possible.
std::unordered_map< std::string, indiCallBack > m_indiSetCallBacks
Map to hold the SetProperty indiCallBacks for this App, with fast lookup by property name.
std::string m_driverInName
Full path name of the INDI driver input FIFO.
A class to manage raw binary log files.
#define MAGAOX_RT_SCHED_POLICY
The real-time scheduling policy.
#define XWC_DEFAULT_VERBOSITY
#define MAGAOX_default_loopPause
The default application loopPause.
#define MAGAOX_calibRelPath
The relative path to the calibration files.
#define MAGAOX_driverFIFORelPath
The relative path to the INDI driver FIFOs.
#define MAGAOX_configRelPath
The relative path to the configuration files.
#define MAGAOX_logRelPath
The relative path to the log directory.
#define MAGAOX_sysRelPath
The relative path to the system directory.
#define MAGAOX_path
The path to the MagAO-X system files.
#define MAGAOX_secretsRelPath
The relative path to the secrets directory. Used for storing passwords, etc.
#define MAGAOX_cpusetPath
The absolute path to the cpuset mount point.
#define MAGAOX_env_sys
Environment variable setting the relative system directory path.
#define MAGAOX_env_path
Environment variable setting the MagAO-X path.
#define MAGAOX_env_log
Environment variable setting the relative log path.
#define MAGAOX_env_calib
Environment variable setting the relative calib path.
#define MAGAOX_env_secrets
Environment variable setting the relative secrets path.
#define MAGAOX_env_config
Environment variable setting the relative config path.
#define MAGAOX_env_cpuset
Environment variable setting the cpu set path.
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define INDI_SETCALLBACK(prop)
Get the name of the static callback wrapper for a set property.
int8_t logPrioT
The type of the log priority code.
std::shared_ptr< char > bufferPtrT
The log entry buffer smart pointer.
static int logLevel(bufferPtrT &logBuffer, const logPrioT &lvl)
Set the level of a log entry in a logBuffer header.
static int timespec(bufferPtrT &logBuffer, const timespecX &ts)
Set the timespec of a log entry.
MagAO-X INDI Driver Wrapper.
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
iosT & logMinStdFormat(iosT &ios, flatlogs::bufferPtrT &buffer)
iosT & logStdFormat(iosT &ios, flatlogs::bufferPtrT &buffer)
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
static constexpr logPrioT LOG_DEBUG
Used for debugging.
Namespace for all libXWC tests.
MagAO-X Application States.
Structure to hold the call-back details for handling INDI communications.
int16_t stateCodeT
The type of the state code.
The type of the input message.
The standard MagAOX log manager, used for both process logs and telemetry streams.
Software CRITICAL log entry.
A simple text log, a string-type log.
A fixed-width timespec structure.
nanosecT time_ns
Nanoseconds.
secT time_s
Time since the Unix epoch.
#define XWCTEST_NAMESPACE