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 };
533 template <
class thisPtr,
class Function>
538 pcf::IndiProperty &thProp,
540 const std::string &cpuset,
541 const std::string &thrdName,
557 bool m_stateAlert{
false };
560 bool m_gitAlert{
false };
562 int m_stateLogged{ 0 };
578 bool stateAlert =
false
632 constexpr static bool m_useINDI = _useINDI;
646 pcf::IndiProperty *
property{ 0 };
647 int ( *callBack )(
void *,
const pcf::IndiProperty & ){ 0 };
650 bool m_defReceived{
false };
677 bool m_allDefsReceived{
false };
697 const std::string &propName,
698 const std::string &label =
"",
700 const std::string &group =
""
710 const std::string &propName,
711 const std::string &elName,
712 const std::string &propLabel =
"",
713 const std::string &propGroup =
"",
714 const std::string &elLabel =
""
722 template <
typename T>
724 const std::string &name,
731 const std::string &format,
734 const std::string &label =
"",
736 const std::string &group =
""
745 const std::string &propName,
746 const std::string &propLabel =
"",
748 const std::string &propGroup =
""
758 const std::string &name,
759 const std::string &label =
"",
761 const std::string &group =
""
771 const std::string &name,
772 const std::string &label =
"",
774 const std::string &group =
""
784 const std::string &name,
785 const std::vector<std::string> &elements,
787 const std::vector<std::string> &elementLabels,
789 const std::string &label =
"",
791 const std::string &group =
""
802 const std::string &name,
803 const std::vector<std::string> &elements,
805 const std::string &label =
"",
807 const std::string &group =
""
830 const std::string &propName,
832 const pcf::IndiProperty::Type &propType,
834 const pcf::IndiProperty::PropertyPermType &propPerm,
837 const pcf::IndiProperty::PropertyStateType &propState
850 int ( * )(
void *,
const pcf::IndiProperty & )
862 pcf::IndiProperty &prop,
863 const std::string &propName,
864 const pcf::IndiProperty::Type &propType,
865 const pcf::IndiProperty::PropertyPermType &propPerm,
866 const pcf::IndiProperty::PropertyStateType &propState,
867 int ( * )(
void *,
const pcf::IndiProperty & )
878 pcf::IndiProperty &prop,
879 const std::string &propName,
880 const pcf::IndiProperty::Type &propType,
881 const pcf::IndiProperty::PropertyPermType &propPerm,
882 const pcf::IndiProperty::PropertyStateType &propState,
883 const pcf::IndiProperty::SwitchRuleType &propRule,
884 int ( * )(
void *,
const pcf::IndiProperty & )
895 pcf::IndiProperty &prop,
896 const std::string &devName,
897 const std::string &propName,
898 int ( * )(
void *,
const pcf::IndiProperty & )
959 template <
typename T>
961 const std::string &el,
963 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
978 const std::string &el,
980 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
989 const std::string &el,
990 const pcf::IndiElement::SwitchStateType &newVal,
991 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
1005 template <
typename T>
1007 pcf::IndiProperty &p,
1008 const std::string &el,
1009 const std::vector<T> &newVals,
1010 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok
1022 template <
typename T>
1024 const std::vector<std::string> &els,
1025 const std::vector<T> &newVals,
1026 pcf::IndiProperty::PropertyStateType newState =
1027 pcf::IndiProperty::Ok
1030 template <
typename T>
1032 const std::vector<const char *> &els,
1033 const std::vector<T> &newVals,
1034 pcf::IndiProperty::PropertyStateType newState =
1035 pcf::IndiProperty::Ok
1043 template <
typename T>
1046 const pcf::IndiProperty &remoteProperty,
1056 template <
typename T>
1058 const std::string &el,
1075 const std::string &property,
1092 const pcf::IndiProperty &ipRecv
1119 bool m_powerMgtEnabled{
false };
1126 std::string m_powerElement{
"state" };
1127 std::string m_powerTargetElement{
"target" };
1129 unsigned long m_powerOnWait{ 0 };
1132 int m_powerOnCounter{ -1 };
1136 int m_powerState{ -1 };
1137 int m_powerTargetState{ -1 };
1269template <
bool _useINDI>
1273template <
bool _useINDI>
1276template <
bool _useINDI>
1279 if( m_self !=
nullptr )
1281 throw std::logic_error(
"Attempt to instantiate 2nd MagAOXApp");
1287 getresuid( &m_euidReal, &m_euidCalled, &m_suid );
1290 m_log.parent(
this );
1293 config.m_sources =
true;
1294 config.configLog = configLog;
1306 if( MXLIB_UNCOMP_REPO_MODIFIED )
1312 log<git_state>(
git_state::messageT(
"mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED ), gl );
1315template <
bool _useINDI>
1319 delete m_indiDriver;
1320 m_log.parent(
nullptr );
1326template <
bool _useINDI>
1335 m_basePath = tmpstr;
1348 m_configDir = m_basePath +
"/" + tmpstr;
1349 m_configPathGlobal = m_configDir +
"/magaox.conf";
1357 m_calibDir = m_basePath +
"/" + tmpstr;
1365 m_log.logPath( m_basePath +
"/" + tmpstr );
1373 m_sysPath = m_basePath +
"/" + tmpstr;
1381 m_secretsPath = m_basePath +
"/" + tmpstr;
1387 m_cpusetPath = tmpstr;
1391 if( m_configBase !=
"" )
1394 m_configPathUser = m_configDir +
"/" + m_configBase +
".conf";
1406 "The name of the application and its device name in INDI (if used), specifies the config file in the XWC config directory." );
1408 config.parseCommandLine( argc, argv,
"name" );
1409 config( m_configName,
"name" );
1411 if( m_configName ==
"" )
1413 m_configName = mx::ioutils::pathStem( invokedName );
1416 log<text_log>(
"Configuration Error: Application name (-n --name) not set." );
1422 m_configPathLocal = m_configDir +
"/" + m_configName +
".conf";
1425 if( registerIndiPropertyNew(
1426 m_indiP_state,
"fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0 ) <
1429 log<software_error>( { __FILE__, __LINE__,
"failed to register read only fsm_state property" } );
1432 m_indiP_state.add( pcf::IndiElement(
"state" ) );
1434 createStandardIndiRequestSw( m_indiP_clearFSMAlert,
"fsm_clear_alert",
"Clear FSM Alert",
"FSM" );
1435 if( registerIndiPropertyNew( m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert ) < 0 )
1437 log<software_error>( { __FILE__, __LINE__,
"failed to register new fsm_alert property" } );
1443template <
bool _useINDI>
1447 config.add(
"config.validate",
1455 "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." );
1458 config.add(
"loopPause",
1466 "The main loop pause time in ns" );
1469 "ignore_git",
"",
"ignore-git", argType::True,
"",
"",
false,
"bool",
"set to true to ignore git "
1470 "status to prevent the fsm_alert" );
1473 m_log.setupConfig( config );
1475 if( m_powerMgtEnabled )
1477 if( _useINDI ==
false )
1480 log<software_critical>( { __FILE__, __LINE__,
"power management is enabled but we are not using INDI" } );
1485 config.add(
"power.device",
1493 "Device controlling power for this app's device (INDI name)." );
1495 config.add(
"power.channel",
1503 "Channel on device for this app's device (INDI name)." );
1505 config.add(
"power.element",
1513 "INDI power state element name. Default is \"state\", only need to specify if different." );
1515 config.add(
"power.targetElement",
1517 "power.targetElement",
1523 "INDI power target element name. Default is \"target\", only need to specify if different." );
1525 config.add(
"power.powerOnWait",
1527 "power.powerOnWait",
1533 "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec." );
1538template <
bool _useINDI>
1543 config( ig,
"ignore_git" );
1545 if( !ig && m_gitAlert )
1547 m_stateAlert =
true;
1551 if(config.isSet(
"config.validate"))
1553 m_configOnly =
true;
1557 m_log.logName( m_configName );
1558 m_log.loadConfig( config );
1561 config( m_loopPause,
"loopPause" );
1564 if( m_powerMgtEnabled )
1566 config( m_powerDevice,
"power.device" );
1567 config( m_powerChannel,
"power.channel" );
1568 config( m_powerElement,
"power.element" );
1569 config( m_powerTargetElement,
"power.targetElement" );
1571 if( m_powerDevice !=
"" && m_powerChannel !=
"" )
1573 log<text_log>(
"enabling power management: " + m_powerDevice +
"." + m_powerChannel +
"." + m_powerElement +
1574 "/" + m_powerTargetElement );
1575 if( registerIndiPropertySet(
1576 m_indiP_powerChannel, m_powerDevice, m_powerChannel,
INDI_SETCALLBACK( m_indiP_powerChannel ) ) <
1579 log<software_error>( { __FILE__, __LINE__,
"failed to register set property" } );
1588 config( m_powerOnWait,
"power.powerOnWait" );
1589 if( m_powerOnWait > 3600 )
1591 log<text_log>(
"powerOnWait longer than 1 hour. Setting to 0.",
logPrio::LOG_ERROR );
1597template <
bool _useINDI>
1602 for(
auto it = config.m_targets.begin();
it != config.m_targets.end(); ++
it )
1604 if(
it->second.used ==
false )
1606 std::string
msg =
it->second.name;
1607 if( config.m_sources &&
it->second.sources.size() > 0 )
1609 msg +=
" [" +
it->second.sources[0] +
"]";
1617 if( config.m_unusedConfigs.size() > 0 )
1619 for(
auto it = config.m_unusedConfigs.begin();
it != config.m_unusedConfigs.end(); ++
it )
1621 if(
it->second.used ==
true )
1626 std::string
msg =
it->second.name;
1627 if( config.m_sources &&
it->second.sources.size() > 0 )
1629 msg +=
" [" +
it->second.sources[0] +
"]";
1638 if( config.nonOptions.size() > 0 )
1640 for(
size_t n = 0; n < config.nonOptions.size(); ++n )
1642 log<text_log>(
"Unrecognized command line argument: " + config.nonOptions[n],
logPrio::LOG_CRITICAL );
1649 if(m_shutdown ==
true)
1651 std::cerr <<
"\nThere were configuration errors.\n\n";
1655 std::cerr <<
"\nConfiguration is valid.\n\n";
1658 else if(m_shutdown ==
true)
1664template <
bool _useINDI>
1671 #ifndef XWC_DISABLE_USER_CHECK
1673 struct stat logstat;
1675 if( stat( m_log.logPath().c_str(), &logstat ) < 0 )
1677 state( stateCodes::FAILURE );
1678 std::cerr <<
"\nCRITICAL: Can not stat the log path.\n\n";
1683 #ifdef XWCTEST_MAGAOXAPP_EXEC_WRONG_USER
1684 logstat.st_uid = geteuid()+1;
1687 if( logstat.st_uid != geteuid() )
1689 state( stateCodes::FAILURE );
1690 std::cerr <<
"\nCRITICAL: You are running this app as the wrong user.\n\n";
1697 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1698 int testTimesThrough = 0;
1706 state( stateCodes::FAILURE );
1709 std::cerr <<
"\nCRITICAL: Failed to lock PID. Exiting.\n\n";
1718 m_log.logThreadStart();
1721 #ifdef XWCTEST_MAGAOXAPP_EXEC_LOG_START
1722 m_log.logShutdown(
true);
1727 while( m_log.logThreadRunning() ==
false && w < 20 )
1730 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( 100000000 ) );
1734 if( m_log.logThreadRunning() ==
false )
1736 state( stateCodes::FAILURE );
1739 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1743 if( unlockPID() < 0 )
1745 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1754 if( m_shutdown == 0 )
1756 if( setSigTermHandler() < 0 )
1758 state( stateCodes::FAILURE );
1760 log<software_critical>( { __FILE__, __LINE__,
"error from setSigTermHandler()" } );
1764 if( unlockPID() < 0 )
1766 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1776 if( m_shutdown == 0 )
1778 state( stateCodes::INITIALIZED );
1780 if( appStartup() < 0 )
1782 state( stateCodes::FAILURE );
1784 log<software_critical>( { __FILE__, __LINE__,
"error from appStartup()" } );
1788 if( unlockPID() < 0 )
1790 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1798 if( m_useINDI && m_shutdown == 0 )
1800 if( startINDI() < 0 )
1802 state( stateCodes::FAILURE );
1804 log<software_critical>( { __FILE__, __LINE__,
"INDI failed to start." } );
1809 if( appShutdown() < 0 )
1811 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1814 if( unlockPID() < 0 )
1816 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1824 if( m_powerMgtEnabled && m_shutdown == 0 )
1827 while( m_powerState < 0 && !m_shutdown )
1830 if( m_powerState < 0 )
1832 if( !stateLogged() )
1833 log<text_log>(
"waiting for power state" );
1840 state( stateCodes::ERROR );
1844 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1849 if( m_powerState > 0 )
1851 state( stateCodes::POWERON );
1855 m_powerOnCounter = 0;
1856 state( stateCodes::POWEROFF );
1857 if( onPowerOff() < 0 )
1859 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1874 while( m_shutdown == 0 )
1877 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1878 if(testTimesThrough > 1)
1885 if( m_powerMgtEnabled )
1887 if( state() == stateCodes::POWEROFF )
1889 if( m_powerState == 1 )
1891 m_powerOnCounter = 0;
1892 state( stateCodes::POWERON );
1897 if( m_powerState == 0 )
1899 state( stateCodes::POWEROFF );
1900 if( onPowerOff() < 0 )
1902 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1912 if( !m_powerMgtEnabled || m_powerState > 0 )
1914 if( appLogic() < 0 )
1916 log<software_error>( { __FILE__, __LINE__,
"error from appLogic()" } );
1921 else if( m_powerState == 0 )
1923 if( whilePowerOff() < 0 )
1925 log<software_error>( { __FILE__, __LINE__,
"error from whilePowerOff()" } );
1931 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1945 sendGetPropertySetList(
false );
1953 if( m_shutdown == 0 )
1955 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( m_loopPause ) );
1959 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1964 if( appShutdown() < 0 )
1966 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1969 state( stateCodes::SHUTDOWN );
1972 if( m_indiDriver !=
nullptr )
1974 pcf::IndiProperty ipSend;
1975 ipSend.setDevice( m_configName );
1978 m_indiDriver->sendDelProperty( ipSend );
1980 catch(
const std::exception &e )
1982 log<software_error>(
1983 { __FILE__, __LINE__, std::string(
"exception caught from sendDelProperty: " ) + e.what() } );
1986 m_indiDriver->quitProcess();
1987 m_indiDriver->deactivate();
1988 log<indidriver_stop>();
1991 if( unlockPID() < 0 )
1993 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
2000template <
bool _useINDI>
2001template <
typename logT,
int retval>
2004 m_log.template log<logT>(
msg, level );
2008template <
bool _useINDI>
2009template <
typename logT,
int retval>
2012 m_log.template log<logT>( level );
2016template <
bool _useINDI>
2027 state( m_state,
true );
2030 if( _useINDI && m_indiDriver )
2032 pcf::IndiProperty
msg;
2033 msg.setDevice( m_configName );
2035 std::stringstream logstdf;
2038 msg.setMessage( logstdf.str() );
2044 tv.tv_usec = (
long int)( ( (
double)ts.
time_ns ) / 1e3 );
2046 msg.setTimeStamp( pcf::TimeStamp( tv ) );
2050 m_indiDriver->sendMessage(
msg );
2052 catch(
const std::exception &e )
2054 log<software_error>(
2055 { __FILE__, __LINE__, std::string(
"exception caught from sendMessage: " ) + e.what() } );
2060template <
bool _useINDI>
2063 const std::string &value,
2064 const std::string &source )
2066 m_log.template log<config_log>( { name, code, value, source } );
2069template <
bool _useINDI>
2073 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_ERR
2077 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGTERM
2079 #define SIGTERM SIGKILL
2082 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGQUIT
2084 #define SIGQUIT SIGKILL
2087 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGINT
2089 #define SIGINT SIGKILL
2094 struct sigaction act;
2098 act.sa_flags = SA_SIGINFO;
2099 sigemptyset( &set );
2103 if( sigaction( SIGTERM, &act, 0 ) < 0 )
2105 std::string logss =
"Setting handler for SIGTERM failed. Errno says: ";
2106 logss += strerror( errno );
2108 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2114 if( sigaction( SIGQUIT, &act, 0 ) < 0 )
2116 std::string logss =
"Setting handler for SIGQUIT failed. Errno says: ";
2117 logss += strerror( errno );
2119 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2125 if( sigaction( SIGINT, &act, 0 ) < 0 )
2127 std::string logss =
"Setting handler for SIGINT failed. Errno says: ";
2128 logss += strerror( errno );
2130 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2135 log<text_log>(
"Installed SIGTERM/SIGQUIT/SIGINT signal handler.",
logPrio::LOG_DEBUG );
2140template <
bool _useINDI>
2146template <
bool _useINDI>
2148 siginfo_t *siginf __attribute__( ( unused ) ),
2149 void *ucont __attribute__( ( unused ) ) )
2153 std::string signame;
2157 signame =
"SIGTERM";
2163 signame =
"SIGQUIT";
2169 std::string logss =
"Caught signal ";
2171 logss +=
". Shutting down.";
2173 std::cerr <<
"\n" << logss << std::endl;
2174 log<text_log>( logss );
2178void sigUsr1Handler(
int signum, siginfo_t *siginf,
void *ucont );
2180template <
bool _useINDI>
2184 if( sys::th_seteuid( m_euidCalled ) < 0 )
2186 log<software_error>( { __FILE__, __LINE__, errno, 0, std::format(
"Setting effective user id to "
2187 "euidCalled ({}) failed. "
2190 strerror( errno )) } );
2198template <
bool _useINDI>
2202 if( sys::th_seteuid( m_euidReal ) < 0 )
2204 log<software_error>( { __FILE__, __LINE__, errno, 0, std::format(
"Setting effective user id to "
2205 "euidReal ({}) failed. "
2208 strerror( errno )) } );
2216template <
bool _useINDI>
2221 std::string statusDir = m_sysPath;
2229 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2231 if( errno != EEXIST )
2233 return log<
software_critical, -1>( { __FILE__, __LINE__, errno, 0,
"Failed to create root of statusDir (" + statusDir +
"). "
2234 "Errno says: " + strerror( errno ) } );
2239 statusDir += m_configName;
2241 pidFileName = statusDir +
"/pid";
2246 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2248 if( errno != EEXIST )
2251 "Failed to create statusDir (" + statusDir +
"). "
2252 "Errno says: " + strerror( errno )} );
2257 std::ifstream pidIn;
2258 pidIn.open( pidFileName );
2268 std::stringstream procN;
2269 procN <<
"/proc/" << testPid <<
"/cmdline";
2271 std::ifstream procIn;
2272 std::string pidCmdLine;
2276 procIn.open( procN.str() );
2279 procIn >> pidCmdLine;
2285 log<
software_critical, -1>( { __FILE__, __LINE__, 0, 0,
"exception caught testing /proc/pid" } );
2292 size_t invokedPos = pidCmdLine.find( invokedName );
2295 size_t configPos = std::string::npos;
2296 if( invokedPos != std::string::npos )
2298 configPos = pidCmdLine.find( m_configName );
2302 #ifdef XWCTEST_MAGAOXAPP_PID_LOCKED
2309 if( invokedPos != std::string::npos && configPos != std::string::npos )
2312 std::cerr <<
"PID already locked (" + std::to_string(testPid) +
"). Time to die." << std::endl;
2314 return log<
text_log, -1>(
"PID already locked (" + std::to_string(testPid) +
"). Time to die." );
2325 std::ofstream pidOut;
2326 pidOut.open( pidFileName );
2329 #ifdef XWCTEST_MAGAOXAPP_PID_WRITE_FAIL
2334 if( !(pidOut << m_pid) )
2336 return log<
software_critical, -1>( { __FILE__, __LINE__, errno, 0,
"failed to write to pid file." } );
2341 return log<text_log, 0>(
"PID (" + std::to_string(m_pid) +
") locked." );
2344template <
bool _useINDI>
2348 #ifdef XWCTEST_MAGAOXAPP_PID_UNLOCK_ERR
2357 if( ::remove( pidFileName.c_str() ) < 0 )
2359 log<software_error>(
2360 { __FILE__, __LINE__, errno, 0, std::string(
"Failed to remove PID file: " ) + strerror( errno ) } );
2365 return log<text_log, 0>(
"PID (" + std::to_string(m_pid) +
") unlocked." );
2369template <
bool _useINDI>
2370template <
class thisPtr,
class Function>
2374 pcf::IndiProperty &thProp,
2376 const std::string &cpuset,
2377 const std::string &thrdName,
2379 Function &&thrdStart )
2387 thrd = std::thread( thrdStart, thrdThis );
2389 catch(
const std::exception &e )
2391 log<software_error>(
2392 { __FILE__, __LINE__, std::string(
"Exception on " + thrdName +
" thread start: " ) + e.what() } );
2397 log<software_error>( { __FILE__, __LINE__,
"Unkown exception on " + thrdName +
" thread start" } );
2401 if( !thrd.joinable() )
2403 log<software_error>( { __FILE__, __LINE__, thrdName +
" thread did not start" } );
2415 sp.sched_priority = thrdPrio;
2429 rv = pthread_setschedparam( thrd.native_handle(), SCHED_OTHER, &sp );
2434 log<software_error>(
2438 "Setting " + thrdName +
" thread scheduler priority to " + std::to_string( thrdPrio ) +
" failed." } );
2442 log<text_log>( thrdName +
" thread scheduler priority set to " + std::to_string( thrdPrio ) );
2448 for(
int i = 0; i < 10; ++i )
2450 mx::sys::milliSleep( 100 );
2458 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"tpid for " + thrdName +
" not set." } );
2462 log<text_log>( thrdName +
" thread pid is " + std::to_string( tpid ) );
2466 thProp = pcf::IndiProperty( pcf::IndiProperty::Number );
2467 thProp.setDevice( configName() );
2468 thProp.setName( std::string(
"th-" ) + thrdName );
2469 thProp.setPerm( pcf::IndiProperty::ReadOnly );
2470 thProp.setState( pcf::IndiProperty::Idle );
2471 thProp.add( pcf::IndiElement(
"pid" ) );
2472 thProp[
"pid"] = tpid;
2473 thProp.add( pcf::IndiElement(
"prio" ) );
2474 thProp[
"prio"] = thrdPrio;
2475 registerIndiPropertyReadOnly( thProp );
2481 std::string cpuFile = m_cpusetPath;
2482 cpuFile +=
"/" + cpuset;
2483 cpuFile +=
"/tasks";
2484 int wfd = open( cpuFile.c_str(), O_WRONLY );
2487 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error from open for " + cpuFile } );
2491 snprintf( pids,
sizeof( pids ),
"%d", tpid );
2493 int w = write( wfd, pids, strnlen( pids,
sizeof( pids ) ) );
2494 if( w != (
int)strnlen( pids,
sizeof(pids) ) )
2496 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error on write" } );
2510template <
bool _useINDI>
2516template <
bool _useINDI>
2523 if( s == stateCodes::ERROR )
2525 if( s == stateCodes::FAILURE )
2528 log<state_change>( { m_state, s }, lvl );
2534 if( m_stateAlert != stateAlert && stateAlert ==
true )
2536 m_stateAlert = stateAlert;
2541 std::unique_lock<std::mutex>
lock( m_indiMutex,
2545 if(
lock.owns_lock() )
2548 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2551 if( m_stateAlert ==
true )
2557 if( m_state == stateCodes::READY )
2559 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING ||
2560 m_state == stateCodes::CONFIGURING )
2562 else if( m_state < stateCodes::NODEVICE )
2564 else if( m_state <= stateCodes::LOGGEDIN )
2566 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2570 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2574template <
bool _useINDI>
2577 return m_stateAlert;
2580template <
bool _useINDI>
2586template <
bool _useINDI>
2589 if( m_stateLogged > 0 )
2592 return m_stateLogged - 1;
2601template <
bool _useINDI>
2604 if( m_stateAlert ==
false )
2609 m_stateAlert =
false;
2613 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2615 if( m_state == stateCodes::READY )
2619 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING || m_state == stateCodes::CONFIGURING )
2623 else if( m_state < stateCodes::NODEVICE )
2627 else if( m_state <= stateCodes::LOGGEDIN )
2631 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2636 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2645template <
bool _useINDI>
2647 const std::string &propName,
2648 const std::string &label,
2649 const std::string &group )
2651 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2652 prop.setDevice( configName() );
2653 prop.setName( propName );
2654 prop.setPerm( pcf::IndiProperty::ReadWrite );
2655 prop.setState( pcf::IndiProperty::Idle );
2656 prop.add( pcf::IndiElement(
"current" ) );
2657 prop.add( pcf::IndiElement(
"target" ) );
2662 prop.setLabel( label );
2667 prop.setGroup( group );
2673template <
bool _useINDI>
2675 const std::string &propName,
2676 const std::string &elName,
2677 const std::string &propLabel,
2678 const std::string &propGroup,
2679 const std::string &elLabel )
2681 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2682 prop.setDevice( configName() );
2683 prop.setName( propName );
2684 prop.setPerm( pcf::IndiProperty::ReadOnly );
2685 prop.setState( pcf::IndiProperty::Idle );
2688 if( propLabel !=
"" )
2690 prop.setLabel( propLabel );
2693 if( propGroup !=
"" )
2695 prop.setGroup( propGroup );
2698 prop.add( pcf::IndiElement( elName ) );
2702 prop[elName].setLabel( elLabel );
2708template <
bool _useINDI>
2709template <
typename T>
2711 const std::string &name,
2715 const std::string &format,
2716 const std::string &label,
2717 const std::string &group )
2719 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2720 prop.setDevice( configName() );
2721 prop.setName( name );
2722 prop.setPerm( pcf::IndiProperty::ReadWrite );
2723 prop.setState( pcf::IndiProperty::Idle );
2724 prop.add( pcf::IndiElement(
"current" ) );
2725 prop[
"current"].setMin( min );
2726 prop[
"current"].setMax( max );
2727 prop[
"current"].setStep( step );
2730 prop[
"current"].setFormat( format );
2733 prop.add( pcf::IndiElement(
"target" ) );
2734 prop[
"target"].setMin( min );
2735 prop[
"target"].setMax( max );
2736 prop[
"target"].setStep( step );
2739 prop[
"target"].setFormat( format );
2745 prop.setLabel( label );
2750 prop.setGroup( group );
2756template <
bool _useINDI>
2758 const std::string &propName,
2759 const std::string &propLabel,
2760 const std::string &propGroup )
2762 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2763 prop.setDevice( configName() );
2764 prop.setName( propName );
2765 prop.setPerm( pcf::IndiProperty::ReadOnly );
2766 prop.setState( pcf::IndiProperty::Idle );
2769 if( propLabel !=
"" )
2771 prop.setLabel( propLabel );
2774 if( propGroup !=
"" )
2776 prop.setGroup( propGroup );
2782template <
bool _useINDI>
2784 const std::string &name,
2785 const std::string &label,
2786 const std::string &group )
2788 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2789 prop.setDevice( configName() );
2790 prop.setName( name );
2791 prop.setPerm( pcf::IndiProperty::ReadWrite );
2792 prop.setState( pcf::IndiProperty::Idle );
2793 prop.setRule( pcf::IndiProperty::AtMostOne );
2796 prop.add( pcf::IndiElement(
"toggle", pcf::IndiElement::Off ) );
2801 prop.setLabel( label );
2806 prop.setGroup( group );
2812template <
bool _useINDI>
2814 const std::string &name,
2815 const std::string &label,
2816 const std::string &group )
2818 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2819 prop.setDevice( configName() );
2820 prop.setName( name );
2821 prop.setPerm( pcf::IndiProperty::ReadWrite );
2822 prop.setState( pcf::IndiProperty::Idle );
2823 prop.setRule( pcf::IndiProperty::AtMostOne );
2826 prop.add( pcf::IndiElement(
"request", pcf::IndiElement::Off ) );
2831 prop.setLabel( label );
2836 prop.setGroup( group );
2842template <
bool _useINDI>
2844 const std::string &name,
2845 const std::vector<std::string> &elements,
2846 const std::vector<std::string> &elementLabels,
2847 const std::string &label,
2848 const std::string &group )
2850 if( elements.size() == 0 )
2852 return log<
software_error, -1>( { __FILE__, __LINE__,
"elements vector has zero size" } );
2855 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2856 prop.setDevice( configName() );
2857 prop.setName( name );
2858 prop.setPerm( pcf::IndiProperty::ReadWrite );
2859 prop.setState( pcf::IndiProperty::Idle );
2860 prop.setRule( pcf::IndiProperty::OneOfMany );
2863 for(
size_t n = 0; n < elements.size(); ++n )
2865 pcf::IndiElement elem = pcf::IndiElement( elements[n], pcf::IndiElement::Off );
2866 if(elementLabels[n] !=
"")
2868 elem.setLabel( elementLabels[n] );
2876 prop.setLabel( label );
2881 prop.setGroup( group );
2887template <
bool _useINDI>
2889 const std::string &name,
2890 const std::vector<std::string> &elements,
2891 const std::string &label,
2892 const std::string &group )
2894 return createStandardIndiSelectionSw( prop, name, elements, elements, label, group );
2897template <
bool _useINDI>
2904 m_indiNewCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, nullptr } ) );
2908 if( !result.second )
2911 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2914 catch( std::exception &e )
2916 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2920 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2926template <
bool _useINDI>
2928 const std::string &propName,
2929 const pcf::IndiProperty::Type &propType,
2930 const pcf::IndiProperty::PropertyPermType &propPerm,
2931 const pcf::IndiProperty::PropertyStateType &propState )
2936 prop = pcf::IndiProperty( propType );
2937 prop.setDevice( m_configName );
2938 prop.setName( propName );
2939 prop.setPerm( propPerm );
2940 prop.setState( propState );
2946 if( !result.second )
2949 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2952 catch( std::exception &e )
2954 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2958 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2963template <
bool _useINDI>
2965 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
2972 callBackInsertResult result =
2973 m_indiNewCallBacks.insert( callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
2975 if( !result.second )
2978 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2981 catch( std::exception &e )
2983 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2987 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2993template <
bool _useINDI>
2994int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
2995 const std::string &propName,
2996 const pcf::IndiProperty::Type &propType,
2997 const pcf::IndiProperty::PropertyPermType &propPerm,
2998 const pcf::IndiProperty::PropertyStateType &propState,
2999 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3004 prop = pcf::IndiProperty( propType );
3005 prop.setDevice( m_configName );
3006 prop.setName( propName );
3007 prop.setPerm( propPerm );
3008 prop.setState( propState );
3010 return registerIndiPropertyNew( prop, callBack );
3013template <
bool _useINDI>
3014int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
3015 const std::string &propName,
3016 const pcf::IndiProperty::Type &propType,
3017 const pcf::IndiProperty::PropertyPermType &propPerm,
3018 const pcf::IndiProperty::PropertyStateType &propState,
3019 const pcf::IndiProperty::SwitchRuleType &propRule,
3020 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3025 prop = pcf::IndiProperty( propType );
3026 prop.setDevice( m_configName );
3027 prop.setName( propName );
3028 prop.setPerm( propPerm );
3029 prop.setState( propState );
3030 prop.setRule( propRule );
3031 return registerIndiPropertyNew( prop, callBack );
3034template <
bool _useINDI>
3036 const std::string &devName,
3037 const std::string &propName,
3038 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3043 prop = pcf::IndiProperty();
3044 prop.setDevice( devName );
3045 prop.setName( propName );
3048 m_indiSetCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
3052 if( !result.second )
3055 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3058 catch( std::exception &e )
3060 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3064 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3070template <
bool _useINDI>
3079 std::string driverFIFOPath = m_basePath;
3080 driverFIFOPath +=
"/";
3083 m_driverInName = driverFIFOPath +
"/" + configName() +
".in";
3084 m_driverOutName = driverFIFOPath +
"/" + configName() +
".out";
3085 m_driverCtrlName = driverFIFOPath +
"/" + configName() +
".ctrl";
3091 mode_t prev = umask( 0 );
3094 if( mkfifo( m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3096 if( errno != EEXIST )
3099 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3106 if( mkfifo( m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3108 if( errno != EEXIST )
3112 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3119 if( mkfifo( m_driverCtrlName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3121 if( errno != EEXIST )
3125 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3136template <
bool _useINDI>
3143 if( createINDIFIFOS() < 0 )
3151 if( m_indiDriver !=
nullptr )
3153 m_indiDriver->quitProcess();
3154 m_indiDriver->deactivate();
3155 log<indidriver_stop>();
3156 delete m_indiDriver;
3157 m_indiDriver =
nullptr;
3164 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction exception." } );
3169 if( m_indiDriver ==
nullptr )
3171 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction failed." } );
3176 if( m_indiDriver->good() ==
false )
3178 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver failed to open FIFOs." } );
3179 delete m_indiDriver;
3180 m_indiDriver =
nullptr;
3185 m_indiDriver->activate();
3186 log<indidriver_start>();
3188 sendGetPropertySetList(
true );
3193template <
bool _useINDI>
3197 if( !all && m_allDefsReceived )
3203 while(
it != m_indiSetCallBacks.end() )
3205 if( all ||
it->second.m_defReceived ==
false )
3207 if(
it->second.property )
3211 m_indiDriver->sendGetProperties( *(
it->second.property ) );
3213 catch(
const std::exception &e )
3215 log<software_error>( { __FILE__,
3217 "exception caught from sendGetProperties for " +
3218 it->second.property->getName() +
": " + e.what() } );
3222 it->second.m_defReceived =
false;
3228 m_allDefsReceived =
false;
3230 m_allDefsReceived =
true;
3233template <
bool _useINDI>
3236 handleSetProperty(
ipRecv );
3239template <
bool _useINDI>
3244 if( m_indiDriver ==
nullptr )
3248 if(
ipRecv.hasValidDevice() &&
ipRecv.getDevice() != m_indiDriver->getName() )
3254 if( !
ipRecv.hasValidName() )
3258 while(
it != m_indiNewCallBacks.end() )
3260 if(
it->second.property )
3264 m_indiDriver->sendDefProperty( *(
it->second.property ) );
3266 catch(
const std::exception &e )
3268 log<software_error>( { __FILE__,
3270 "exception caught from sendDefProperty for " +
3271 it->second.property->getName() +
": " + e.what() } );
3278 sendGetPropertySetList(
true );
3284 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3290 if( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property )
3294 m_indiDriver->sendDefProperty( *( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property ) );
3296 catch(
const std::exception &e )
3298 log<software_error>( { __FILE__,
3300 "exception caught from sendDefProperty for " +
3301 m_indiNewCallBacks[
ipRecv.createUniqueKey()].property->getName() +
": " +
3308template <
bool _useINDI>
3313 if( m_indiDriver ==
nullptr )
3317 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3319 log<software_debug>( { __FILE__, __LINE__,
"invalid NewProperty request for " +
ipRecv.createUniqueKey() } );
3323 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiNewCallBacks[
ipRecv.createUniqueKey()].callBack;
3326 callBack(
this,
ipRecv );
3328 log<software_debug>( { __FILE__, __LINE__,
"NewProperty callback null for " +
ipRecv.createUniqueKey() } );
3333template <
bool _useINDI>
3338 if( m_indiDriver ==
nullptr )
3341 std::string key =
ipRecv.createUniqueKey();
3344 if( m_indiSetCallBacks.count( key ) > 0 )
3346 m_indiSetCallBacks[key].m_defReceived =
true;
3349 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiSetCallBacks[key].callBack;
3351 callBack(
this,
ipRecv );
3363template <
bool _useINDI>
3364template <
typename T>
3366 const std::string &el,
3368 pcf::IndiProperty::PropertyStateType ipState )
3376 indi::updateIfChanged( p, el, newVal, m_indiDriver, ipState );
3379template <
bool _useINDI>
3381 const std::string &el,
3383 pcf::IndiProperty::PropertyStateType ipState )
3385 updateIfChanged<std::string>( p, el, std::string( newVal ), ipState );
3388template <
bool _useINDI>
3390 const std::string &el,
3391 const pcf::IndiElement::SwitchStateType &newVal,
3392 pcf::IndiProperty::PropertyStateType ipState )
3400 indi::updateSwitchIfChanged( p, el, newVal, m_indiDriver, ipState );
3403template <
bool _useINDI>
3404template <
typename T>
3406 const std::string &el,
3407 const std::vector<T> &newVals,
3408 pcf::IndiProperty::PropertyStateType ipState )
3416 std::vector<std::string> descriptors( newVals.size(), el );
3417 for(
size_t index = 0; index < newVals.size(); ++index )
3419 descriptors[index] += std::to_string( index );
3421 indi::updateIfChanged( p, descriptors, newVals, m_indiDriver );
3424template <
bool _useINDI>
3425template <
typename T>
3427 const std::vector<std::string> &els,
3428 const std::vector<T> &newVals,
3429 pcf::IndiProperty::PropertyStateType newState )
3437 indi::updateIfChanged( p, els, newVals, m_indiDriver, newState );
3440template <
bool _useINDI>
3441template <
typename T>
3443 const std::vector<const char *> &els,
3444 const std::vector<T> &newVals,
3445 pcf::IndiProperty::PropertyStateType newState )
3457 indi::updatesIfChanged( p, els, newVals, m_indiDriver, newState );
3460template <
bool _useINDI>
3461template <
typename T>
3464 const pcf::IndiProperty &remoteProperty,
3467 if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey() )
3472 if( !( remoteProperty.find(
"target" ) || remoteProperty.find(
"current" ) ) )
3479 if( remoteProperty.find(
"target" ) )
3481 localTarget = remoteProperty[
"target"].get<T>();
3487 if( remoteProperty.find(
"current" ) )
3489 localTarget = remoteProperty[
"current"].get<T>();
3501 updateIfChanged( localProperty,
"target", localTarget,
INDI_BUSY );
3505 updateIfChanged( localProperty,
"target", localTarget );
3511template <
bool _useINDI>
3512template <
typename T>
3522 log<software_error>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3525 pcf::IndiProperty ipToSend = ipSend;
3529 ipToSend[el].setValue( newVal );
3533 log<software_error>(
3534 { __FILE__, __LINE__,
"Exception caught setting " + ipSend.createUniqueKey() +
"." + el } );
3541 log<software_error>( { __FILE__, __LINE__ } );
3548template <
bool _useINDI>
3558 return log<
software_error, -1>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3561 if( m_indiDriver->sendNewProperty( ipSend ) < 0 )
3569template <
bool _useINDI>
3575 pcf::IndiProperty ipSend( pcf::IndiProperty::Switch );
3579 ipSend.setDevice( device );
3580 ipSend.setName( property );
3581 ipSend.add( pcf::IndiElement(
"toggle" ) );
3583 catch( std::exception &e )
3585 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"exception: " ) + e.what() } );
3588 if( onoff ==
false )
3590 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::Off );
3594 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::On );
3597 if( sendNewProperty( ipSend ) < 0 )
3600 { __FILE__, __LINE__,
"sendNewProperty failed for " + device +
"." +
property } );
3606template <
bool _useINDI>
3612template <
bool _useINDI>
3616 if(
ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey() )
3618 return log<
software_error, -1>( { __FILE__, __LINE__,
"wrong indi property received" } );
3621 if(
ipRecv.find(
"request" ) )
3623 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
3626 updateSwitchIfChanged( m_indiP_clearFSMAlert,
"request", pcf::IndiElement::Off,
INDI_IDLE );
3633template <
bool _useINDI>
3639template <
bool _useINDI>
3645template <
bool _useINDI>
3648 if( !m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0 )
3653 if( m_powerOnCounter * m_loopPause > ( (
double)m_powerOnWait ) * 1e9 )
3664template <
bool _useINDI>
3667 if( !m_powerMgtEnabled )
3672 return m_powerState;
3675template <
bool _useINDI>
3678 if( !m_powerMgtEnabled )
3683 return m_powerTargetState;
3686template <
bool _useINDI>
3694 ps =
ipRecv[m_powerElement].get<std::string>();
3700 else if( ps ==
"Off" )
3712 ps =
ipRecv[m_powerTargetElement].get<std::string>();
3716 m_powerTargetState = 1;
3718 else if( ps ==
"Off" )
3720 m_powerTargetState = 0;
3724 m_powerTargetState = -1;
3731template <
bool _useINDI>
3737template <
bool _useINDI>
3740 return m_configName;
3743template <
bool _useINDI>
3749template <
bool _useINDI>
3752 return m_configBase;
3755template <
bool _useINDI>
3761template <
bool _useINDI>
3767template <
bool _useINDI>
3770 return m_secretsPath;
3773template <
bool _useINDI>
3776 return m_cpusetPath;
3779template <
bool _useINDI>
3785template <
bool _useINDI>
3791template <
bool _useINDI>
3794 return m_driverInName;
3797template <
bool _useINDI>
3800 return m_driverOutName;
3803template <
bool _useINDI>
3806 return m_driverCtrlName;
3809#ifdef XWCTEST_NAMESPACE
3814extern template class MagAOXApp<true>;
3815extern template class MagAOXApp<false>;
3835#define XWCAPP_THREAD_START( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart ) \
3836 if( threadStart( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, this, thrdStart ) < 0 ) \
3838 log<software_error>( { __FILE__, __LINE__, "error from threadStart for " #thrdName } ); \
3849#define XWCAPP_THREAD_CHECK( thrdSt, thrdName ) \
3852 if( pthread_tryjoin_np( thrdSt.native_handle(), 0 ) == 0 ) \
3854 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3860 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3869#define XWCAPP_THREAD_STOP( thrdSt ) \
3870 if( thrdSt.joinable() ) \
3872 pthread_kill( thrdSt.native_handle(), SIGUSR1 ); \
#define XWCTEST_NAMESPACE
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.