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;
124template <
bool _useINDI = true>
332 const std::
string &value,
381 bool m_elevated{
false };
497 template <
class thisPtr,
class Function>
502 pcf::IndiProperty &
thProp,
504 const std::string &
cpuset,
610 pcf::IndiProperty *
property{ 0 };
662 const std::string &
label =
"",
664 const std::string &
group =
""
675 const std::string &
elName,
678 const std::string &
elLabel =
""
686 template <
typename T>
688 const std::string &name,
695 const std::string &format,
698 const std::string &
label =
"",
700 const std::string &
group =
""
722 const std::string &name,
723 const std::string &
label =
"",
725 const std::string &
group =
""
735 const std::string &name,
736 const std::string &
label =
"",
738 const std::string &
group =
""
748 const std::string &name,
749 const std::vector<std::string> &elements,
753 const std::string &
label =
"",
755 const std::string &
group =
""
766 const std::string &name,
767 const std::vector<std::string> &elements,
769 const std::string &
label =
"",
771 const std::string &
group =
""
796 const pcf::IndiProperty::Type &
propType,
798 const pcf::IndiProperty::PropertyPermType &
propPerm,
801 const pcf::IndiProperty::PropertyStateType &
propState
814 int ( * )(
void *,
const pcf::IndiProperty & )
826 pcf::IndiProperty &prop,
828 const pcf::IndiProperty::Type &
propType,
829 const pcf::IndiProperty::PropertyPermType &
propPerm,
830 const pcf::IndiProperty::PropertyStateType &
propState,
831 int ( * )(
void *,
const pcf::IndiProperty & )
842 pcf::IndiProperty &prop,
844 const pcf::IndiProperty::Type &
propType,
845 const pcf::IndiProperty::PropertyPermType &
propPerm,
846 const pcf::IndiProperty::PropertyStateType &
propState,
847 const pcf::IndiProperty::SwitchRuleType &
propRule,
848 int ( * )(
void *,
const pcf::IndiProperty & )
859 pcf::IndiProperty &prop,
862 int ( * )(
void *,
const pcf::IndiProperty & )
923 template <
typename T>
925 const std::string &el,
927 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
942 const std::string &el,
944 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
953 const std::string &el,
954 const pcf::IndiElement::SwitchStateType &
newVal,
955 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
969 template <
typename T>
971 pcf::IndiProperty &p,
972 const std::string &el,
974 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok
986 template <
typename T>
988 const std::vector<std::string> &
els,
990 pcf::IndiProperty::PropertyStateType
newState =
991 pcf::IndiProperty::Ok
994 template <
typename T>
996 const std::vector<const char *> &
els,
998 pcf::IndiProperty::PropertyStateType
newState =
999 pcf::IndiProperty::Ok
1007 template <
typename T>
1020 template <
typename T>
1022 const std::string &el,
1039 const std::string &property,
1056 const pcf::IndiProperty &
ipRecv
1233template <
bool _useINDI>
1237template <
bool _useINDI>
1240template <
bool _useINDI>
1243 if( m_self !=
nullptr )
1245 std::cerr <<
"Attempt to instantiate 2nd MagAOXApp. Exiting immediately.\n";
1252 getresuid( &m_euidReal, &m_euidCalled, &m_suid );
1255 m_log.parent(
this );
1258 config.m_sources =
true;
1259 config.configLog = configLog;
1271 if( MXLIB_UNCOMP_REPO_MODIFIED )
1277 log<git_state>(
git_state::messageT(
"mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED ), gl );
1280template <
bool _useINDI>
1284 delete m_indiDriver;
1285 m_log.parent(
nullptr );
1291template <
bool _useINDI>
1300 m_basePath = tmpstr;
1313 m_configDir = m_basePath +
"/" + tmpstr;
1314 m_configPathGlobal = m_configDir +
"/magaox.conf";
1322 m_calibDir = m_basePath +
"/" + tmpstr;
1330 m_log.logPath( m_basePath +
"/" + tmpstr );
1338 m_sysPath = m_basePath +
"/" + tmpstr;
1346 m_secretsPath = m_basePath +
"/" + tmpstr;
1352 m_cpusetPath = tmpstr;
1356 if( m_configBase !=
"" )
1359 m_configPathUser = m_configDir +
"/" + m_configBase +
".conf";
1371 "The name of the application and its device name in INDI (if used), specifies the config file in the XWC config directory." );
1373 config.parseCommandLine( argc, argv,
"name" );
1374 config( m_configName,
"name" );
1376 if( m_configName ==
"" )
1378 m_configName = mx::ioutils::pathStem( invokedName );
1381 log<text_log>(
"Configuration Error: Application name (-n --name) not set." );
1387 m_configPathLocal = m_configDir +
"/" + m_configName +
".conf";
1390 if( registerIndiPropertyNew(
1391 m_indiP_state,
"fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0 ) <
1394 log<software_error>( { __FILE__, __LINE__,
"failed to register read only fsm_state property" } );
1397 m_indiP_state.add( pcf::IndiElement(
"state" ) );
1399 createStandardIndiRequestSw( m_indiP_clearFSMAlert,
"fsm_clear_alert",
"Clear FSM Alert",
"FSM" );
1400 if( registerIndiPropertyNew( m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert ) < 0 )
1402 log<software_error>( { __FILE__, __LINE__,
"failed to register new fsm_alert property" } );
1408template <
bool _useINDI>
1412 config.add(
"config.validate",
1420 "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." );
1423 config.add(
"loopPause",
1431 "The main loop pause time in ns" );
1434 "ignore_git",
"",
"ignore-git", argType::True,
"",
"",
false,
"bool",
"set to true to ignore git "
1435 "status to prevent the fsm_alert" );
1438 m_log.setupConfig( config );
1440 if( m_powerMgtEnabled )
1442 if( _useINDI ==
false )
1445 log<software_critical>( { __FILE__, __LINE__,
"power management is enabled but we are not using INDI" } );
1450 config.add(
"power.device",
1458 "Device controlling power for this app's device (INDI name)." );
1460 config.add(
"power.channel",
1468 "Channel on device for this app's device (INDI name)." );
1470 config.add(
"power.element",
1478 "INDI power state element name. Default is \"state\", only need to specify if different." );
1480 config.add(
"power.targetElement",
1482 "power.targetElement",
1488 "INDI power target element name. Default is \"target\", only need to specify if different." );
1490 config.add(
"power.powerOnWait",
1492 "power.powerOnWait",
1498 "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec." );
1503template <
bool _useINDI>
1508 config( ig,
"ignore_git" );
1510 if( !ig && m_gitAlert )
1512 m_stateAlert =
true;
1516 if(config.isSet(
"config.validate"))
1518 m_configOnly =
true;
1522 m_log.logName( m_configName );
1523 m_log.loadConfig( config );
1526 config( m_loopPause,
"loopPause" );
1529 if( m_powerMgtEnabled )
1531 config( m_powerDevice,
"power.device" );
1532 config( m_powerChannel,
"power.channel" );
1533 config( m_powerElement,
"power.element" );
1534 config( m_powerTargetElement,
"power.targetElement" );
1536 if( m_powerDevice !=
"" && m_powerChannel !=
"" )
1538 log<text_log>(
"enabling power management: " + m_powerDevice +
"." + m_powerChannel +
"." + m_powerElement +
1539 "/" + m_powerTargetElement );
1540 if( registerIndiPropertySet(
1541 m_indiP_powerChannel, m_powerDevice, m_powerChannel,
INDI_SETCALLBACK( m_indiP_powerChannel ) ) <
1544 log<software_error>( { __FILE__, __LINE__,
"failed to register set property" } );
1553 config( m_powerOnWait,
"power.powerOnWait" );
1554 if( m_powerOnWait > 3600 )
1556 log<text_log>(
"powerOnWait longer than 1 hour. Setting to 0.",
logPrio::LOG_ERROR );
1562template <
bool _useINDI>
1567 for(
auto it = config.m_targets.begin();
it != config.m_targets.end(); ++
it )
1569 if(
it->second.used ==
false )
1571 std::string
msg =
it->second.name;
1572 if( config.m_sources &&
it->second.sources.size() > 0 )
1574 msg +=
" [" +
it->second.sources[0] +
"]";
1582 if( config.m_unusedConfigs.size() > 0 )
1584 for(
auto it = config.m_unusedConfigs.begin();
it != config.m_unusedConfigs.end(); ++
it )
1586 if(
it->second.used ==
true )
1591 std::string
msg =
it->second.name;
1592 if( config.m_sources &&
it->second.sources.size() > 0 )
1594 msg +=
" [" +
it->second.sources[0] +
"]";
1603 if( config.nonOptions.size() > 0 )
1605 for(
size_t n = 0; n < config.nonOptions.size(); ++n )
1607 log<text_log>(
"Unrecognized command line argument: " + config.nonOptions[n],
logPrio::LOG_CRITICAL );
1614 if(m_shutdown ==
true)
1616 std::cerr <<
"\nThere were configuration errors.\n\n";
1620 std::cerr <<
"\nConfiguration is valid.\n\n";
1623 else if(m_shutdown ==
true)
1629template <
bool _useINDI>
1635#ifndef XWC_DISABLE_USER_CHECK
1636 struct stat logstat;
1638 if( stat( m_log.logPath().c_str(), &logstat ) < 0 )
1641 std::cerr <<
"\nCRITICAL: Can not stat the log path.\n\n";
1645 if( logstat.st_uid != geteuid() )
1648 std::cerr <<
"\nCRITICAL: You are running this app as the wrong user.\n\n";
1661 std::cerr <<
"\nCRITICAL: Failed to lock PID. Exiting.\n\n";
1670 m_log.logThreadStart();
1674 while( m_log.logThreadRunning() ==
false && w < 20 )
1677 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( 100000000 ) );
1681 if( m_log.logThreadRunning() ==
false )
1686 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1690 if( unlockPID() < 0 )
1692 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1701 if( m_shutdown == 0 )
1703 if( setSigTermHandler() < 0 )
1707 log<software_critical>( { __FILE__, __LINE__,
"error from setSigTermHandler()" } );
1711 if( unlockPID() < 0 )
1713 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1723 if( m_shutdown == 0 )
1727 if( appStartup() < 0 )
1731 log<software_critical>( { __FILE__, __LINE__,
"error from appStartup()" } );
1735 if( unlockPID() < 0 )
1737 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1745 if( m_useINDI && m_shutdown == 0 )
1747 if( startINDI() < 0 )
1751 log<software_critical>( { __FILE__, __LINE__,
"INDI failed to start." } );
1756 if( appShutdown() < 0 )
1758 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1761 if( unlockPID() < 0 )
1763 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1771 if( m_powerMgtEnabled && m_shutdown == 0 )
1774 while( m_powerState < 0 && !m_shutdown )
1777 if( m_powerState < 0 )
1779 if( !stateLogged() )
1780 log<text_log>(
"waiting for power state" );
1791 if( m_powerState > 0 )
1797 m_powerOnCounter = 0;
1799 if( onPowerOff() < 0 )
1801 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1816 while( m_shutdown == 0 )
1819 if( m_powerMgtEnabled )
1823 if( m_powerState == 1 )
1825 m_powerOnCounter = 0;
1831 if( m_powerState == 0 )
1834 if( onPowerOff() < 0 )
1836 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1846 if( !m_powerMgtEnabled || m_powerState > 0 )
1848 if( appLogic() < 0 )
1850 log<software_error>( { __FILE__, __LINE__,
"error from appLogic()" } );
1855 else if( m_powerState == 0 )
1857 if( whilePowerOff() < 0 )
1859 log<software_error>( { __FILE__, __LINE__,
"error from whilePowerOff()" } );
1874 sendGetPropertySetList(
false );
1882 if( m_shutdown == 0 )
1884 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( m_loopPause ) );
1888 if( appShutdown() < 0 )
1890 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1896 if( m_indiDriver !=
nullptr )
1898 pcf::IndiProperty ipSend;
1899 ipSend.setDevice( m_configName );
1902 m_indiDriver->sendDelProperty( ipSend );
1904 catch(
const std::exception &e )
1906 log<software_error>(
1907 { __FILE__, __LINE__, std::string(
"exception caught from sendDelProperty: " ) + e.what() } );
1910 m_indiDriver->quitProcess();
1911 m_indiDriver->deactivate();
1912 log<indidriver_stop>();
1915 if( unlockPID() < 0 )
1917 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1924template <
bool _useINDI>
1925template <
typename logT,
int retval>
1928 m_log.template log<logT>(
msg, level );
1932template <
bool _useINDI>
1933template <
typename logT,
int retval>
1936 m_log.template log<logT>( level );
1940template <
bool _useINDI>
1945 logStdFormat( std::cerr, b );
1951 state( m_state,
true );
1954 if( _useINDI && m_indiDriver )
1956 pcf::IndiProperty
msg;
1957 msg.setDevice( m_configName );
1959 std::stringstream logstdf;
1960 logMinStdFormat( logstdf, b );
1962 msg.setMessage( logstdf.str() );
1968 tv.tv_usec = (
long int)( ( (
double)ts.
time_ns ) / 1e3 );
1970 msg.setTimeStamp( pcf::TimeStamp( tv ) );
1974 m_indiDriver->sendMessage(
msg );
1976 catch(
const std::exception &e )
1978 log<software_error>(
1979 { __FILE__, __LINE__, std::string(
"exception caught from sendMessage: " ) + e.what() } );
1984template <
bool _useINDI>
1987 const std::string &value,
1988 const std::string &source )
1990 m_log.template log<config_log>( { name, code, value, source } );
1993template <
bool _useINDI>
1996 struct sigaction act;
2000 act.sa_flags = SA_SIGINFO;
2001 sigemptyset( &set );
2005 if( sigaction( SIGTERM, &act, 0 ) < 0 )
2007 std::string logss =
"Setting handler for SIGTERM failed. Errno says: ";
2008 logss += strerror( errno );
2010 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2016 if( sigaction( SIGQUIT, &act, 0 ) < 0 )
2018 std::string logss =
"Setting handler for SIGQUIT failed. Errno says: ";
2019 logss += strerror( errno );
2021 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2027 if( sigaction( SIGINT, &act, 0 ) < 0 )
2029 std::string logss =
"Setting handler for SIGINT failed. Errno says: ";
2030 logss += strerror( errno );
2032 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2037 log<text_log>(
"Installed SIGTERM/SIGQUIT/SIGINT signal handler.",
logPrio::LOG_DEBUG );
2042template <
bool _useINDI>
2045 m_self->handlerSigTerm( signum, siginf, ucont );
2048template <
bool _useINDI>
2050 siginfo_t *siginf __attribute__( ( unused ) ),
2051 void *ucont __attribute__( ( unused ) ) )
2055 std::string signame;
2059 signame =
"SIGTERM";
2065 signame =
"SIGQUIT";
2071 std::string logss =
"Caught signal ";
2073 logss +=
". Shutting down.";
2075 std::cerr <<
"\n" << logss << std::endl;
2076 log<text_log>( logss );
2080void sigUsr1Handler(
int signum, siginfo_t *siginf,
void *ucont );
2082template <
bool _useINDI>
2088 std::string logss =
"Setting effective user id to euidCalled (";
2089 logss += mx::ioutils::convertToString<int>( m_euidCalled );
2090 logss +=
") failed. Errno says: ";
2091 logss += strerror( errno );
2093 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2101template <
bool _useINDI>
2107 std::string logss =
"Setting effective user id to euidReal (";
2108 logss += mx::ioutils::convertToString<int>( m_euidReal );
2109 logss +=
") failed. Errno says: ";
2110 logss += strerror( errno );
2112 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2120template <
bool _useINDI>
2125 std::string statusDir = m_sysPath;
2133 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2135 if( errno != EEXIST )
2137 std::stringstream logss;
2138 logss <<
"Failed to create root of statusDir (" << statusDir <<
"). Errno says: " << strerror( errno );
2139 log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
2145 statusDir += m_configName;
2147 pidFileName = statusDir +
"/pid";
2152 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2154 if( errno != EEXIST )
2156 std::stringstream logss;
2157 logss <<
"Failed to create statusDir (" << statusDir <<
"). Errno says: " << strerror( errno );
2158 log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
2164 std::ifstream pidIn;
2165 pidIn.open( pidFileName );
2175 std::stringstream procN;
2176 procN <<
"/proc/" << testPid <<
"/cmdline";
2178 std::ifstream procIn;
2179 std::string pidCmdLine;
2183 procIn.open( procN.str() );
2185 procIn >> pidCmdLine;
2190 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"exception caught testing /proc/pid" } );
2198 size_t invokedPos = pidCmdLine.find( invokedName );
2201 size_t configPos = std::string::npos;
2202 if( invokedPos != std::string::npos )
2203 configPos = pidCmdLine.find( m_configName );
2206 if( invokedPos != std::string::npos && configPos != std::string::npos )
2209 std::stringstream logss;
2210 logss <<
"PID already locked (" << testPid <<
"). Time to die.";
2211 std::cerr << logss.str() << std::endl;
2226 std::ofstream pidOut;
2227 pidOut.open( pidFileName );
2229 if( !pidOut.good() )
2231 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"could not open pid file for writing." } );
2240 std::stringstream logss;
2241 logss <<
"PID (" << m_pid <<
") locked.";
2242 log<text_log>( logss.str() );
2254template <
bool _useINDI>
2262 if( ::remove( pidFileName.c_str() ) < 0 )
2264 log<software_error>(
2265 { __FILE__, __LINE__, errno, 0, std::string(
"Failed to remove PID file: " ) + strerror( errno ) } );
2270 std::stringstream logss;
2271 logss <<
"PID (" << m_pid <<
") unlocked.";
2272 log<text_log>( logss.str() );
2277template <
bool _useINDI>
2278template <
class thisPtr,
class Function>
2282 pcf::IndiProperty &thProp,
2284 const std::string &cpuset,
2285 const std::string &thrdName,
2287 Function &&thrdStart )
2295 thrd = std::thread( thrdStart, thrdThis );
2297 catch(
const std::exception &e )
2299 log<software_error>(
2300 { __FILE__, __LINE__, std::string(
"Exception on " + thrdName +
" thread start: " ) + e.what() } );
2305 log<software_error>( { __FILE__, __LINE__,
"Unkown exception on " + thrdName +
" thread start" } );
2309 if( !thrd.joinable() )
2311 log<software_error>( { __FILE__, __LINE__, thrdName +
" thread did not start" } );
2323 sp.sched_priority = thrdPrio;
2337 rv = pthread_setschedparam( thrd.native_handle(), SCHED_OTHER, &sp );
2342 log<software_error>(
2346 "Setting " + thrdName +
" thread scheduler priority to " + std::to_string( thrdPrio ) +
" failed." } );
2350 log<text_log>( thrdName +
" thread scheduler priority set to " + std::to_string( thrdPrio ) );
2356 for(
int i = 0; i < 10; ++i )
2358 mx::sys::milliSleep( 100 );
2366 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"tpid for " + thrdName +
" not set." } );
2370 log<text_log>( thrdName +
" thread pid is " + std::to_string( tpid ) );
2374 thProp = pcf::IndiProperty( pcf::IndiProperty::Number );
2375 thProp.setDevice( configName() );
2376 thProp.setName( std::string(
"th-" ) + thrdName );
2377 thProp.setPerm( pcf::IndiProperty::ReadOnly );
2378 thProp.setState( pcf::IndiProperty::Idle );
2379 thProp.add( pcf::IndiElement(
"pid" ) );
2380 thProp[
"pid"] = tpid;
2381 thProp.add( pcf::IndiElement(
"prio" ) );
2382 thProp[
"prio"] = thrdPrio;
2383 registerIndiPropertyReadOnly( thProp );
2389 std::string cpuFile = m_cpusetPath;
2390 cpuFile +=
"/" + cpuset;
2391 cpuFile +=
"/tasks";
2392 int wfd = open( cpuFile.c_str(), O_WRONLY );
2395 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error from open for " + cpuFile } );
2399 snprintf( pids,
sizeof( pids ),
"%d", tpid );
2401 int w = write( wfd, pids, strnlen( pids,
sizeof( pids ) ) );
2402 if( w != (
int)strnlen( pids,
sizeof(pids) ) )
2404 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error on write" } );
2418template <
bool _useINDI>
2424template <
bool _useINDI>
2436 log<state_change>( { m_state, s }, lvl );
2442 if( m_stateAlert != stateAlert && stateAlert ==
true )
2444 m_stateAlert = stateAlert;
2449 std::unique_lock<std::mutex>
lock( m_indiMutex,
2453 if(
lock.owns_lock() )
2456 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2459 if( m_stateAlert ==
true )
2482template <
bool _useINDI>
2485 return m_stateAlert;
2488template <
bool _useINDI>
2494template <
bool _useINDI>
2497 if( m_stateLogged > 0 )
2500 return m_stateLogged - 1;
2509template <
bool _useINDI>
2512 if( m_stateAlert ==
false )
2517 m_stateAlert =
false;
2521 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2543template <
bool _useINDI>
2545 const std::string &propName,
2546 const std::string &label,
2547 const std::string &group )
2549 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2550 prop.setDevice( configName() );
2551 prop.setName( propName );
2552 prop.setPerm( pcf::IndiProperty::ReadWrite );
2553 prop.setState( pcf::IndiProperty::Idle );
2554 prop.add( pcf::IndiElement(
"current" ) );
2555 prop.add( pcf::IndiElement(
"target" ) );
2560 prop.setLabel( label );
2565 prop.setGroup( group );
2571template <
bool _useINDI>
2573 const std::string &propName,
2574 const std::string &elName,
2575 const std::string &propLabel,
2576 const std::string &propGroup,
2577 const std::string &elLabel )
2579 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2580 prop.setDevice( configName() );
2581 prop.setName( propName );
2582 prop.setPerm( pcf::IndiProperty::ReadOnly );
2583 prop.setState( pcf::IndiProperty::Idle );
2586 if( propLabel !=
"" )
2588 prop.setLabel( propLabel );
2591 if( propGroup !=
"" )
2593 prop.setGroup( propGroup );
2596 prop.add( pcf::IndiElement( elName ) );
2600 prop[elName].setLabel( elLabel );
2606template <
bool _useINDI>
2607template <
typename T>
2609 const std::string &name,
2613 const std::string &format,
2614 const std::string &label,
2615 const std::string &group )
2617 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2618 prop.setDevice( configName() );
2619 prop.setName( name );
2620 prop.setPerm( pcf::IndiProperty::ReadWrite );
2621 prop.setState( pcf::IndiProperty::Idle );
2622 prop.add( pcf::IndiElement(
"current" ) );
2623 prop[
"current"].setMin( min );
2624 prop[
"current"].setMax( max );
2625 prop[
"current"].setStep( step );
2628 prop[
"current"].setFormat( format );
2631 prop.add( pcf::IndiElement(
"target" ) );
2632 prop[
"target"].setMin( min );
2633 prop[
"target"].setMax( max );
2634 prop[
"target"].setStep( step );
2637 prop[
"target"].setFormat( format );
2643 prop.setLabel( label );
2648 prop.setGroup( group );
2654template <
bool _useINDI>
2656 const std::string &propName,
2657 const std::string &propLabel,
2658 const std::string &propGroup )
2660 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2661 prop.setDevice( configName() );
2662 prop.setName( propName );
2663 prop.setPerm( pcf::IndiProperty::ReadOnly );
2664 prop.setState( pcf::IndiProperty::Idle );
2667 if( propLabel !=
"" )
2669 prop.setLabel( propLabel );
2672 if( propGroup !=
"" )
2674 prop.setGroup( propGroup );
2680template <
bool _useINDI>
2682 const std::string &name,
2683 const std::string &label,
2684 const std::string &group )
2686 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2687 prop.setDevice( configName() );
2688 prop.setName( name );
2689 prop.setPerm( pcf::IndiProperty::ReadWrite );
2690 prop.setState( pcf::IndiProperty::Idle );
2691 prop.setRule( pcf::IndiProperty::AtMostOne );
2694 prop.add( pcf::IndiElement(
"toggle", pcf::IndiElement::Off ) );
2699 prop.setLabel( label );
2704 prop.setGroup( group );
2710template <
bool _useINDI>
2712 const std::string &name,
2713 const std::string &label,
2714 const std::string &group )
2716 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2717 prop.setDevice( configName() );
2718 prop.setName( name );
2719 prop.setPerm( pcf::IndiProperty::ReadWrite );
2720 prop.setState( pcf::IndiProperty::Idle );
2721 prop.setRule( pcf::IndiProperty::AtMostOne );
2724 prop.add( pcf::IndiElement(
"request", pcf::IndiElement::Off ) );
2729 prop.setLabel( label );
2734 prop.setGroup( group );
2740template <
bool _useINDI>
2742 const std::string &name,
2743 const std::vector<std::string> &elements,
2744 const std::vector<std::string> &elementLabels,
2745 const std::string &label,
2746 const std::string &group )
2748 if( elements.size() == 0 )
2750 return log<
software_error, -1>( { __FILE__, __LINE__,
"elements vector has zero size" } );
2753 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2754 prop.setDevice( configName() );
2755 prop.setName( name );
2756 prop.setPerm( pcf::IndiProperty::ReadWrite );
2757 prop.setState( pcf::IndiProperty::Idle );
2758 prop.setRule( pcf::IndiProperty::OneOfMany );
2761 for(
size_t n = 0; n < elements.size(); ++n )
2763 pcf::IndiElement elem = pcf::IndiElement( elements[n], pcf::IndiElement::Off );
2764 elem.setLabel( elementLabels[n] );
2771 prop.setLabel( label );
2776 prop.setGroup( group );
2782template <
bool _useINDI>
2784 const std::string &name,
2785 const std::vector<std::string> &elements,
2786 const std::string &label,
2787 const std::string &group )
2789 return createStandardIndiSelectionSw( prop, name, elements, elements, label, group );
2792template <
bool _useINDI>
2799 m_indiNewCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, nullptr } ) );
2803 if( !result.second )
2806 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2809 catch( std::exception &e )
2811 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2815 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2821template <
bool _useINDI>
2823 const std::string &propName,
2824 const pcf::IndiProperty::Type &
propType,
2825 const pcf::IndiProperty::PropertyPermType &propPerm,
2826 const pcf::IndiProperty::PropertyStateType &propState )
2831 prop = pcf::IndiProperty(
propType );
2832 prop.setDevice( m_configName );
2833 prop.setName( propName );
2834 prop.setPerm( propPerm );
2835 prop.setState( propState );
2841 if( !result.second )
2844 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2847 catch( std::exception &e )
2849 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2853 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2858template <
bool _useINDI>
2860 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2867 callBackInsertResult result =
2868 m_indiNewCallBacks.insert( callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
2870 if( !result.second )
2873 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2876 catch( std::exception &e )
2878 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2882 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2888template <
bool _useINDI>
2890 const std::string &propName,
2891 const pcf::IndiProperty::Type &
propType,
2892 const pcf::IndiProperty::PropertyPermType &propPerm,
2893 const pcf::IndiProperty::PropertyStateType &propState,
2894 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2899 prop = pcf::IndiProperty(
propType );
2900 prop.setDevice( m_configName );
2901 prop.setName( propName );
2902 prop.setPerm( propPerm );
2903 prop.setState( propState );
2905 return registerIndiPropertyNew( prop, callBack );
2908template <
bool _useINDI>
2910 const std::string &propName,
2911 const pcf::IndiProperty::Type &
propType,
2912 const pcf::IndiProperty::PropertyPermType &propPerm,
2913 const pcf::IndiProperty::PropertyStateType &propState,
2914 const pcf::IndiProperty::SwitchRuleType &propRule,
2915 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2920 prop = pcf::IndiProperty(
propType );
2921 prop.setDevice( m_configName );
2922 prop.setName( propName );
2923 prop.setPerm( propPerm );
2924 prop.setState( propState );
2925 prop.setRule( propRule );
2926 return registerIndiPropertyNew( prop, callBack );
2929template <
bool _useINDI>
2931 const std::string &devName,
2932 const std::string &propName,
2933 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2938 prop = pcf::IndiProperty();
2939 prop.setDevice( devName );
2940 prop.setName( propName );
2943 m_indiSetCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
2947 if( !result.second )
2950 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2953 catch( std::exception &e )
2955 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2959 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2965template <
bool _useINDI>
2973 driverFIFOPath +=
"/";
2976 m_driverInName = driverFIFOPath +
"/" + configName() +
".in";
2977 m_driverOutName = driverFIFOPath +
"/" + configName() +
".out";
2978 m_driverCtrlName = driverFIFOPath +
"/" + configName() +
".ctrl";
2984 mode_t prev = umask( 0 );
2987 if( mkfifo( m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
2989 if( errno != EEXIST )
2992 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
2999 if( mkfifo( m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3001 if( errno != EEXIST )
3005 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3012 if( mkfifo( m_driverCtrlName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
3014 if( errno != EEXIST )
3018 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
3029template <
bool _useINDI>
3036 if( createINDIFIFOS() < 0 )
3044 if( m_indiDriver !=
nullptr )
3046 m_indiDriver->quitProcess();
3047 m_indiDriver->deactivate();
3048 log<indidriver_stop>();
3049 delete m_indiDriver;
3050 m_indiDriver =
nullptr;
3057 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction exception." } );
3062 if( m_indiDriver ==
nullptr )
3064 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction failed." } );
3069 if( m_indiDriver->good() ==
false )
3071 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver failed to open FIFOs." } );
3072 delete m_indiDriver;
3073 m_indiDriver =
nullptr;
3078 m_indiDriver->activate();
3079 log<indidriver_start>();
3081 sendGetPropertySetList(
true );
3086template <
bool _useINDI>
3090 if( !all && m_allDefsReceived )
3096 while(
it != m_indiSetCallBacks.end() )
3098 if( all ||
it->second.m_defReceived ==
false )
3100 if(
it->second.property )
3104 m_indiDriver->sendGetProperties( *(
it->second.property ) );
3106 catch(
const std::exception &e )
3108 log<software_error>( { __FILE__,
3110 "exception caught from sendGetProperties for " +
3111 it->second.property->getName() +
": " + e.what() } );
3115 it->second.m_defReceived =
false;
3121 m_allDefsReceived =
false;
3123 m_allDefsReceived =
true;
3126template <
bool _useINDI>
3129 handleSetProperty(
ipRecv );
3132template <
bool _useINDI>
3137 if( m_indiDriver ==
nullptr )
3141 if(
ipRecv.hasValidDevice() &&
ipRecv.getDevice() != m_indiDriver->getName() )
3147 if( !
ipRecv.hasValidName() )
3151 while(
it != m_indiNewCallBacks.end() )
3153 if(
it->second.property )
3157 m_indiDriver->sendDefProperty( *(
it->second.property ) );
3159 catch(
const std::exception &e )
3161 log<software_error>( { __FILE__,
3163 "exception caught from sendDefProperty for " +
3164 it->second.property->getName() +
": " + e.what() } );
3171 sendGetPropertySetList(
true );
3177 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3183 if( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property )
3187 m_indiDriver->sendDefProperty( *( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property ) );
3189 catch(
const std::exception &e )
3191 log<software_error>( { __FILE__,
3193 "exception caught from sendDefProperty for " +
3194 m_indiNewCallBacks[
ipRecv.createUniqueKey()].property->getName() +
": " +
3201template <
bool _useINDI>
3206 if( m_indiDriver ==
nullptr )
3210 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3212 log<software_debug>( { __FILE__, __LINE__,
"invalid NewProperty request for " +
ipRecv.createUniqueKey() } );
3216 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiNewCallBacks[
ipRecv.createUniqueKey()].callBack;
3219 callBack(
this,
ipRecv );
3221 log<software_debug>( { __FILE__, __LINE__,
"NewProperty callback null for " +
ipRecv.createUniqueKey() } );
3226template <
bool _useINDI>
3231 if( m_indiDriver ==
nullptr )
3234 std::string key =
ipRecv.createUniqueKey();
3237 if( m_indiSetCallBacks.count( key ) > 0 )
3239 m_indiSetCallBacks[key].m_defReceived =
true;
3242 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiSetCallBacks[key].callBack;
3244 callBack(
this,
ipRecv );
3256template <
bool _useINDI>
3257template <
typename T>
3259 const std::string &el,
3261 pcf::IndiProperty::PropertyStateType ipState )
3272template <
bool _useINDI>
3274 const std::string &el,
3276 pcf::IndiProperty::PropertyStateType ipState )
3278 updateIfChanged<std::string>( p, el, std::string( newVal ), ipState );
3281template <
bool _useINDI>
3283 const std::string &el,
3284 const pcf::IndiElement::SwitchStateType &newVal,
3285 pcf::IndiProperty::PropertyStateType ipState )
3296template <
bool _useINDI>
3297template <
typename T>
3299 const std::string &el,
3300 const std::vector<T> &newVals,
3301 pcf::IndiProperty::PropertyStateType ipState )
3309 std::vector<std::string> descriptors( newVals.size(), el );
3310 for(
size_t index = 0; index < newVals.size(); ++index )
3312 descriptors[index] += std::to_string( index );
3317template <
bool _useINDI>
3318template <
typename T>
3320 const std::vector<std::string> &els,
3321 const std::vector<T> &newVals,
3322 pcf::IndiProperty::PropertyStateType newState )
3333template <
bool _useINDI>
3334template <
typename T>
3336 const std::vector<const char *> &els,
3337 const std::vector<T> &newVals,
3338 pcf::IndiProperty::PropertyStateType newState )
3353template <
bool _useINDI>
3354template <
typename T>
3357 const pcf::IndiProperty &remoteProperty,
3360 if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey() )
3365 if( !( remoteProperty.find(
"target" ) || remoteProperty.find(
"current" ) ) )
3372 if( remoteProperty.find(
"target" ) )
3374 localTarget = remoteProperty[
"target"].get<T>();
3380 if( remoteProperty.find(
"current" ) )
3382 localTarget = remoteProperty[
"current"].get<T>();
3406template <
typename T>
3409 return pcf::IndiProperty::Unknown;
3424template <
bool _useINDI>
3425template <
typename T>
3433 log<software_error>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3436 pcf::IndiProperty ipToSend = ipSend;
3440 ipToSend[el].setValue( newVal );
3444 log<software_error>(
3445 { __FILE__, __LINE__,
"Exception caught setting " + ipSend.createUniqueKey() +
"." + el } );
3449 int rv = m_indiDriver->sendNewProperty( ipToSend );
3452 log<software_error>( { __FILE__, __LINE__ } );
3459template <
bool _useINDI>
3467 return log<
software_error, -1>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3470 if( m_indiDriver->sendNewProperty( ipSend ) < 0 )
3478template <
bool _useINDI>
3484 pcf::IndiProperty ipSend( pcf::IndiProperty::Switch );
3488 ipSend.setDevice( device );
3489 ipSend.setName( property );
3490 ipSend.add( pcf::IndiElement(
"toggle" ) );
3492 catch( std::exception &e )
3494 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"exception: " ) + e.what() } );
3497 if( onoff ==
false )
3499 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::Off );
3503 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::On );
3506 if( sendNewProperty( ipSend ) < 0 )
3509 { __FILE__, __LINE__,
"sendNewProperty failed for " + device +
"." +
property } );
3515template <
bool _useINDI>
3521template <
bool _useINDI>
3525 if(
ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey() )
3527 return log<
software_error, -1>( { __FILE__, __LINE__,
"wrong indi property received" } );
3530 if( !
ipRecv.find(
"request" ) )
3533 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
3536 updateSwitchIfChanged( m_indiP_clearFSMAlert,
"request", pcf::IndiElement::Off,
INDI_IDLE );
3542template <
bool _useINDI>
3548template <
bool _useINDI>
3554template <
bool _useINDI>
3557 if( !m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0 )
3562 if( m_powerOnCounter * m_loopPause > ( (
double)m_powerOnWait ) * 1e9 )
3573template <
bool _useINDI>
3576 if( !m_powerMgtEnabled )
3579 return m_powerState;
3582template <
bool _useINDI>
3585 if( !m_powerMgtEnabled )
3588 return m_powerTargetState;
3591template <
bool _useINDI>
3599 ps =
ipRecv[m_powerElement].get<std::string>();
3605 else if( ps ==
"Off" )
3617 ps =
ipRecv[m_powerTargetElement].get<std::string>();
3621 m_powerTargetState = 1;
3623 else if( ps ==
"Off" )
3625 m_powerTargetState = 0;
3629 m_powerTargetState = -1;
3636template <
bool _useINDI>
3642template <
bool _useINDI>
3645 return m_configName;
3648template <
bool _useINDI>
3654template <
bool _useINDI>
3657 return m_configBase;
3660template <
bool _useINDI>
3666template <
bool _useINDI>
3672template <
bool _useINDI>
3675 return m_secretsPath;
3678template <
bool _useINDI>
3681 return m_cpusetPath;
3684template <
bool _useINDI>
3690template <
bool _useINDI>
3696template <
bool _useINDI>
3699 return m_driverInName;
3702template <
bool _useINDI>
3705 return m_driverOutName;
3708template <
bool _useINDI>
3711 return m_driverCtrlName;
3735#define XWCAPP_THREAD_START( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart ) \
3736 if( threadStart( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, this, thrdStart ) < 0 ) \
3738 log<software_error>( { __FILE__, __LINE__, "error from threadStart for " #thrdName } ); \
3749#define XWCAPP_THREAD_CHECK( thrdSt, thrdName ) \
3752 if( pthread_tryjoin_np( thrdSt.native_handle(), 0 ) == 0 ) \
3754 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3760 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3769#define XWCAPP_THREAD_STOP( thrdSt ) \
3770 if( thrdSt.joinable() ) \
3772 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.
static void _handlerSigTerm(int signum, siginfo_t *siginf, void *ucont)
The handler called when SIGTERM, SIGQUIT, or SIGINT is received. Just a wrapper for handlerSigTerm.
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.
bool m_allDefsReceived
Flag indicating that all registered Set properties have been updated since last Get.
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.
virtual int execute()
The execute method implementing the standard main loop. Should not normally be overridden.
static void configLog(const std::string &name, const int &code, const std::string &value, const std::string &source)
Callback for config system logging.
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)
unsigned long m_loopPause
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.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
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.
virtual int appLogic()=0
This is where derived applications implement their main FSM logic.
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.
int shutdown()
Get the value of the shutdown flag.
stateCodes::stateCodeT m_state
int createStandardIndiSelectionSw(pcf::IndiProperty &prop, const std::string &name, const std::vector< std::string > &elements, const std::string &label="", const std::string &group="")
int m_powerState
Current power state, 1=On, 0=Off, -1=Unk.
std::string m_powerDevice
The INDI device name of the power controller.
friend class MagAOXApp_test
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.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
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::string m_powerElement
The INDI element name to monitor for this device's power state.
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.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
void handleGetProperties(const pcf::IndiProperty &ipRecv)
Handler for the get INDI properties request.
virtual int appShutdown()=0
Any tasks to perform after main loop exit go here.
uid_t m_euidReal
The real user id of the proces (i.e. the lower privileged id of the user)
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.
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.
virtual void setDefaults(int argc, char **argv)
Set the paths for config files.
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.
static constexpr bool m_useINDI
Flag controlling whether INDI is used. If false, then no INDI code executes.
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.
logger::logManager< MagAOXApp< _useINDI >, logFileRaw > logManagerT
The log manager type.
pid_t m_pid
This process's PID.
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.
unsigned long m_powerOnWait
Time in sec to wait for device to boot after power on.
std::string m_powerTargetElement
The INDI element name to monitor for this device's power state.
int clearFSMAlert()
Clear the FSM alert state.
std::string m_cpusetPath
Path to the cpusets mount.
std::string cpusetPath()
Get the cpuset path.
virtual void setupBasicConfig()
The basic MagAO-X configuration setup method. Should not normally be overridden.
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 setSigTermHandler()
Sets the handler for SIGTERM, SIGQUIT, and SIGINT.
virtual int appStartup()=0
Any tasks to perform prior to the main event loop go here.
int startINDI()
Start INDI Communications.
virtual void checkConfig()
Check for unused and unrecognized config options and settings.
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)
virtual void loadBasicConfig()
The basic MagAO-X configuration processing method. Should not normally be overridden.
void logMessage(bufferPtrT &b)
Handle a log message from the logging system.
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.
static MagAOXApp * m_self
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.
int m_powerTargetState
Current target power state, 1=On, 0=Off, -1=Unk.
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.
uid_t m_suid
The save-set user id of the process.
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 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.
@ OPERATING
The device is operating, other than homing.
@ POWEROFF
The device power is off.
@ NODEVICE
No device exists for the application to control.
@ SHUTDOWN
The application has shutdown, set just after calling appShutdown().
@ NOTHOMED
The device has not been homed.
@ HOMING
The device is homing.
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
@ CONFIGURING
The application is configuring the device.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ READY
The device is ready for operation, but is not operating.
@ LOGGEDIN
The application has logged into the device or service.
@ UNINITIALIZED
The application is unitialized, the default.
@ INITIALIZED
The application has been initialized, set just before calling appStartup().
@ POWERON
The device power is on.
int th_seteuid(uid_t euid)
Sets the effective user id of the calling thread, rather than the whole process.
MagAO-X INDI Driver Wrapper.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
void updatesIfChanged(pcf::IndiProperty &p, const elVecT &els, const std::vector< T > &newVals, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the elements of an INDI propery, but only if there has been a change in at least one.
pcf::IndiProperty::Type propType< int >()
pcf::IndiProperty::Type propType< std::string >()
pcf::IndiProperty::Type propType< char * >()
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
pcf::IndiProperty::Type propType< double >()
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
pcf::IndiProperty::Type propType()
std::unique_lock< std::mutex > lock(m_indiMutex)
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.
MagAO-X Application States.
Structure to hold the call-back details for handling INDI communications.
int(* callBack)(void *, const pcf::IndiProperty &)
int16_t stateCodeT
The type of the state code.
static std::string codeText(const stateCodeT &stateCode)
Get an ASCII string corresponding to an application stateCode.
The type of the input message.
The standard MagAOX log manager, used for both process logs and telemetry streams.
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.