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
159template <
bool _useINDI = true>
163 #ifdef XWCTEST_NAMESPACE
164 friend struct libXWCTest::appTest::MagAOXAppTest::XWCTEST_NAMESPACE::MagAOXApp_test;
226 const bool git_modified
239 virtual
void setDefaults(
int argc,
251 virtual
void setupBasicConfig();
261 virtual
void loadBasicConfig();
267 virtual
void checkConfig();
297 virtual
int execute();
310 virtual
int appStartup() = 0;
321 virtual
int appLogic() = 0;
326 virtual
int appShutdown() = 0;
343 template <typename logT,
int retval = 0>
344 static
int log( const typename logT::messageT &msg,
345 logPrioT level = logPrio::LOG_DEFAULT
356 template <typename logT,
int retval = 0>
357 static
int log(
logPrioT level = logPrio::LOG_DEFAULT
373 static
void configLog( const std::
string &name,
375 const std::
string &value,
376 const std::
string &source
390 int setSigTermHandler();
393 static
void _handlerSigTerm(
int signum,
399 void handlerSigTerm(
int signum,
424 bool m_elevated{
false };
544 template <
class thisPtr,
class Function>
549 pcf::IndiProperty &thProp,
551 const std::string &cpuset,
552 const std::string &thrdName,
568 bool m_stateAlert{
false };
571 bool m_gitAlert{
false };
573 int m_stateLogged{ 0 };
589 bool stateAlert =
false
643 constexpr static bool m_useINDI = _useINDI;
657 pcf::IndiProperty *
property{ 0 };
658 int ( *callBack )(
void *,
const pcf::IndiProperty & ){ 0 };
661 bool m_defReceived{
false };
688 bool m_allDefsReceived{
false };
708 const std::string &propName,
709 const std::string &label =
"",
711 const std::string &group =
""
721 const std::string &propName,
722 const std::string &elName,
723 const std::string &propLabel =
"",
724 const std::string &propGroup =
"",
725 const std::string &elLabel =
""
733 template <
typename T>
735 const std::string &name,
742 const std::string &format,
745 const std::string &label =
"",
747 const std::string &group =
""
756 const std::string &propName,
757 const std::string &propLabel =
"",
759 const std::string &propGroup =
""
769 const std::string &name,
770 const std::string &label =
"",
772 const std::string &group =
""
782 const std::string &name,
783 const std::string &label =
"",
785 const std::string &group =
""
795 const std::string &name,
796 const std::vector<std::string> &elements,
798 const std::vector<std::string> &elementLabels,
800 const std::string &label =
"",
802 const std::string &group =
""
813 const std::string &name,
814 const std::vector<std::string> &elements,
816 const std::string &label =
"",
818 const std::string &group =
""
841 const std::string &propName,
843 const pcf::IndiProperty::Type &propType,
845 const pcf::IndiProperty::PropertyPermType &propPerm,
848 const pcf::IndiProperty::PropertyStateType &propState
861 int ( * )(
void *,
const pcf::IndiProperty & )
873 pcf::IndiProperty &prop,
874 const std::string &propName,
875 const pcf::IndiProperty::Type &propType,
876 const pcf::IndiProperty::PropertyPermType &propPerm,
877 const pcf::IndiProperty::PropertyStateType &propState,
878 int ( * )(
void *,
const pcf::IndiProperty & )
889 pcf::IndiProperty &prop,
890 const std::string &propName,
891 const pcf::IndiProperty::Type &propType,
892 const pcf::IndiProperty::PropertyPermType &propPerm,
893 const pcf::IndiProperty::PropertyStateType &propState,
894 const pcf::IndiProperty::SwitchRuleType &propRule,
895 int ( * )(
void *,
const pcf::IndiProperty & )
906 pcf::IndiProperty &prop,
907 const std::string &devName,
908 const std::string &propName,
909 int ( * )(
void *,
const pcf::IndiProperty & )
970 template <
typename T>
972 const std::string &el,
974 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
989 const std::string &el,
991 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
1000 const std::string &el,
1001 const pcf::IndiElement::SwitchStateType &newVal,
1002 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
1016 template <
typename T>
1018 pcf::IndiProperty &p,
1019 const std::string &el,
1020 const std::vector<T> &newVals,
1021 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok
1033 template <
typename T>
1035 const std::vector<std::string> &els,
1036 const std::vector<T> &newVals,
1037 pcf::IndiProperty::PropertyStateType newState =
1038 pcf::IndiProperty::Ok
1041 template <
typename T>
1043 const std::vector<const char *> &els,
1044 const std::vector<T> &newVals,
1045 pcf::IndiProperty::PropertyStateType newState =
1046 pcf::IndiProperty::Ok
1054 template <
typename T>
1057 const pcf::IndiProperty &remoteProperty,
1067 template <
typename T>
1069 const std::string &el,
1086 const std::string &property,
1103 const pcf::IndiProperty &ipRecv
1130 bool m_powerMgtEnabled{
false };
1137 std::string m_powerElement{
"state" };
1138 std::string m_powerTargetElement{
"target" };
1140 unsigned long m_powerOnWait{ 0 };
1143 int m_powerOnCounter{ -1 };
1147 int m_powerState{ -1 };
1148 int m_powerTargetState{ -1 };
1280template <
bool _useINDI>
1284template <
bool _useINDI>
1287template <
bool _useINDI>
1290 if( m_self !=
nullptr )
1292 throw std::logic_error(
"Attempt to instantiate 2nd MagAOXApp");
1298 getresuid( &m_euidReal, &m_euidCalled, &m_suid );
1301 m_log.parent(
this );
1304 config.m_sources =
true;
1305 config.configLog = configLog;
1317 if( MXLIB_UNCOMP_REPO_MODIFIED )
1323 log<git_state>(
git_state::messageT(
"mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED ), gl );
1326template <
bool _useINDI>
1330 delete m_indiDriver;
1331 m_log.parent(
nullptr );
1337template <
bool _useINDI>
1346 m_basePath = tmpstr;
1359 m_configDir = m_basePath +
"/" + tmpstr;
1360 m_configPathGlobal = m_configDir +
"/magaox.conf";
1368 m_calibDir = m_basePath +
"/" + tmpstr;
1376 m_log.logPath( m_basePath +
"/" + tmpstr );
1384 m_sysPath = m_basePath +
"/" + tmpstr;
1392 m_secretsPath = m_basePath +
"/" + tmpstr;
1398 m_cpusetPath = tmpstr;
1402 if( m_configBase !=
"" )
1405 m_configPathUser = m_configDir +
"/" + m_configBase +
".conf";
1417 "The name of the application and its device name in INDI (if used), specifies the config file in the XWC config directory." );
1419 config.parseCommandLine( argc, argv,
"name" );
1420 config( m_configName,
"name" );
1422 if( m_configName ==
"" )
1424 m_configName = mx::ioutils::pathStem( invokedName );
1427 log<text_log>(
"Configuration Error: Application name (-n --name) not set." );
1433 m_configPathLocal = m_configDir +
"/" + m_configName +
".conf";
1436 if( registerIndiPropertyNew(
1437 m_indiP_state,
"fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0 ) <
1440 log<software_error>( { __FILE__, __LINE__,
"failed to register read only fsm_state property" } );
1443 m_indiP_state.add( pcf::IndiElement(
"state" ) );
1445 createStandardIndiRequestSw( m_indiP_clearFSMAlert,
"fsm_clear_alert",
"Clear FSM Alert",
"FSM" );
1446 if( registerIndiPropertyNew( m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert ) < 0 )
1448 log<software_error>( { __FILE__, __LINE__,
"failed to register new fsm_alert property" } );
1454template <
bool _useINDI>
1458 config.add(
"config.validate",
1466 "Validate the configuration. App will exit after loading the configuration, but before "
1467 "entering the event loop. Errors from configuration processing will be shown. "
1468 "Always safe to run." );
1471 config.add(
"loopPause",
1479 "The main loop pause time in ns" );
1482 "ignore_git",
"",
"ignore-git", argType::True,
"",
"",
false,
"bool",
"set to true to ignore git "
1483 "status to prevent the fsm_alert" );
1486 m_log.setupConfig( config );
1488 if( m_powerMgtEnabled )
1490 if( _useINDI ==
false )
1493 log<software_critical>( { __FILE__, __LINE__,
"power management is enabled but we are not using INDI" } );
1498 config.add(
"power.device",
1506 "Device controlling power for this app's device (INDI name)." );
1508 config.add(
"power.channel",
1516 "Channel on device for this app's device (INDI name)." );
1518 config.add(
"power.element",
1526 "INDI power state element name. Default is \"state\", only need to specify if different." );
1528 config.add(
"power.targetElement",
1530 "power.targetElement",
1536 "INDI power target element name. Default is \"target\", only need to specify if different." );
1538 config.add(
"power.powerOnWait",
1540 "power.powerOnWait",
1546 "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec." );
1551template <
bool _useINDI>
1556 config( ig,
"ignore_git" );
1558 if( !ig && m_gitAlert )
1560 m_stateAlert =
true;
1564 if(config.isSet(
"config.validate"))
1566 m_configOnly =
true;
1570 m_log.logName( m_configName );
1571 m_log.loadConfig( config );
1574 config( m_loopPause,
"loopPause" );
1577 if( m_powerMgtEnabled )
1579 config( m_powerDevice,
"power.device" );
1580 config( m_powerChannel,
"power.channel" );
1581 config( m_powerElement,
"power.element" );
1582 config( m_powerTargetElement,
"power.targetElement" );
1584 if( m_powerDevice !=
"" && m_powerChannel !=
"" )
1586 log<text_log>(
"enabling power management: " + m_powerDevice +
"." + m_powerChannel +
"." + m_powerElement +
1587 "/" + m_powerTargetElement );
1588 if( registerIndiPropertySet(
1589 m_indiP_powerChannel, m_powerDevice, m_powerChannel,
INDI_SETCALLBACK( m_indiP_powerChannel ) ) <
1592 log<software_error>( { __FILE__, __LINE__,
"failed to register set property" } );
1601 config( m_powerOnWait,
"power.powerOnWait" );
1602 if( m_powerOnWait > 3600 )
1604 log<text_log>(
"powerOnWait longer than 1 hour. Setting to 0.",
logPrio::LOG_ERROR );
1610template <
bool _useINDI>
1615 for(
auto it = config.m_targets.begin();
it != config.m_targets.end(); ++
it )
1617 if(
it->second.used ==
false )
1619 std::string
msg =
it->second.name;
1620 if( config.m_sources &&
it->second.sources.size() > 0 )
1622 msg +=
" [" +
it->second.sources[0] +
"]";
1630 if( config.m_unusedConfigs.size() > 0 )
1632 for(
auto it = config.m_unusedConfigs.begin();
it != config.m_unusedConfigs.end(); ++
it )
1634 if(
it->second.used ==
true )
1639 std::string
msg =
it->second.name;
1640 if( config.m_sources &&
it->second.sources.size() > 0 )
1642 msg +=
" [" +
it->second.sources[0] +
"]";
1651 if( config.nonOptions.size() > 0 )
1653 for(
size_t n = 0; n < config.nonOptions.size(); ++n )
1655 log<text_log>(
"Unrecognized command line argument: " + config.nonOptions[n],
logPrio::LOG_CRITICAL );
1662 if(m_shutdown ==
true)
1664 std::cerr <<
"\nThere were configuration errors.\n\n";
1668 std::cerr <<
"\nConfiguration is valid.\n\n";
1671 else if(m_shutdown ==
true)
1677template <
bool _useINDI>
1684 #ifndef XWC_DISABLE_USER_CHECK
1686 struct stat logstat;
1688 if( stat( m_log.logPath().c_str(), &logstat ) < 0 )
1690 state( stateCodes::FAILURE );
1691 std::cerr <<
"\nCRITICAL: Can not stat the log path.\n\n";
1696 #ifdef XWCTEST_MAGAOXAPP_EXEC_WRONG_USER
1697 logstat.st_uid = geteuid()+1;
1700 if( logstat.st_uid != geteuid() )
1702 state( stateCodes::FAILURE );
1703 std::cerr <<
"\nCRITICAL: You are running this app as the wrong user.\n\n";
1710 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1711 int testTimesThrough = 0;
1719 state( stateCodes::FAILURE );
1722 std::cerr <<
"\nCRITICAL: Failed to lock PID. Exiting.\n\n";
1731 m_log.logThreadStart();
1734 #ifdef XWCTEST_MAGAOXAPP_EXEC_LOG_START
1735 m_log.logShutdown(
true);
1740 while( m_log.logThreadRunning() ==
false && w < 20 )
1743 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( 100000000 ) );
1747 if( m_log.logThreadRunning() ==
false )
1749 state( stateCodes::FAILURE );
1752 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1756 if( unlockPID() < 0 )
1758 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1767 if( m_shutdown == 0 )
1769 if( setSigTermHandler() < 0 )
1771 state( stateCodes::FAILURE );
1773 log<software_critical>( { __FILE__, __LINE__,
"error from setSigTermHandler()" } );
1777 if( unlockPID() < 0 )
1779 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1789 if( m_shutdown == 0 )
1791 state( stateCodes::INITIALIZED );
1793 if( appStartup() < 0 )
1795 state( stateCodes::FAILURE );
1797 log<software_critical>( { __FILE__, __LINE__,
"error from appStartup()" } );
1801 if( unlockPID() < 0 )
1803 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1811 if( m_useINDI && m_shutdown == 0 )
1813 if( startINDI() < 0 )
1815 state( stateCodes::FAILURE );
1817 log<software_critical>( { __FILE__, __LINE__,
"INDI failed to start." } );
1822 if( appShutdown() < 0 )
1824 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1827 if( unlockPID() < 0 )
1829 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1839 if( m_powerMgtEnabled && m_shutdown == 0 )
1842 while( m_powerState < 0 && !m_shutdown )
1845 if( m_powerState < 0 )
1847 if( !stateLogged() )
1849 log<text_log>(
"waiting for power state" );
1857 state( stateCodes::ERROR );
1862 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1867 if( m_powerState > 0 )
1869 state( stateCodes::POWERON );
1873 m_powerOnCounter = 0;
1874 state( stateCodes::POWEROFF );
1875 if( onPowerOff() < 0 )
1877 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1893 while( m_shutdown == 0 )
1896 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1897 if(testTimesThrough > 1)
1904 if( m_log.logThreadRunning() ==
false )
1906 state( stateCodes::FAILURE );
1909 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1917 if( m_powerMgtEnabled )
1919 if( state() == stateCodes::POWEROFF )
1921 if( m_powerState == 1 )
1923 m_powerOnCounter = 0;
1924 state( stateCodes::POWERON );
1929 if( m_powerState == 0 )
1931 state( stateCodes::POWEROFF );
1932 if( onPowerOff() < 0 )
1934 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1945 if( !m_powerMgtEnabled || m_powerState > 0 )
1947 if( appLogic() < 0 )
1949 log<software_error>( { __FILE__, __LINE__,
"error from appLogic()" } );
1955 else if( m_powerState == 0 )
1957 if( whilePowerOff() < 0 )
1959 log<software_error>( { __FILE__, __LINE__,
"error from whilePowerOff()" } );
1966 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1980 sendGetPropertySetList(
false );
1988 if( m_shutdown == 0 )
1990 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( m_loopPause ) );
1994 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1999 if( appShutdown() < 0 )
2002 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
2005 state( stateCodes::SHUTDOWN );
2008 if( m_indiDriver !=
nullptr )
2010 pcf::IndiProperty ipSend;
2011 ipSend.setDevice( m_configName );
2014 m_indiDriver->sendDelProperty( ipSend );
2016 catch(
const std::exception &e )
2018 log<software_error>( { __FILE__,
2020 std::string(
"exception caught from"
2021 " sendDelProperty: " ) +
2025 m_indiDriver->quitProcess();
2026 m_indiDriver->deactivate();
2027 log<indidriver_stop>();
2030 if( unlockPID() < 0 )
2033 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
2040template <
bool _useINDI>
2041template <
typename logT,
int retval>
2044 m_log.template log<logT>(
msg, level );
2048template <
bool _useINDI>
2049template <
typename logT,
int retval>
2052 m_log.template log<logT>( level );
2056template <
bool _useINDI>
2067 state( m_state,
true );
2070 if( _useINDI && m_indiDriver )
2072 pcf::IndiProperty
msg;
2073 msg.setDevice( m_configName );
2075 std::stringstream logstdf;
2078 msg.setMessage( logstdf.str() );
2084 tv.tv_usec = (
long int)( ( (
double)ts.
time_ns ) / 1e3 );
2086 msg.setTimeStamp( pcf::TimeStamp( tv ) );
2090 m_indiDriver->sendMessage(
msg );
2092 catch(
const std::exception &e )
2094 log<software_error>(
2095 { __FILE__, __LINE__, std::string(
"exception caught from sendMessage: " ) + e.what() } );
2100template <
bool _useINDI>
2103 const std::string &value,
2104 const std::string &source )
2106 m_log.template log<config_log>( { name, code, value, source } );
2109template <
bool _useINDI>
2113 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_ERR
2117 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGTERM
2119 #define SIGTERM SIGKILL
2122 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGQUIT
2124 #define SIGQUIT SIGKILL
2127 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGINT
2129 #define SIGINT SIGKILL
2134 struct sigaction act;
2138 act.sa_flags = SA_SIGINFO;
2139 sigemptyset( &set );
2143 if( sigaction( SIGTERM, &act, 0 ) < 0 )
2145 std::string logss =
"Setting handler for SIGTERM failed. Errno says: ";
2146 logss += strerror( errno );
2148 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2154 if( sigaction( SIGQUIT, &act, 0 ) < 0 )
2156 std::string logss =
"Setting handler for SIGQUIT failed. Errno says: ";
2157 logss += strerror( errno );
2159 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2165 if( sigaction( SIGINT, &act, 0 ) < 0 )
2167 std::string logss =
"Setting handler for SIGINT failed. Errno says: ";
2168 logss += strerror( errno );
2170 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2175 log<text_log>(
"Installed SIGTERM/SIGQUIT/SIGINT signal handler.",
logPrio::LOG_DEBUG );
2180template <
bool _useINDI>
2186template <
bool _useINDI>
2188 siginfo_t *siginf __attribute__( ( unused ) ),
2189 void *ucont __attribute__( ( unused ) ) )
2193 std::string signame;
2197 signame =
"SIGTERM";
2203 signame =
"SIGQUIT";
2209 std::string logss =
"Caught signal ";
2211 logss +=
". Shutting down.";
2213 std::cerr <<
"\n" << logss << std::endl;
2214 log<text_log>( logss );
2218void sigUsr1Handler(
int signum, siginfo_t *siginf,
void *ucont );
2220template <
bool _useINDI>
2224 if( sys::th_seteuid( m_euidCalled ) < 0 )
2226 log<software_error>( { __FILE__,
2230 std::format(
"Setting effective user id to "
2231 "euidCalled ({}) failed. "
2234 strerror( errno ) ) } );
2241template <
bool _useINDI>
2245 if( sys::th_seteuid( m_euidReal ) < 0 )
2247 log<software_error>( { __FILE__,
2251 std::format(
"Setting effective user id to "
2252 "euidReal ({}) failed. "
2255 strerror( errno ) ) } );
2263template <
bool _useINDI>
2268 std::string statusDir = m_sysPath;
2276 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2278 if( errno != EEXIST )
2284 "Failed to create root of statusDir (" + statusDir +
2287 strerror( errno ) } );
2292 statusDir += m_configName;
2294 pidFileName = statusDir +
"/pid";
2299 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2301 if( errno != EEXIST )
2307 "Failed to create statusDir (" + statusDir +
2310 strerror( errno ) } );
2315 std::ifstream pidIn;
2316 pidIn.open( pidFileName );
2326 std::stringstream procN;
2327 procN <<
"/proc/" << testPid <<
"/cmdline";
2329 std::ifstream procIn;
2330 std::string pidCmdLine;
2334 procIn.open( procN.str() );
2337 procIn >> pidCmdLine;
2343 log<
software_critical, -1>( { __FILE__, __LINE__, 0, 0,
"exception caught testing /proc/pid" } );
2350 size_t invokedPos = pidCmdLine.find( invokedName );
2353 size_t configPos = std::string::npos;
2354 if( invokedPos != std::string::npos )
2356 configPos = pidCmdLine.find( m_configName );
2360 #ifdef XWCTEST_MAGAOXAPP_PID_LOCKED
2367 if( invokedPos != std::string::npos && configPos != std::string::npos )
2370 std::cerr <<
"PID already locked (" + std::to_string( testPid ) +
"). Time to die." << std::endl;
2372 return log<
text_log, -1>(
"PID already locked (" + std::to_string( testPid ) +
"). Time to die." );
2383 std::ofstream pidOut;
2384 pidOut.open( pidFileName );
2387 #ifdef XWCTEST_MAGAOXAPP_PID_WRITE_FAIL
2392 if( !( pidOut << m_pid ) )
2394 return log<
software_critical, -1>( { __FILE__, __LINE__, errno, 0,
"failed to write to pid file." } );
2399 return log<text_log, 0>(
"PID (" + std::to_string( m_pid ) +
") locked." );
2402template <
bool _useINDI>
2406 #ifdef XWCTEST_MAGAOXAPP_PID_UNLOCK_ERR
2415 if( ::remove( pidFileName.c_str() ) < 0 )
2417 log<software_error>(
2418 { __FILE__, __LINE__, errno, 0, std::string(
"Failed to remove PID file: " ) + strerror( errno ) } );
2423 return log<text_log, 0>(
"PID (" + std::to_string(m_pid) +
") unlocked." );
2427template <
bool _useINDI>
2428template <
class thisPtr,
class Function>
2432 pcf::IndiProperty &thProp,
2434 const std::string &cpuset,
2435 const std::string &thrdName,
2437 Function &&thrdStart )
2445 thrd = std::thread( thrdStart, thrdThis );
2447 catch(
const std::exception &e )
2449 log<software_error>(
2450 { __FILE__, __LINE__, std::string(
"Exception on " + thrdName +
" thread start: " ) + e.what() } );
2455 log<software_error>( { __FILE__, __LINE__,
"Unkown exception on " + thrdName +
" thread start" } );
2459 if( !thrd.joinable() )
2461 log<software_error>( { __FILE__, __LINE__, thrdName +
" thread did not start" } );
2473 sp.sched_priority = thrdPrio;
2487 rv = pthread_setschedparam( thrd.native_handle(), SCHED_OTHER, &sp );
2492 log<software_error>(
2496 "Setting " + thrdName +
" thread scheduler priority to " + std::to_string( thrdPrio ) +
" failed." } );
2500 log<text_log>( thrdName +
" thread scheduler priority set to " + std::to_string( thrdPrio ) );
2506 for(
int i = 0; i < 10; ++i )
2508 mx::sys::milliSleep( 100 );
2516 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"tpid for " + thrdName +
" not set." } );
2520 log<text_log>( thrdName +
" thread pid is " + std::to_string( tpid ) );
2524 thProp = pcf::IndiProperty( pcf::IndiProperty::Number );
2525 thProp.setDevice( configName() );
2526 thProp.setName( std::string(
"th-" ) + thrdName );
2527 thProp.setPerm( pcf::IndiProperty::ReadOnly );
2528 thProp.setState( pcf::IndiProperty::Idle );
2529 thProp.add( pcf::IndiElement(
"pid" ) );
2530 thProp[
"pid"] = tpid;
2531 thProp.add( pcf::IndiElement(
"prio" ) );
2532 thProp[
"prio"] = thrdPrio;
2533 registerIndiPropertyReadOnly( thProp );
2539 std::string cpuFile = m_cpusetPath;
2540 cpuFile +=
"/" + cpuset;
2541 cpuFile +=
"/tasks";
2542 int wfd = open( cpuFile.c_str(), O_WRONLY );
2545 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error from open for " + cpuFile } );
2549 snprintf( pids,
sizeof( pids ),
"%d", tpid );
2551 int w = write( wfd, pids, strnlen( pids,
sizeof( pids ) ) );
2552 if( w != (
int)strnlen( pids,
sizeof(pids) ) )
2554 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error on write" } );
2568template <
bool _useINDI>
2574template <
bool _useINDI>
2581 if( s == stateCodes::ERROR )
2583 if( s == stateCodes::FAILURE )
2586 log<state_change>( { m_state, s }, lvl );
2592 if( m_stateAlert != stateAlert && stateAlert ==
true )
2594 m_stateAlert = stateAlert;
2599 std::unique_lock<std::mutex>
lock( m_indiMutex,
2603 if(
lock.owns_lock() )
2606 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2609 if( m_stateAlert ==
true )
2615 if( m_state == stateCodes::READY )
2617 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING ||
2618 m_state == stateCodes::CONFIGURING )
2620 else if( m_state < stateCodes::NODEVICE )
2622 else if( m_state <= stateCodes::LOGGEDIN )
2624 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2628 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2632template <
bool _useINDI>
2635 return m_stateAlert;
2638template <
bool _useINDI>
2644template <
bool _useINDI>
2647 if( m_stateLogged > 0 )
2650 return m_stateLogged - 1;
2659template <
bool _useINDI>
2662 if( m_stateAlert ==
false )
2667 m_stateAlert =
false;
2671 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2673 if( m_state == stateCodes::READY )
2677 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING || m_state == stateCodes::CONFIGURING )
2681 else if( m_state < stateCodes::NODEVICE )
2685 else if( m_state <= stateCodes::LOGGEDIN )
2689 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2694 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2703template <
bool _useINDI>
2705 const std::string &propName,
2706 const std::string &label,
2707 const std::string &group )
2709 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2710 prop.setDevice( configName() );
2711 prop.setName( propName );
2712 prop.setPerm( pcf::IndiProperty::ReadWrite );
2713 prop.setState( pcf::IndiProperty::Idle );
2714 prop.add( pcf::IndiElement(
"current" ) );
2715 prop.add( pcf::IndiElement(
"target" ) );
2720 prop.setLabel( label );
2725 prop.setGroup( group );
2731template <
bool _useINDI>
2733 const std::string &propName,
2734 const std::string &elName,
2735 const std::string &propLabel,
2736 const std::string &propGroup,
2737 const std::string &elLabel )
2739 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2740 prop.setDevice( configName() );
2741 prop.setName( propName );
2742 prop.setPerm( pcf::IndiProperty::ReadOnly );
2743 prop.setState( pcf::IndiProperty::Idle );
2746 if( propLabel !=
"" )
2748 prop.setLabel( propLabel );
2751 if( propGroup !=
"" )
2753 prop.setGroup( propGroup );
2756 prop.add( pcf::IndiElement( elName ) );
2760 prop[elName].setLabel( elLabel );
2766template <
bool _useINDI>
2767template <
typename T>
2769 const std::string &name,
2773 const std::string &format,
2774 const std::string &label,
2775 const std::string &group )
2777 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2778 prop.setDevice( configName() );
2779 prop.setName( name );
2780 prop.setPerm( pcf::IndiProperty::ReadWrite );
2781 prop.setState( pcf::IndiProperty::Idle );
2782 prop.add( pcf::IndiElement(
"current" ) );
2783 prop[
"current"].setMin( min );
2784 prop[
"current"].setMax( max );
2785 prop[
"current"].setStep( step );
2788 prop[
"current"].setFormat( format );
2791 prop.add( pcf::IndiElement(
"target" ) );
2792 prop[
"target"].setMin( min );
2793 prop[
"target"].setMax( max );
2794 prop[
"target"].setStep( step );
2797 prop[
"target"].setFormat( format );
2803 prop.setLabel( label );
2808 prop.setGroup( group );
2814template <
bool _useINDI>
2816 const std::string &propName,
2817 const std::string &propLabel,
2818 const std::string &propGroup )
2820 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2821 prop.setDevice( configName() );
2822 prop.setName( propName );
2823 prop.setPerm( pcf::IndiProperty::ReadOnly );
2824 prop.setState( pcf::IndiProperty::Idle );
2827 if( propLabel !=
"" )
2829 prop.setLabel( propLabel );
2832 if( propGroup !=
"" )
2834 prop.setGroup( propGroup );
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(
"toggle", pcf::IndiElement::Off ) );
2859 prop.setLabel( label );
2864 prop.setGroup( group );
2870template <
bool _useINDI>
2872 const std::string &name,
2873 const std::string &label,
2874 const std::string &group )
2876 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2877 prop.setDevice( configName() );
2878 prop.setName( name );
2879 prop.setPerm( pcf::IndiProperty::ReadWrite );
2880 prop.setState( pcf::IndiProperty::Idle );
2881 prop.setRule( pcf::IndiProperty::AtMostOne );
2884 prop.add( pcf::IndiElement(
"request", pcf::IndiElement::Off ) );
2889 prop.setLabel( label );
2894 prop.setGroup( group );
2900template <
bool _useINDI>
2902 const std::string &name,
2903 const std::vector<std::string> &elements,
2904 const std::vector<std::string> &elementLabels,
2905 const std::string &label,
2906 const std::string &group )
2908 if( elements.size() == 0 )
2910 return log<
software_error, -1>( { __FILE__, __LINE__,
"elements vector has zero size" } );
2913 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2914 prop.setDevice( configName() );
2915 prop.setName( name );
2916 prop.setPerm( pcf::IndiProperty::ReadWrite );
2917 prop.setState( pcf::IndiProperty::Idle );
2918 prop.setRule( pcf::IndiProperty::OneOfMany );
2921 for(
size_t n = 0; n < elements.size(); ++n )
2923 pcf::IndiElement elem = pcf::IndiElement( elements[n], pcf::IndiElement::Off );
2924 if(elementLabels[n] !=
"")
2926 elem.setLabel( elementLabels[n] );
2934 prop.setLabel( label );
2939 prop.setGroup( group );
2945template <
bool _useINDI>
2947 const std::string &name,
2948 const std::vector<std::string> &elements,
2949 const std::string &label,
2950 const std::string &group )
2952 return createStandardIndiSelectionSw( prop, name, elements, elements, label, group );
2955template <
bool _useINDI>
2967 if( !result.second )
2970 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2975 catch( std::exception &e )
2977 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2981 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2986template <
bool _useINDI>
2988 const std::string &propName,
2989 const pcf::IndiProperty::Type &propType,
2990 const pcf::IndiProperty::PropertyPermType &propPerm,
2991 const pcf::IndiProperty::PropertyStateType &propState )
3000 prop = pcf::IndiProperty( propType );
3001 prop.setDevice( m_configName );
3002 prop.setName( propName );
3003 prop.setPerm( propPerm );
3004 prop.setState( propState );
3008 if( !result.second )
3011 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3016 catch( std::exception &e )
3018 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3022 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3027template <
bool _useINDI>
3029 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3036 callBackInsertResult result =
3037 m_indiNewCallBacks.insert( callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
3039 if( !result.second )
3042 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3047 catch( std::exception &e )
3049 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3053 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3058template <
bool _useINDI>
3059int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
3060 const std::string &propName,
3061 const pcf::IndiProperty::Type &propType,
3062 const pcf::IndiProperty::PropertyPermType &propPerm,
3063 const pcf::IndiProperty::PropertyStateType &propState,
3064 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3069 prop = pcf::IndiProperty( propType );
3070 prop.setDevice( m_configName );
3071 prop.setName( propName );
3072 prop.setPerm( propPerm );
3073 prop.setState( propState );
3075 return registerIndiPropertyNew( prop, callBack );
3078template <
bool _useINDI>
3079int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
3080 const std::string &propName,
3081 const pcf::IndiProperty::Type &propType,
3082 const pcf::IndiProperty::PropertyPermType &propPerm,
3083 const pcf::IndiProperty::PropertyStateType &propState,
3084 const pcf::IndiProperty::SwitchRuleType &propRule,
3085 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3090 prop = pcf::IndiProperty( propType );
3091 prop.setDevice( m_configName );
3092 prop.setName( propName );
3093 prop.setPerm( propPerm );
3094 prop.setState( propState );
3095 prop.setRule( propRule );
3096 return registerIndiPropertyNew( prop, callBack );
3099template <
bool _useINDI>
3101 const std::string &devName,
3102 const std::string &propName,
3103 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3112 prop = pcf::IndiProperty();
3113 prop.setDevice( devName );
3114 prop.setName( propName );
3118 if( !result.second )
3121 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() +
". Possible duplicate." } );
3124 catch( std::exception &e )
3126 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3130 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3136template <
bool _useINDI>
3145 std::string driverFIFOPath = m_basePath;
3146 driverFIFOPath +=
"/";
3149 m_driverInName = driverFIFOPath +
"/" + configName() +
".in";
3150 m_driverOutName = driverFIFOPath +
"/" + configName() +
".out";
3151 m_driverCtrlName = driverFIFOPath +
"/" + configName() +
".ctrl";
3157 mode_t prev = umask( 0 );
3160 if( mkfifo( m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3162 if( errno != EEXIST )
3165 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3172 if( mkfifo( m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3174 if( errno != EEXIST )
3178 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3185 if( mkfifo( m_driverCtrlName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3187 if( errno != EEXIST )
3191 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3202template <
bool _useINDI>
3209 if( createINDIFIFOS() < 0 )
3217 if( m_indiDriver !=
nullptr )
3219 m_indiDriver->quitProcess();
3220 m_indiDriver->deactivate();
3221 log<indidriver_stop>();
3222 delete m_indiDriver;
3223 m_indiDriver =
nullptr;
3230 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction exception." } );
3235 if( m_indiDriver ==
nullptr )
3237 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction failed." } );
3242 if( m_indiDriver->good() ==
false )
3244 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver failed to open FIFOs." } );
3245 delete m_indiDriver;
3246 m_indiDriver =
nullptr;
3251 m_indiDriver->activate();
3252 log<indidriver_start>();
3254 sendGetPropertySetList(
true );
3259template <
bool _useINDI>
3263 if( !all && m_allDefsReceived )
3271 while(
it != m_indiSetCallBacks.end() )
3273 if( all ||
it->second.m_defReceived ==
false )
3275 if(
it->second.property )
3277 if(
it->first !=
it->second.property->createUniqueKey())
3279 std::cerr <<
it->first <<
" bad device\n";
3280 it->second.m_defReceived =
true;
3287 m_indiDriver->sendGetProperties( *(
it->second.property ) );
3289 catch(
const std::exception &e )
3291 log<software_error>( { __FILE__,
3293 "exception caught from sendGetProperties for " +
3294 it->second.property->getName() +
": " + e.what() } );
3298 it->second.m_defReceived =
false;
3306 m_allDefsReceived =
false;
3311 m_allDefsReceived =
true;
3315template <
bool _useINDI>
3318 handleSetProperty(
ipRecv );
3321template <
bool _useINDI>
3329 if( m_indiDriver ==
nullptr )
3335 if(
ipRecv.hasValidDevice() &&
ipRecv.getDevice() != m_indiDriver->getName() )
3341 if( !
ipRecv.hasValidName() )
3345 while(
it != m_indiNewCallBacks.end() )
3347 if(
it->second.property )
3351 m_indiDriver->sendDefProperty( *(
it->second.property ) );
3353 catch(
const std::exception &e )
3355 log<software_error>( { __FILE__,
3357 "exception caught from sendDefProperty for " +
3358 it->second.property->getName() +
": " + e.what() } );
3365 sendGetPropertySetList(
true );
3371 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3377 if( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property )
3381 m_indiDriver->sendDefProperty( *( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property ) );
3383 catch(
const std::exception &e )
3385 log<software_error>( { __FILE__,
3387 "exception caught from sendDefProperty for " +
3388 m_indiNewCallBacks[
ipRecv.createUniqueKey()].property->getName() +
": " +
3395template <
bool _useINDI>
3400 if( m_indiDriver ==
nullptr )
3404 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3406 log<software_debug>( { __FILE__, __LINE__,
"invalid NewProperty request for " +
ipRecv.createUniqueKey() } );
3410 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiNewCallBacks[
ipRecv.createUniqueKey()].callBack;
3413 callBack(
this,
ipRecv );
3415 log<software_debug>( { __FILE__, __LINE__,
"NewProperty callback null for " +
ipRecv.createUniqueKey() } );
3420template <
bool _useINDI>
3428 if( m_indiDriver ==
nullptr )
3433 std::string key =
ipRecv.createUniqueKey();
3436 if( m_indiSetCallBacks.count( key ) > 0 )
3438 m_indiSetCallBacks[key].m_defReceived =
true;
3441 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiSetCallBacks[key].callBack;
3445 callBack(
this,
ipRecv );
3458template <
bool _useINDI>
3459template <
typename T>
3461 const std::string &el,
3463 pcf::IndiProperty::PropertyStateType ipState )
3475 indi::updateIfChanged( p, el, newVal, m_indiDriver, ipState );
3478template <
bool _useINDI>
3480 const std::string &el,
3482 pcf::IndiProperty::PropertyStateType ipState )
3484 updateIfChanged<std::string>( p, el, std::string( newVal ), ipState );
3487template <
bool _useINDI>
3489 const std::string &el,
3490 const pcf::IndiElement::SwitchStateType &newVal,
3491 pcf::IndiProperty::PropertyStateType ipState )
3499 indi::updateSwitchIfChanged( p, el, newVal, m_indiDriver, ipState );
3502template <
bool _useINDI>
3503template <
typename T>
3505 const std::string &el,
3506 const std::vector<T> &newVals,
3507 pcf::IndiProperty::PropertyStateType ipState )
3515 std::vector<std::string> descriptors( newVals.size(), el );
3516 for(
size_t index = 0; index < newVals.size(); ++index )
3518 descriptors[index] += std::to_string( index );
3520 indi::updateIfChanged( p, descriptors, newVals, m_indiDriver );
3523template <
bool _useINDI>
3524template <
typename T>
3526 const std::vector<std::string> &els,
3527 const std::vector<T> &newVals,
3528 pcf::IndiProperty::PropertyStateType newState )
3536 indi::updateIfChanged( p, els, newVals, m_indiDriver, newState );
3539template <
bool _useINDI>
3540template <
typename T>
3542 const std::vector<const char *> &els,
3543 const std::vector<T> &newVals,
3544 pcf::IndiProperty::PropertyStateType newState )
3556 indi::updatesIfChanged( p, els, newVals, m_indiDriver, newState );
3559template <
bool _useINDI>
3560template <
typename T>
3563 const pcf::IndiProperty &remoteProperty,
3566 if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey() )
3571 if( !( remoteProperty.find(
"target" ) || remoteProperty.find(
"current" ) ) )
3578 if( remoteProperty.find(
"target" ) )
3580 localTarget = remoteProperty[
"target"].get<T>();
3586 if( remoteProperty.find(
"current" ) )
3588 localTarget = remoteProperty[
"current"].get<T>();
3600 updateIfChanged( localProperty,
"target", localTarget,
INDI_BUSY );
3604 updateIfChanged( localProperty,
"target", localTarget );
3610template <
bool _useINDI>
3611template <
typename T>
3621 log<software_error>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3624 pcf::IndiProperty ipToSend = ipSend;
3628 ipToSend[el].setValue( newVal );
3632 log<software_error>(
3633 { __FILE__, __LINE__,
"Exception caught setting " + ipSend.createUniqueKey() +
"." + el } );
3640 log<software_error>( { __FILE__, __LINE__ } );
3647template <
bool _useINDI>
3657 return log<
software_error, -1>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3660 if( m_indiDriver->sendNewProperty( ipSend ) < 0 )
3668template <
bool _useINDI>
3674 pcf::IndiProperty ipSend( pcf::IndiProperty::Switch );
3678 ipSend.setDevice( device );
3679 ipSend.setName( property );
3680 ipSend.add( pcf::IndiElement(
"toggle" ) );
3682 catch( std::exception &e )
3684 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"exception: " ) + e.what() } );
3687 if( onoff ==
false )
3689 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::Off );
3693 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::On );
3696 if( sendNewProperty( ipSend ) < 0 )
3699 { __FILE__, __LINE__,
"sendNewProperty failed for " + device +
"." +
property } );
3705template <
bool _useINDI>
3711template <
bool _useINDI>
3715 if(
ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey() )
3717 return log<
software_error, -1>( { __FILE__, __LINE__,
"wrong indi property received" } );
3720 if(
ipRecv.find(
"request" ) )
3722 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
3725 updateSwitchIfChanged( m_indiP_clearFSMAlert,
"request", pcf::IndiElement::Off,
INDI_IDLE );
3732template <
bool _useINDI>
3738template <
bool _useINDI>
3744template <
bool _useINDI>
3747 if( !m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0 )
3752 if( m_powerOnCounter * m_loopPause > ( (
double)m_powerOnWait ) * 1e9 )
3763template <
bool _useINDI>
3766 if( !m_powerMgtEnabled )
3771 return m_powerState;
3774template <
bool _useINDI>
3777 if( !m_powerMgtEnabled )
3782 return m_powerTargetState;
3785template <
bool _useINDI>
3793 ps =
ipRecv[m_powerElement].get<std::string>();
3799 else if( ps ==
"Off" )
3811 ps =
ipRecv[m_powerTargetElement].get<std::string>();
3815 m_powerTargetState = 1;
3817 else if( ps ==
"Off" )
3819 m_powerTargetState = 0;
3823 m_powerTargetState = -1;
3830template <
bool _useINDI>
3836template <
bool _useINDI>
3839 return m_configName;
3842template <
bool _useINDI>
3848template <
bool _useINDI>
3851 return m_configBase;
3854template <
bool _useINDI>
3860template <
bool _useINDI>
3866template <
bool _useINDI>
3869 return m_secretsPath;
3872template <
bool _useINDI>
3875 return m_cpusetPath;
3878template <
bool _useINDI>
3884template <
bool _useINDI>
3890template <
bool _useINDI>
3893 return m_driverInName;
3896template <
bool _useINDI>
3899 return m_driverOutName;
3902template <
bool _useINDI>
3905 return m_driverCtrlName;
3908#ifdef XWCTEST_NAMESPACE
3913extern template class MagAOXApp<true>;
3914extern template class MagAOXApp<false>;
3934#define XWCAPP_THREAD_START( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart ) \
3935 if( threadStart( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, this, thrdStart ) < 0 ) \
3937 log<software_error>( { __FILE__, __LINE__, "error from threadStart for " #thrdName } ); \
3948#define XWCAPP_THREAD_CHECK( thrdSt, thrdName ) \
3951 if( pthread_tryjoin_np( thrdSt.native_handle(), 0 ) == 0 ) \
3953 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3959 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3968#define XWCAPP_THREAD_STOP( thrdSt ) \
3969 if( thrdSt.joinable() ) \
3971 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