7#ifndef app_MagAOXApp_hpp
8#define app_MagAOXApp_hpp
12#include <sys/syscall.h>
21#include <unordered_map>
23#include <mx/mxlib.hpp>
24#include <mx/app/application.hpp>
25#include <mx/sys/environment.hpp>
26#include <mx/sys/timeUtils.hpp>
27#include <mx/ioutils/fileUtils.hpp>
29#include "../common/environment.hpp"
30#include "../common/paths.hpp"
31#include "../common/defaults.hpp"
32#include "../common/config.hpp"
34#include "../logger/logFileRaw.hpp"
35#include "../logger/logManager.hpp"
37#include "../sys/thSetuid.hpp"
44using namespace mx::app;
53namespace MagAOXAppTest
56#ifdef XWCTEST_NAMESPACE
63#ifdef XWCTEST_NAMESPACE
75#ifdef XWCTEST_NAMESPACE
170template <
bool _useINDI = true>
174 #ifdef XWCTEST_NAMESPACE
175 friend struct libXWCTest::appTest::MagAOXAppTest::XWCTEST_NAMESPACE::MagAOXApp_test;
237 const bool git_modified
250 virtual
void setDefaults(
int argc,
262 virtual
void setupBasicConfig();
272 virtual
void loadBasicConfig();
278 virtual
void checkConfig();
308 virtual
int execute();
321 virtual
int appStartup() = 0;
332 virtual
int appLogic() = 0;
337 virtual
int appShutdown() = 0;
354 template <typename logT,
int retval = 0>
355 static
int log( const typename logT::messageT &msg,
356 logPrioT level = logPrio::LOG_DEFAULT
367 template <typename logT,
int retval = 0>
368 static
int log(
logPrioT level = logPrio::LOG_DEFAULT
384 static
void configLog( const std::
string &name,
386 const std::
string &value,
387 const std::
string &source
401 int setSigTermHandler();
404 static
void _handlerSigTerm(
int signum,
410 void handlerSigTerm(
int signum,
435 bool m_elevated{
false };
555 template <
class thisPtr,
class Function>
560 pcf::IndiProperty &thProp,
562 const std::string &cpuset,
563 const std::string &thrdName,
579 bool m_stateAlert{
false };
582 bool m_gitAlert{
false };
584 int m_stateLogged{ 0 };
600 bool stateAlert =
false
654 constexpr static bool m_useINDI = _useINDI;
675 pcf::IndiProperty *
property{ 0 };
676 int ( *callBack )(
void *,
const pcf::IndiProperty & ){ 0 };
679 bool m_defReceived{
false };
682 uint32_t m_retryCount{ 0 };
684 std::chrono::steady_clock::duration m_retryDelay{
685 std::chrono::steady_clock::duration::zero() };
687 std::chrono::steady_clock::time_point m_nextRetry{
688 std::chrono::steady_clock::time_point::min() };
690 bool m_missingLogged{
false };
716 bool m_allDefsReceived{
false };
736 const std::string &propName,
737 const std::string &label =
"",
739 const std::string &group =
""
749 const std::string &propName,
750 const std::string &elName,
751 const std::string &propLabel =
"",
752 const std::string &propGroup =
"",
753 const std::string &elLabel =
""
761 template <
typename T>
763 const std::string &name,
770 const std::string &format,
773 const std::string &label =
"",
775 const std::string &group =
""
784 const std::string &propName,
785 const std::string &propLabel =
"",
787 const std::string &propGroup =
""
797 const std::string &name,
798 const std::string &label =
"",
800 const std::string &group =
""
810 const std::string &name,
811 const std::string &label =
"",
813 const std::string &group =
""
823 const std::string &name,
824 const std::vector<std::string> &elements,
826 const std::vector<std::string> &elementLabels,
828 const std::string &label =
"",
830 const std::string &group =
""
841 const std::string &name,
842 const std::vector<std::string> &elements,
844 const std::string &label =
"",
846 const std::string &group =
""
869 const std::string &propName,
871 const pcf::IndiProperty::Type &propType,
873 const pcf::IndiProperty::PropertyPermType &propPerm,
876 const pcf::IndiProperty::PropertyStateType &propState
889 int ( * )(
void *,
const pcf::IndiProperty & )
901 pcf::IndiProperty &prop,
902 const std::string &propName,
903 const pcf::IndiProperty::Type &propType,
904 const pcf::IndiProperty::PropertyPermType &propPerm,
905 const pcf::IndiProperty::PropertyStateType &propState,
906 int ( * )(
void *,
const pcf::IndiProperty & )
917 pcf::IndiProperty &prop,
918 const std::string &propName,
919 const pcf::IndiProperty::Type &propType,
920 const pcf::IndiProperty::PropertyPermType &propPerm,
921 const pcf::IndiProperty::PropertyStateType &propState,
922 const pcf::IndiProperty::SwitchRuleType &propRule,
923 int ( * )(
void *,
const pcf::IndiProperty & )
934 pcf::IndiProperty &prop,
935 const std::string &devName,
936 const std::string &propName,
937 int ( * )(
void *,
const pcf::IndiProperty & )
945 const indiCallBack &callBack,
947 const std::chrono::steady_clock::time_point &now
952 const std::chrono::steady_clock::time_point &now
1013 template <
typename T>
1015 const std::string &el,
1017 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
1032 const std::string &el,
1034 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
1043 const std::string &el,
1044 const pcf::IndiElement::SwitchStateType &newVal,
1045 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok );
1059 template <
typename T>
1061 pcf::IndiProperty &p,
1062 const std::string &el,
1063 const std::vector<T> &newVals,
1064 pcf::IndiProperty::PropertyStateType ipState = pcf::IndiProperty::Ok
1076 template <
typename T>
1078 const std::vector<std::string> &els,
1079 const std::vector<T> &newVals,
1080 pcf::IndiProperty::PropertyStateType newState =
1081 pcf::IndiProperty::Ok
1084 template <
typename T>
1086 const std::vector<const char *> &els,
1087 const std::vector<T> &newVals,
1088 pcf::IndiProperty::PropertyStateType newState =
1089 pcf::IndiProperty::Ok
1097 template <
typename T>
1100 const pcf::IndiProperty &remoteProperty,
1110 template <
typename T>
1112 const std::string &el,
1129 const std::string &property,
1146 const pcf::IndiProperty &ipRecv
1173 bool m_powerMgtEnabled{
false };
1180 std::string m_powerElement{
"state" };
1181 std::string m_powerTargetElement{
"target" };
1183 unsigned long m_powerOnWait{ 55 };
1186 int m_powerOnCounter{ -1 };
1190 int m_powerState{ -1 };
1191 int m_powerTargetState{ -1 };
1323template <
bool _useINDI>
1327template <
bool _useINDI>
1330template <
bool _useINDI>
1333 if( m_self !=
nullptr )
1335 throw std::logic_error(
"Attempt to instantiate 2nd MagAOXApp");
1341 getresuid( &m_euidReal, &m_euidCalled, &m_suid );
1344 m_log.parent(
this );
1347 config.m_sources =
true;
1348 config.configLog = configLog;
1360 if( MXLIB_UNCOMP_REPO_MODIFIED )
1366 log<git_state>(
git_state::messageT(
"mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED ), gl );
1369template <
bool _useINDI>
1373 delete m_indiDriver;
1374 m_log.parent(
nullptr );
1380template <
bool _useINDI>
1389 m_basePath = tmpstr;
1402 m_configDir = m_basePath +
"/" + tmpstr;
1403 m_configPathGlobal = m_configDir +
"/magaox.conf";
1411 m_calibDir = m_basePath +
"/" + tmpstr;
1419 m_log.logPath( m_basePath +
"/" + tmpstr );
1427 m_sysPath = m_basePath +
"/" + tmpstr;
1435 m_secretsPath = m_basePath +
"/" + tmpstr;
1441 m_cpusetPath = tmpstr;
1445 if( m_configBase !=
"" )
1448 m_configPathUser = m_configDir +
"/" + m_configBase +
".conf";
1460 "The name of the application and its device name in INDI (if used), specifies the config file in the XWC config directory." );
1462 config.parseCommandLine( argc, argv,
"name" );
1463 config( m_configName,
"name" );
1465 if( m_configName ==
"" )
1467 m_configName = mx::ioutils::pathStem( invokedName );
1470 log<text_log>(
"Configuration Error: Application name (-n --name) not set." );
1476 m_configPathLocal = m_configDir +
"/" + m_configName +
".conf";
1479 if( registerIndiPropertyNew(
1480 m_indiP_state,
"fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0 ) <
1483 log<software_error>( { __FILE__, __LINE__,
"failed to register read only fsm_state property" } );
1486 m_indiP_state.add( pcf::IndiElement(
"state" ) );
1488 createStandardIndiRequestSw( m_indiP_clearFSMAlert,
"fsm_clear_alert",
"Clear FSM Alert",
"FSM" );
1489 if( registerIndiPropertyNew( m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert ) < 0 )
1491 log<software_error>( { __FILE__, __LINE__,
"failed to register new fsm_alert property" } );
1497template <
bool _useINDI>
1501 config.add(
"config.validate",
1509 "Validate the configuration. App will exit after loading the configuration, but before "
1510 "entering the event loop. Errors from configuration processing will be shown. "
1511 "Always safe to run." );
1514 config.add(
"loopPause",
1522 "The main loop pause time in ns" );
1525 "ignore_git",
"",
"ignore-git", argType::True,
"",
"",
false,
"bool",
"set to true to ignore git "
1526 "status to prevent the fsm_alert" );
1529 m_log.setupConfig( config );
1531 if( m_powerMgtEnabled )
1533 if( _useINDI ==
false )
1536 log<software_critical>( { __FILE__, __LINE__,
"power management is enabled but we are not using INDI" } );
1541 config.add(
"power.device",
1549 "Device controlling power for this app's device (INDI name)." );
1551 config.add(
"power.channel",
1559 "Channel on device for this app's device (INDI name)." );
1561 config.add(
"power.element",
1569 "INDI power state element name. Default is \"state\", only need to specify if different." );
1571 config.add(
"power.targetElement",
1573 "power.targetElement",
1579 "INDI power target element name. Default is \"target\", only need to specify if different." );
1581 config.add(
"power.powerOnWait",
1583 "power.powerOnWait",
1589 "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec." );
1594template <
bool _useINDI>
1599 config( ig,
"ignore_git" );
1601 if( !ig && m_gitAlert )
1603 m_stateAlert =
true;
1607 if(config.isSet(
"config.validate"))
1609 m_configOnly =
true;
1613 m_log.logName( m_configName );
1614 m_log.loadConfig( config );
1617 config( m_loopPause,
"loopPause" );
1620 if( m_powerMgtEnabled )
1622 config( m_powerDevice,
"power.device" );
1623 config( m_powerChannel,
"power.channel" );
1624 config( m_powerElement,
"power.element" );
1625 config( m_powerTargetElement,
"power.targetElement" );
1627 if( m_powerDevice !=
"" && m_powerChannel !=
"" )
1629 log<text_log>(
"enabling power management: " + m_powerDevice +
"." + m_powerChannel +
"." + m_powerElement +
1630 "/" + m_powerTargetElement );
1632 if( registerIndiPropertySet(
1633 m_indiP_powerChannel,
1639 log<software_error>( { __FILE__, __LINE__,
"failed to register set property" } );
1648 config( m_powerOnWait,
"power.powerOnWait" );
1649 if( m_powerOnWait > 3600 )
1651 log<text_log>(
"powerOnWait longer than 1 hour. Setting to 0.",
logPrio::LOG_ERROR );
1657template <
bool _useINDI>
1662 for(
auto it = config.m_targets.begin();
it != config.m_targets.end(); ++
it )
1664 if(
it->second.used ==
false )
1666 std::string
msg =
it->second.name;
1667 if( config.m_sources &&
it->second.sources.size() > 0 )
1669 msg +=
" [" +
it->second.sources[0] +
"]";
1677 if( config.m_unusedConfigs.size() > 0 )
1679 for(
auto it = config.m_unusedConfigs.begin();
it != config.m_unusedConfigs.end(); ++
it )
1681 if(
it->second.used ==
true )
1686 std::string
msg =
it->second.name;
1687 if( config.m_sources &&
it->second.sources.size() > 0 )
1689 msg +=
" [" +
it->second.sources[0] +
"]";
1698 if( config.nonOptions.size() > 0 )
1700 for(
size_t n = 0; n < config.nonOptions.size(); ++n )
1702 log<text_log>(
"Unrecognized command line argument: " + config.nonOptions[n],
logPrio::LOG_CRITICAL );
1709 if(m_shutdown ==
true)
1711 std::cerr <<
"\nThere were configuration errors.\n\n";
1715 std::cerr <<
"\nConfiguration is valid.\n\n";
1718 else if(m_shutdown ==
true)
1724template <
bool _useINDI>
1731 #ifndef XWC_DISABLE_USER_CHECK
1733 struct stat logstat;
1735 if( stat( m_log.logPath().c_str(), &logstat ) < 0 )
1737 state( stateCodes::FAILURE );
1738 std::cerr <<
"\nCRITICAL: Can not stat the log path.\n\n";
1743 #ifdef XWCTEST_MAGAOXAPP_EXEC_WRONG_USER
1744 logstat.st_uid = geteuid()+1;
1747 if( logstat.st_uid != geteuid() )
1749 state( stateCodes::FAILURE );
1750 std::cerr <<
"\nCRITICAL: You are running this app as the wrong user.\n\n";
1757 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1758 int testTimesThrough = 0;
1766 state( stateCodes::FAILURE );
1769 std::cerr <<
"\nCRITICAL: Failed to lock PID. Exiting.\n\n";
1778 m_log.logThreadStart();
1781 #ifdef XWCTEST_MAGAOXAPP_EXEC_LOG_START
1782 m_log.logShutdown(
true);
1787 while( m_log.logThreadRunning() ==
false && w < 20 )
1790 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( 100000000 ) );
1794 if( m_log.logThreadRunning() ==
false )
1796 state( stateCodes::FAILURE );
1799 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1803 if( unlockPID() < 0 )
1805 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1814 if( m_shutdown == 0 )
1816 if( setSigTermHandler() < 0 )
1818 state( stateCodes::FAILURE );
1820 log<software_critical>( { __FILE__, __LINE__,
"error from setSigTermHandler()" } );
1824 if( unlockPID() < 0 )
1826 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1836 if( m_shutdown == 0 )
1838 state( stateCodes::INITIALIZED );
1840 if( appStartup() < 0 )
1842 state( stateCodes::FAILURE );
1844 log<software_critical>( { __FILE__, __LINE__,
"error from appStartup()" } );
1848 if( unlockPID() < 0 )
1850 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1858 if( m_useINDI && m_shutdown == 0 )
1860 if( startINDI() < 0 )
1862 state( stateCodes::FAILURE );
1864 log<software_critical>( { __FILE__, __LINE__,
"INDI failed to start." } );
1869 if( appShutdown() < 0 )
1871 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1874 if( unlockPID() < 0 )
1876 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1884 if( m_powerMgtEnabled && m_shutdown == 0 )
1887 while( m_powerState < 0 && !m_shutdown )
1890 if( m_powerState < 0 )
1892 if( !stateLogged() )
1894 log<text_log>(
"waiting for power state" );
1902 state( stateCodes::ERROR );
1907 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1912 if( m_powerState > 0 )
1914 state( stateCodes::POWERON );
1918 m_powerOnCounter = 0;
1919 state( stateCodes::POWEROFF );
1920 if( onPowerOff() < 0 )
1922 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1937 while( m_shutdown == 0 )
1940 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
1941 if(testTimesThrough > 1)
1948 if( m_log.logThreadRunning() ==
false )
1950 state( stateCodes::FAILURE );
1953 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1961 if( m_powerMgtEnabled )
1963 if( state() == stateCodes::POWEROFF )
1965 if( m_powerState == 1 )
1967 m_powerOnCounter = 0;
1968 state( stateCodes::POWERON );
1973 if( m_powerState == 0 )
1975 state( stateCodes::POWEROFF );
1976 if( onPowerOff() < 0 )
1978 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1988 if( !m_powerMgtEnabled || m_powerState > 0 )
1990 if( appLogic() < 0 )
1992 log<software_error>( { __FILE__, __LINE__,
"error from appLogic()" } );
1997 else if( m_powerState == 0 )
1999 if( whilePowerOff() < 0 )
2001 log<software_error>( { __FILE__, __LINE__,
"error from whilePowerOff()" } );
2007 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
2021 sendGetPropertySetList(
false );
2029 if( m_shutdown == 0 )
2031 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( m_loopPause ) );
2035 #ifdef XWCTEST_MAGAOXAPP_EXEC_NORM
2040 if( appShutdown() < 0 )
2042 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
2045 state( stateCodes::SHUTDOWN );
2048 if( m_indiDriver !=
nullptr )
2050 pcf::IndiProperty ipSend;
2051 ipSend.setDevice( m_configName );
2054 m_indiDriver->sendDelProperty( ipSend );
2056 catch(
const std::exception &e )
2058 log<software_error>( { __FILE__,
2060 std::string(
"exception caught from"
2061 " sendDelProperty: " ) +
2065 m_indiDriver->quitProcess();
2066 m_indiDriver->deactivate();
2067 log<indidriver_stop>();
2070 if( unlockPID() < 0 )
2072 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
2079template <
bool _useINDI>
2080template <
typename logT,
int retval>
2083 m_log.template log<logT>(
msg, level );
2087template <
bool _useINDI>
2088template <
typename logT,
int retval>
2091 m_log.template log<logT>(
typename logT::messageT(), level );
2095template <
bool _useINDI>
2106 state( m_state,
true );
2109 if( _useINDI && m_indiDriver )
2111 pcf::IndiProperty
msg;
2112 msg.setDevice( m_configName );
2114 std::stringstream logstdf;
2117 msg.setMessage( logstdf.str() );
2123 tv.tv_usec = (
long int)( ( (
double)ts.
time_ns ) / 1e3 );
2125 msg.setTimeStamp( pcf::TimeStamp( tv ) );
2129 m_indiDriver->sendMessage(
msg );
2131 catch(
const std::exception &e )
2133 log<software_error>( { std::string(
"exception caught from sendMessage: " ) + e.what() } );
2138template <
bool _useINDI>
2141 const std::string &value,
2142 const std::string &source )
2144 m_log.template log<config_log>( { name, code, value, source } );
2147template <
bool _useINDI>
2151 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_ERR
2155 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGTERM
2157 #define SIGTERM SIGKILL
2160 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGQUIT
2162 #define SIGQUIT SIGKILL
2165 #ifdef XWCTEST_MAGAOXAPP_SIGTERMH_SIGINT
2167 #define SIGINT SIGKILL
2172 struct sigaction act;
2176 act.sa_flags = SA_SIGINFO;
2177 sigemptyset( &set );
2181 if( sigaction( SIGTERM, &act, 0 ) < 0 )
2183 std::string logss =
"Setting handler for SIGTERM failed. Errno says: ";
2184 logss += strerror( errno );
2186 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2192 if( sigaction( SIGQUIT, &act, 0 ) < 0 )
2194 std::string logss =
"Setting handler for SIGQUIT failed. Errno says: ";
2195 logss += strerror( errno );
2197 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2203 if( sigaction( SIGINT, &act, 0 ) < 0 )
2205 std::string logss =
"Setting handler for SIGINT failed. Errno says: ";
2206 logss += strerror( errno );
2208 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2213 log<text_log>(
"Installed SIGTERM/SIGQUIT/SIGINT signal handler.",
logPrio::LOG_DEBUG );
2218template <
bool _useINDI>
2224template <
bool _useINDI>
2226 siginfo_t *siginf __attribute__( ( unused ) ),
2227 void *ucont __attribute__( ( unused ) ) )
2231 std::string signame;
2235 signame =
"SIGTERM";
2241 signame =
"SIGQUIT";
2247 std::string logss =
"Caught signal ";
2249 logss +=
". Shutting down.";
2251 std::cerr <<
"\n" << logss << std::endl;
2252 log<text_log>( logss );
2256void sigUsr1Handler(
int signum, siginfo_t *siginf,
void *ucont );
2258template <
bool _useINDI>
2262 if( sys::th_seteuid( m_euidCalled ) < 0 )
2264 log<software_error>( { errno,
2265 std::format(
"Setting effective user id to "
2266 "euidCalled ({}) failed. "
2269 strerror( errno ) ) } );
2276template <
bool _useINDI>
2280 if( sys::th_seteuid( m_euidReal ) < 0 )
2282 log<software_error>( { errno,
2283 std::format(
"Setting effective user id to "
2284 "euidReal ({}) failed. "
2287 strerror( errno ) ) } );
2295template <
bool _useINDI>
2300 std::string statusDir = m_sysPath;
2308 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2310 if( errno != EEXIST )
2316 "Failed to create root of statusDir (" + statusDir +
2319 strerror( errno ) } );
2324 statusDir += m_configName;
2326 pidFileName = statusDir +
"/pid";
2331 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2333 if( errno != EEXIST )
2339 "Failed to create statusDir (" + statusDir +
2342 strerror( errno ) } );
2347 std::ifstream pidIn;
2348 pidIn.open( pidFileName );
2358 std::stringstream procN;
2359 procN <<
"/proc/" << testPid <<
"/cmdline";
2361 std::ifstream procIn;
2362 std::string pidCmdLine;
2366 procIn.open( procN.str() );
2369 procIn >> pidCmdLine;
2375 log<
software_critical, -1>( { __FILE__, __LINE__, 0, 0,
"exception caught testing /proc/pid" } );
2382 size_t invokedPos = pidCmdLine.find( invokedName );
2385 size_t configPos = std::string::npos;
2386 if( invokedPos != std::string::npos )
2388 configPos = pidCmdLine.find( m_configName );
2392 #ifdef XWCTEST_MAGAOXAPP_PID_LOCKED
2399 if( invokedPos != std::string::npos && configPos != std::string::npos )
2402 std::cerr <<
"PID already locked (" + std::to_string( testPid ) +
"). Time to die." << std::endl;
2404 return log<
text_log, -1>(
"PID already locked (" + std::to_string( testPid ) +
"). Time to die." );
2415 std::ofstream pidOut;
2416 pidOut.open( pidFileName );
2419 #ifdef XWCTEST_MAGAOXAPP_PID_WRITE_FAIL
2424 if( !( pidOut << m_pid ) )
2426 return log<
software_critical, -1>( { __FILE__, __LINE__, errno, 0,
"failed to write to pid file." } );
2431 return log<text_log, 0>(
"PID (" + std::to_string( m_pid ) +
") locked." );
2434template <
bool _useINDI>
2438 #ifdef XWCTEST_MAGAOXAPP_PID_UNLOCK_ERR
2447 if( ::remove( pidFileName.c_str() ) < 0 )
2449 log<software_error>(
2450 { __FILE__, __LINE__, errno, 0, std::string(
"Failed to remove PID file: " ) + strerror( errno ) } );
2455 return log<text_log, 0>(
"PID (" + std::to_string(m_pid) +
") unlocked." );
2459template <
bool _useINDI>
2460template <
class thisPtr,
class Function>
2464 pcf::IndiProperty &thProp,
2466 const std::string &cpuset,
2467 const std::string &thrdName,
2469 Function &&thrdStart )
2477 thrd = std::thread( thrdStart, thrdThis );
2479 catch(
const std::exception &e )
2481 log<software_error>(
2482 { __FILE__, __LINE__, std::string(
"Exception on " + thrdName +
" thread start: " ) + e.what() } );
2487 log<software_error>( { __FILE__, __LINE__,
"Unkown exception on " + thrdName +
" thread start" } );
2491 if( !thrd.joinable() )
2493 log<software_error>( { __FILE__, __LINE__, thrdName +
" thread did not start" } );
2505 sp.sched_priority = thrdPrio;
2519 rv = pthread_setschedparam( thrd.native_handle(), SCHED_OTHER, &sp );
2524 log<software_error>(
2528 "Setting " + thrdName +
" thread scheduler priority to " + std::to_string( thrdPrio ) +
" failed." } );
2532 log<text_log>( thrdName +
" thread scheduler priority set to " + std::to_string( thrdPrio ) );
2538 for(
int i = 0; i < 10; ++i )
2540 mx::sys::milliSleep( 100 );
2548 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"tpid for " + thrdName +
" not set." } );
2552 log<text_log>( thrdName +
" thread pid is " + std::to_string( tpid ) );
2556 thProp = pcf::IndiProperty( pcf::IndiProperty::Number );
2557 thProp.setDevice( configName() );
2558 thProp.setName( std::string(
"th-" ) + thrdName );
2559 thProp.setPerm( pcf::IndiProperty::ReadOnly );
2560 thProp.setState( pcf::IndiProperty::Idle );
2561 thProp.add( pcf::IndiElement(
"pid" ) );
2562 thProp[
"pid"] = tpid;
2563 thProp.add( pcf::IndiElement(
"prio" ) );
2564 thProp[
"prio"] = thrdPrio;
2565 registerIndiPropertyReadOnly( thProp );
2571 std::string cpuFile = m_cpusetPath;
2572 cpuFile +=
"/" + cpuset;
2573 cpuFile +=
"/tasks";
2574 int wfd = open( cpuFile.c_str(), O_WRONLY );
2577 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error from open for " + cpuFile } );
2581 snprintf( pids,
sizeof( pids ),
"%d", tpid );
2583 int w = write( wfd, pids, strnlen( pids,
sizeof( pids ) ) );
2584 if( w != (
int)strnlen( pids,
sizeof(pids) ) )
2586 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error on write" } );
2600template <
bool _useINDI>
2606template <
bool _useINDI>
2613 if( s == stateCodes::ERROR )
2615 if( s == stateCodes::FAILURE )
2618 log<state_change>( { m_state, s }, lvl );
2624 if( m_stateAlert != stateAlert && stateAlert ==
true )
2626 m_stateAlert = stateAlert;
2631 std::unique_lock<std::mutex>
lock( m_indiMutex,
2635 if(
lock.owns_lock() )
2638 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2641 if( m_stateAlert ==
true )
2647 if( m_state == stateCodes::READY )
2649 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING ||
2650 m_state == stateCodes::CONFIGURING )
2652 else if( m_state < stateCodes::NODEVICE )
2654 else if( m_state <= stateCodes::LOGGEDIN )
2656 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2660 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2664template <
bool _useINDI>
2667 return m_stateAlert;
2670template <
bool _useINDI>
2676template <
bool _useINDI>
2679 if( m_stateLogged > 0 )
2682 return m_stateLogged - 1;
2691template <
bool _useINDI>
2694 if( m_stateAlert ==
false )
2699 m_stateAlert =
false;
2703 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2705 if( m_state == stateCodes::READY )
2709 else if( m_state == stateCodes::OPERATING || m_state == stateCodes::HOMING || m_state == stateCodes::CONFIGURING )
2713 else if( m_state < stateCodes::NODEVICE )
2717 else if( m_state <= stateCodes::LOGGEDIN )
2721 else if( m_state == stateCodes::NOTHOMED || m_state == stateCodes::SHUTDOWN )
2726 updateIfChanged( m_indiP_state,
"state", stateCodes::codeText( m_state ), stst );
2735template <
bool _useINDI>
2737 const std::string &propName,
2738 const std::string &label,
2739 const std::string &group )
2741 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2742 prop.setDevice( configName() );
2743 prop.setName( propName );
2744 prop.setPerm( pcf::IndiProperty::ReadWrite );
2745 prop.setState( pcf::IndiProperty::Idle );
2746 prop.add( pcf::IndiElement(
"current" ) );
2747 prop.add( pcf::IndiElement(
"target" ) );
2752 prop.setLabel( label );
2757 prop.setGroup( group );
2763template <
bool _useINDI>
2765 const std::string &propName,
2766 const std::string &elName,
2767 const std::string &propLabel,
2768 const std::string &propGroup,
2769 const std::string &elLabel )
2771 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2772 prop.setDevice( configName() );
2773 prop.setName( propName );
2774 prop.setPerm( pcf::IndiProperty::ReadOnly );
2775 prop.setState( pcf::IndiProperty::Idle );
2778 if( propLabel !=
"" )
2780 prop.setLabel( propLabel );
2783 if( propGroup !=
"" )
2785 prop.setGroup( propGroup );
2788 prop.add( pcf::IndiElement( elName ) );
2792 prop[elName].setLabel( elLabel );
2798template <
bool _useINDI>
2799template <
typename T>
2801 const std::string &name,
2805 const std::string &format,
2806 const std::string &label,
2807 const std::string &group )
2809 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2810 prop.setDevice( configName() );
2811 prop.setName( name );
2812 prop.setPerm( pcf::IndiProperty::ReadWrite );
2813 prop.setState( pcf::IndiProperty::Idle );
2814 prop.add( pcf::IndiElement(
"current" ) );
2815 prop[
"current"].setMin( min );
2816 prop[
"current"].setMax( max );
2817 prop[
"current"].setStep( step );
2820 prop[
"current"].setFormat( format );
2823 prop.add( pcf::IndiElement(
"target" ) );
2824 prop[
"target"].setMin( min );
2825 prop[
"target"].setMax( max );
2826 prop[
"target"].setStep( step );
2829 prop[
"target"].setFormat( format );
2835 prop.setLabel( label );
2840 prop.setGroup( group );
2846template <
bool _useINDI>
2848 const std::string &propName,
2849 const std::string &propLabel,
2850 const std::string &propGroup )
2852 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2853 prop.setDevice( configName() );
2854 prop.setName( propName );
2855 prop.setPerm( pcf::IndiProperty::ReadOnly );
2856 prop.setState( pcf::IndiProperty::Idle );
2859 if( propLabel !=
"" )
2861 prop.setLabel( propLabel );
2864 if( propGroup !=
"" )
2866 prop.setGroup( propGroup );
2872template <
bool _useINDI>
2874 const std::string &name,
2875 const std::string &label,
2876 const std::string &group )
2878 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2879 prop.setDevice( configName() );
2880 prop.setName( name );
2881 prop.setPerm( pcf::IndiProperty::ReadWrite );
2882 prop.setState( pcf::IndiProperty::Idle );
2883 prop.setRule( pcf::IndiProperty::AtMostOne );
2886 prop.add( pcf::IndiElement(
"toggle", pcf::IndiElement::Off ) );
2891 prop.setLabel( label );
2896 prop.setGroup( group );
2902template <
bool _useINDI>
2904 const std::string &name,
2905 const std::string &label,
2906 const std::string &group )
2908 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2909 prop.setDevice( configName() );
2910 prop.setName( name );
2911 prop.setPerm( pcf::IndiProperty::ReadWrite );
2912 prop.setState( pcf::IndiProperty::Idle );
2913 prop.setRule( pcf::IndiProperty::AtMostOne );
2916 prop.add( pcf::IndiElement(
"request", pcf::IndiElement::Off ) );
2921 prop.setLabel( label );
2926 prop.setGroup( group );
2932template <
bool _useINDI>
2934 const std::string &name,
2935 const std::vector<std::string> &elements,
2936 const std::vector<std::string> &elementLabels,
2937 const std::string &label,
2938 const std::string &group )
2940 if( elements.size() == 0 )
2942 return log<
software_error, -1>( { __FILE__, __LINE__,
"elements vector has zero size" } );
2945 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2946 prop.setDevice( configName() );
2947 prop.setName( name );
2948 prop.setPerm( pcf::IndiProperty::ReadWrite );
2949 prop.setState( pcf::IndiProperty::Idle );
2950 prop.setRule( pcf::IndiProperty::OneOfMany );
2953 for(
size_t n = 0; n < elements.size(); ++n )
2955 pcf::IndiElement elem = pcf::IndiElement( elements[n], pcf::IndiElement::Off );
2956 if(elementLabels[n] !=
"")
2958 elem.setLabel( elementLabels[n] );
2966 prop.setLabel( label );
2971 prop.setGroup( group );
2977template <
bool _useINDI>
2979 const std::string &name,
2980 const std::vector<std::string> &elements,
2981 const std::string &label,
2982 const std::string &group )
2984 return createStandardIndiSelectionSw( prop, name, elements, elements, label, group );
2987template <
bool _useINDI>
2997 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3000 if( !result.second )
3003 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3008 catch( std::exception &e )
3010 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3014 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3019template <
bool _useINDI>
3021 const std::string &propName,
3022 const pcf::IndiProperty::Type &propType,
3023 const pcf::IndiProperty::PropertyPermType &propPerm,
3024 const pcf::IndiProperty::PropertyStateType &propState )
3033 prop = pcf::IndiProperty( propType );
3034 prop.setDevice( m_configName );
3035 prop.setName( propName );
3036 prop.setPerm( propPerm );
3037 prop.setState( propState );
3039 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3042 if( !result.second )
3045 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3050 catch( std::exception &e )
3052 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3056 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3061template <
bool _useINDI>
3063 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3070 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3071 callBackInsertResult result =
3072 m_indiNewCallBacks.insert( callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
3074 if( !result.second )
3077 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
3082 catch( std::exception &e )
3084 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3088 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3093template <
bool _useINDI>
3094int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
3095 const std::string &propName,
3096 const pcf::IndiProperty::Type &propType,
3097 const pcf::IndiProperty::PropertyPermType &propPerm,
3098 const pcf::IndiProperty::PropertyStateType &propState,
3099 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3104 prop = pcf::IndiProperty( propType );
3105 prop.setDevice( m_configName );
3106 prop.setName( propName );
3107 prop.setPerm( propPerm );
3108 prop.setState( propState );
3110 return registerIndiPropertyNew( prop, callBack );
3113template <
bool _useINDI>
3114int MagAOXApp<_useINDI>::registerIndiPropertyNew( pcf::IndiProperty &prop,
3115 const std::string &propName,
3116 const pcf::IndiProperty::Type &propType,
3117 const pcf::IndiProperty::PropertyPermType &propPerm,
3118 const pcf::IndiProperty::PropertyStateType &propState,
3119 const pcf::IndiProperty::SwitchRuleType &propRule,
3120 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3125 prop = pcf::IndiProperty( propType );
3126 prop.setDevice( m_configName );
3127 prop.setName( propName );
3128 prop.setPerm( propPerm );
3129 prop.setState( propState );
3130 prop.setRule( propRule );
3131 return registerIndiPropertyNew( prop, callBack );
3134template <
bool _useINDI>
3136 const std::string &devName,
3137 const std::string &propName,
3138 int ( *callBack )(
void *,
const pcf::IndiProperty &ipRecv ) )
3147 prop = pcf::IndiProperty();
3148 prop.setDevice( devName );
3149 prop.setName( propName );
3151 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3154 if( !result.second )
3157 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() +
". Possible duplicate." } );
3160 catch( std::exception &e )
3162 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
3166 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
3172template <
bool _useINDI>
3176 callBack.
m_retryDelay = std::chrono::steady_clock::duration::zero();
3177 callBack.
m_nextRetry = std::chrono::steady_clock::time_point::min();
3181template <
bool _useINDI>
3184 const std::chrono::steady_clock::time_point &now )
const
3196 return callBack.
m_nextRetry == std::chrono::steady_clock::time_point::min() || now >= callBack.
m_nextRetry;
3199template <
bool _useINDI>
3201 const std::chrono::steady_clock::time_point &now )
3203 using namespace std::chrono;
3204 constexpr seconds retryInitialDelay{ 1 };
3205 constexpr seconds retryMaxDelay{ 60 };
3207 if( callBack.
m_retryDelay <= steady_clock::duration::zero() )
3222 log<text_log>(
"INDI property still unresolved after retry backoff: " + callBack.
property->createUniqueKey(),
3228template <
bool _useINDI>
3237 std::string driverFIFOPath = m_basePath;
3238 driverFIFOPath +=
"/";
3241 m_driverInName = driverFIFOPath +
"/" + configName() +
".in";
3242 m_driverOutName = driverFIFOPath +
"/" + configName() +
".out";
3243 m_driverCtrlName = driverFIFOPath +
"/" + configName() +
".ctrl";
3249 mode_t prev = umask( 0 );
3252 if( mkfifo( m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3254 if( errno != EEXIST )
3257 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3264 if( mkfifo( m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3266 if( errno != EEXIST )
3270 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3277 if( mkfifo( m_driverCtrlName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3279 if( errno != EEXIST )
3283 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3294template <
bool _useINDI>
3301 if( createINDIFIFOS() < 0 )
3309 if( m_indiDriver !=
nullptr )
3311 m_indiDriver->quitProcess();
3312 m_indiDriver->deactivate();
3313 log<indidriver_stop>();
3314 delete m_indiDriver;
3315 m_indiDriver =
nullptr;
3322 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction exception." } );
3327 if( m_indiDriver ==
nullptr )
3329 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction failed." } );
3334 if( m_indiDriver->good() ==
false )
3336 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver failed to open FIFOs." } );
3337 delete m_indiDriver;
3338 m_indiDriver =
nullptr;
3343 m_indiDriver->activate();
3344 log<indidriver_start>();
3346 sendGetPropertySetList(
true );
3351template <
bool _useINDI>
3354 std::vector<pcf::IndiProperty *> propsToGet;
3356 auto now = std::chrono::steady_clock::now();
3358 int unresolvedCount = 0;
3361 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3364 if( !all && m_allDefsReceived )
3371 while(
it != m_indiSetCallBacks.end() )
3375 if(
it->second.property )
3377 if(
it->first !=
it->second.property->createUniqueKey() )
3379 it->second.m_defReceived =
true;
3380 resetIndiSetPropertyRetry(
it->second );
3385 propsToGet.push_back(
it->second.property );
3388 it->second.m_defReceived =
false;
3389 resetIndiSetPropertyRetry(
it->second );
3392 else if(
it->second.m_defReceived ==
false )
3396 if(
it->second.property )
3398 if(
it->first !=
it->second.property->createUniqueKey() )
3400 it->second.m_defReceived =
true;
3401 resetIndiSetPropertyRetry(
it->second );
3407 if( indiSetPropertyShouldRequest(
it->second,
false, now ) )
3409 propsToGet.push_back(
it->second.property );
3410 noteIndiSetPropertyRequested(
it->second, now );
3418 m_allDefsReceived = ( unresolvedCount == 0 );
3421 for(
auto * prop : propsToGet )
3425 m_indiDriver->sendGetProperties( *prop );
3427 catch(
const std::exception &e )
3429 log<software_error>( { __FILE__,
3431 "exception caught from sendGetProperties for " + prop->getName() +
": " + e.what() } );
3436template <
bool _useINDI>
3439 handleSetProperty(
ipRecv );
3442template <
bool _useINDI>
3450 if( m_indiDriver ==
nullptr )
3456 if(
ipRecv.hasValidDevice() &&
ipRecv.getDevice() != m_indiDriver->getName() )
3462 if( !
ipRecv.hasValidName() )
3464 std::vector<pcf::IndiProperty *> propsToSend;
3467 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3470 while(
it != m_indiNewCallBacks.end() )
3472 if(
it->second.property )
3474 propsToSend.push_back(
it->second.property );
3480 for(
auto * prop : propsToSend )
3484 m_indiDriver->sendDefProperty( *prop );
3486 catch(
const std::exception &e )
3488 log<software_error>( { __FILE__,
3490 "exception caught from sendDefProperty for " + prop->getName() +
": " + e.what() } );
3495 sendGetPropertySetList(
true );
3500 pcf::IndiProperty * prop =
nullptr;
3502 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3503 auto it = m_indiNewCallBacks.find(
ipRecv.createUniqueKey() );
3504 if(
it == m_indiNewCallBacks.end() )
3509 prop =
it->second.property;
3517 m_indiDriver->sendDefProperty( *prop );
3519 catch(
const std::exception &e )
3521 log<software_error>( { __FILE__,
3523 "exception caught from sendDefProperty for " + prop->getName() +
": " + e.what() } );
3529template <
bool _useINDI>
3534 if( m_indiDriver ==
nullptr )
3537 int ( *callBack )(
void *,
const pcf::IndiProperty & ) =
nullptr;
3539 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3540 auto it = m_indiNewCallBacks.find(
ipRecv.createUniqueKey() );
3541 if(
it == m_indiNewCallBacks.end() )
3543 log<software_debug>( { __FILE__, __LINE__,
"invalid NewProperty request for " +
ipRecv.createUniqueKey() } );
3547 callBack =
it->second.callBack;
3552 callBack(
this,
ipRecv );
3556 log<software_debug>( { __FILE__, __LINE__,
"NewProperty callback null for " +
ipRecv.createUniqueKey() } );
3561template <
bool _useINDI>
3569 if( m_indiDriver ==
nullptr )
3574 std::string key =
ipRecv.createUniqueKey();
3575 int ( *callBack )(
void *,
const pcf::IndiProperty & ) =
nullptr;
3578 std::lock_guard<std::mutex>
lock( m_indiCallBackMutex );
3581 auto it = m_indiSetCallBacks.find( key );
3582 if(
it != m_indiSetCallBacks.end() )
3584 it->second.m_defReceived =
true;
3585 resetIndiSetPropertyRetry(
it->second );
3586 callBack =
it->second.callBack;
3598 callBack(
this,
ipRecv );
3604template <
bool _useINDI>
3605template <
typename T>
3607 const std::string &el,
3609 pcf::IndiProperty::PropertyStateType ipState )
3621 indi::updateIfChanged( p, el, newVal, m_indiDriver, ipState );
3624template <
bool _useINDI>
3626 const std::string &el,
3628 pcf::IndiProperty::PropertyStateType ipState )
3630 updateIfChanged<std::string>( p, el, std::string( newVal ), ipState );
3633template <
bool _useINDI>
3635 const std::string &el,
3636 const pcf::IndiElement::SwitchStateType &newVal,
3637 pcf::IndiProperty::PropertyStateType ipState )
3645 indi::updateSwitchIfChanged( p, el, newVal, m_indiDriver, ipState );
3648template <
bool _useINDI>
3649template <
typename T>
3651 const std::string &el,
3652 const std::vector<T> &newVals,
3653 pcf::IndiProperty::PropertyStateType ipState )
3661 std::vector<std::string> descriptors( newVals.size(), el );
3662 for(
size_t index = 0; index < newVals.size(); ++index )
3664 descriptors[index] += std::to_string( index );
3666 indi::updateIfChanged( p, descriptors, newVals, m_indiDriver );
3669template <
bool _useINDI>
3670template <
typename T>
3672 const std::vector<std::string> &els,
3673 const std::vector<T> &newVals,
3674 pcf::IndiProperty::PropertyStateType newState )
3682 indi::updateIfChanged( p, els, newVals, m_indiDriver, newState );
3685template <
bool _useINDI>
3686template <
typename T>
3688 const std::vector<const char *> &els,
3689 const std::vector<T> &newVals,
3690 pcf::IndiProperty::PropertyStateType newState )
3702 indi::updatesIfChanged( p, els, newVals, m_indiDriver, newState );
3705template <
bool _useINDI>
3706template <
typename T>
3709 const pcf::IndiProperty &remoteProperty,
3712 if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey() )
3717 if( !( remoteProperty.find(
"target" ) || remoteProperty.find(
"current" ) ) )
3724 if( remoteProperty.find(
"target" ) )
3726 localTarget = remoteProperty[
"target"].get<T>();
3732 if( remoteProperty.find(
"current" ) )
3734 localTarget = remoteProperty[
"current"].get<T>();
3746 updateIfChanged( localProperty,
"target", localTarget,
INDI_BUSY );
3750 updateIfChanged( localProperty,
"target", localTarget );
3756template <
bool _useINDI>
3757template <
typename T>
3767 log<software_error>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3770 pcf::IndiProperty ipToSend = ipSend;
3774 ipToSend[el].setValue( newVal );
3778 log<software_error>(
3779 { __FILE__, __LINE__,
"Exception caught setting " + ipSend.createUniqueKey() +
"." + el } );
3786 log<software_error>( { __FILE__, __LINE__ } );
3793template <
bool _useINDI>
3803 return log<
software_error, -1>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3806 if( m_indiDriver->sendNewProperty( ipSend ) < 0 )
3814template <
bool _useINDI>
3820 pcf::IndiProperty ipSend( pcf::IndiProperty::Switch );
3824 ipSend.setDevice( device );
3825 ipSend.setName( property );
3826 ipSend.add( pcf::IndiElement(
"toggle" ) );
3828 catch( std::exception &e )
3830 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"exception: " ) + e.what() } );
3833 if( onoff ==
false )
3835 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::Off );
3839 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::On );
3842 if( sendNewProperty( ipSend ) < 0 )
3845 { __FILE__, __LINE__,
"sendNewProperty failed for " + device +
"." +
property } );
3851template <
bool _useINDI>
3857template <
bool _useINDI>
3861 if(
ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey() )
3863 return log<
software_error, -1>( { __FILE__, __LINE__,
"wrong indi property received" } );
3866 if(
ipRecv.find(
"request" ) )
3868 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
3871 updateSwitchIfChanged( m_indiP_clearFSMAlert,
"request", pcf::IndiElement::Off,
INDI_IDLE );
3878template <
bool _useINDI>
3884template <
bool _useINDI>
3890template <
bool _useINDI>
3893 if( !m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0 )
3898 if( m_powerOnCounter * m_loopPause > ( (
double)m_powerOnWait ) * 1e9 )
3909template <
bool _useINDI>
3912 if( !m_powerMgtEnabled )
3917 return m_powerState;
3920template <
bool _useINDI>
3923 if( !m_powerMgtEnabled )
3928 return m_powerTargetState;
3931template <
bool _useINDI>
3936 if(
ipRecv.find( m_powerElement ) )
3938 ps =
ipRecv[m_powerElement].get<std::string>();
3944 else if( ps ==
"Off" )
3954 if(
ipRecv.find( m_powerTargetElement ) )
3956 ps =
ipRecv[m_powerTargetElement].get<std::string>();
3960 m_powerTargetState = 1;
3962 else if( ps ==
"Off" )
3964 m_powerTargetState = 0;
3968 m_powerTargetState = -1;
3975template <
bool _useINDI>
3981template <
bool _useINDI>
3984 return m_configName;
3987template <
bool _useINDI>
3993template <
bool _useINDI>
3996 return m_configBase;
3999template <
bool _useINDI>
4005template <
bool _useINDI>
4011template <
bool _useINDI>
4014 return m_secretsPath;
4017template <
bool _useINDI>
4020 return m_cpusetPath;
4023template <
bool _useINDI>
4029template <
bool _useINDI>
4035template <
bool _useINDI>
4038 return m_driverInName;
4041template <
bool _useINDI>
4044 return m_driverOutName;
4047template <
bool _useINDI>
4050 return m_driverCtrlName;
4053#ifdef XWCTEST_NAMESPACE
4058extern template class MagAOXApp<true>;
4059extern template class MagAOXApp<false>;
4079#define XWCAPP_THREAD_START( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart ) \
4080 if( threadStart( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, this, thrdStart ) < 0 ) \
4082 log<software_error>( { __FILE__, __LINE__, "error from threadStart for " #thrdName } ); \
4093#define XWCAPP_THREAD_CHECK( thrdSt, thrdName ) \
4096 if( pthread_tryjoin_np( thrdSt.native_handle(), 0 ) == 0 ) \
4098 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
4104 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
4113#define XWCAPP_THREAD_STOP( thrdSt ) \
4114 if( thrdSt.joinable() ) \
4116 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.
bool indiSetPropertyShouldRequest(const indiCallBack &callBack, bool all, const std::chrono::steady_clock::time_point &now) const
Determine whether an unresolved Set-property subscription should be requested now.
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::mutex m_indiCallBackMutex
Mutex for locking INDI callback maps and per-entry callback 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.
void noteIndiSetPropertyRequested(indiCallBack &callBack, const std::chrono::steady_clock::time_point &now)
Update retry tracking after sending a GetProperties request for a monitored Set-property.
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.
void resetIndiSetPropertyRetry(indiCallBack &callBack)
Reset retry tracking for a monitored INDI Set-property subscription.
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.
const pcf::IndiProperty & ipRecv
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
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.
pcf::IndiProperty * property
A pointer to an INDI property.
uint32_t m_retryCount
Number of GetProperties retries sent while waiting for a matching Def/Set.
std::chrono::steady_clock::time_point m_nextRetry
Earliest instant when the next retry may be sent.
std::chrono::steady_clock::duration m_retryDelay
Current retry delay for this unresolved subscription.
bool m_missingLogged
Tracks whether a long-unresolved notice has already been logged.
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