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>
331 const std::
string &value,
380 bool m_elevated{
false };
504 template <
class thisPtr,
class Function>
509 pcf::IndiProperty &
thProp,
511 const std::string &
cpuset,
604 pcf::IndiProperty *
property{ 0 };
656 const std::string &
label =
"",
658 const std::string &
group =
""
669 const std::string &
elName,
672 const std::string &
elLabel =
""
680 template <
typename T>
682 const std::string &name,
689 const std::string &format,
692 const std::string &
label =
"",
694 const std::string &
group =
""
716 const std::string &name,
717 const std::string &
label =
"",
719 const std::string &
group =
""
729 const std::string &name,
730 const std::string &
label =
"",
732 const std::string &
group =
""
742 const std::string &name,
743 const std::vector<std::string> &elements,
747 const std::string &
label =
"",
749 const std::string &
group =
""
760 const std::string &name,
761 const std::vector<std::string> &elements,
763 const std::string &
label =
"",
765 const std::string &
group =
""
790 const pcf::IndiProperty::Type &
propType,
792 const pcf::IndiProperty::PropertyPermType &
propPerm,
795 const pcf::IndiProperty::PropertyStateType &
propState
808 int ( * )(
void *,
const pcf::IndiProperty & )
820 pcf::IndiProperty &prop,
822 const pcf::IndiProperty::Type &
propType,
823 const pcf::IndiProperty::PropertyPermType &
propPerm,
824 const pcf::IndiProperty::PropertyStateType &
propState,
825 int ( * )(
void *,
const pcf::IndiProperty & )
836 pcf::IndiProperty &prop,
838 const pcf::IndiProperty::Type &
propType,
839 const pcf::IndiProperty::PropertyPermType &
propPerm,
840 const pcf::IndiProperty::PropertyStateType &
propState,
841 const pcf::IndiProperty::SwitchRuleType &
propRule,
842 int ( * )(
void *,
const pcf::IndiProperty & )
853 pcf::IndiProperty &prop,
856 int ( * )(
void *,
const pcf::IndiProperty & )
917 template <
typename T>
919 const std::string &el,
921 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
936 const std::string &el,
938 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
947 const std::string &el,
948 const pcf::IndiElement::SwitchStateType &
newVal,
949 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
963 template <
typename T>
965 pcf::IndiProperty &p,
966 const std::string &el,
968 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok
980 template <
typename T>
982 const std::vector<std::string> &
els,
984 pcf::IndiProperty::PropertyStateType
newState =
985 pcf::IndiProperty::Ok
988 template <
typename T>
990 const std::vector<const char *> &
els,
992 pcf::IndiProperty::PropertyStateType
newState =
993 pcf::IndiProperty::Ok
1001 template <
typename T>
1014 template <
typename T>
1016 const std::string &el,
1033 const std::string &property,
1050 const pcf::IndiProperty &
ipRecv
1185template <
bool _useINDI>
1189template <
bool _useINDI>
1192template <
bool _useINDI>
1195 if( m_self !=
nullptr )
1197 std::cerr <<
"Attempt to instantiate 2nd MagAOXApp. Exiting immediately.\n";
1204 getresuid( &m_euidReal, &m_euidCalled, &m_suid );
1207 m_log.parent(
this );
1210 config.m_sources =
true;
1211 config.configLog = configLog;
1223 if( MXLIB_UNCOMP_REPO_MODIFIED )
1229 log<git_state>(
git_state::messageT(
"mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED ), gl );
1232template <
bool _useINDI>
1236 delete m_indiDriver;
1237 m_log.parent(
nullptr );
1242template <
bool _useINDI>
1248template <
bool _useINDI>
1257 MagAOXPath = tmpstr;
1270 m_configDir = MagAOXPath +
"/" + tmpstr;
1271 m_configPathGlobal = m_configDir +
"/magaox.conf";
1279 m_calibDir = MagAOXPath +
"/" + tmpstr;
1284 m_log.logPath( tmpstr );
1292 secretsPath = tmpstr;
1298 m_cpusetPath = tmpstr;
1302 if( m_configBase !=
"" )
1305 m_configPathUser = m_configDir +
"/" + m_configBase +
".conf";
1317 "The name of the application and its device name in INDI (if used), specifies the config file in the XWC config directory." );
1319 config.parseCommandLine( argc, argv,
"name" );
1320 config( m_configName,
"name" );
1322 if( m_configName ==
"" )
1324 m_configName = mx::ioutils::pathStem( invokedName );
1327 log<text_log>(
"Configuration Error: Application name (-n --name) not set." );
1333 m_configPathLocal = m_configDir +
"/" + m_configName +
".conf";
1336 if( registerIndiPropertyNew(
1337 m_indiP_state,
"fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0 ) <
1340 log<software_error>( { __FILE__, __LINE__,
"failed to register read only fsm_state property" } );
1343 m_indiP_state.add( pcf::IndiElement(
"state" ) );
1345 createStandardIndiRequestSw( m_indiP_clearFSMAlert,
"fsm_clear_alert",
"Clear FSM Alert",
"FSM" );
1346 if( registerIndiPropertyNew( m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert ) < 0 )
1348 log<software_error>( { __FILE__, __LINE__,
"failed to register new fsm_alert property" } );
1354template <
bool _useINDI>
1358 config.add(
"config.validate",
1366 "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." );
1369 config.add(
"loopPause",
1377 "The main loop pause time in ns" );
1380 "ignore_git",
"",
"ignore-git", argType::True,
"",
"",
false,
"bool",
"set to true to ignore git status to prevent the fsm_alert" );
1383 m_log.setupConfig( config );
1385 if( m_powerMgtEnabled )
1387 if( _useINDI ==
false )
1390 log<software_critical>( { __FILE__, __LINE__,
"power management is enabled but we are not using INDI" } );
1395 config.add(
"power.device",
1403 "Device controlling power for this app's device (INDI name)." );
1404 config.add(
"power.channel",
1412 "Channel on device for this app's device (INDI name)." );
1413 config.add(
"power.element",
1421 "INDI power state element name. Default is \"state\", only need to specify if different." );
1422 config.add(
"power.targetElement",
1424 "power.targetElement",
1430 "INDI power target element name. Default is \"target\", only need to specify if different." );
1431 config.add(
"power.powerOnWait",
1433 "power.powerOnWait",
1439 "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec." );
1443template <
bool _useINDI>
1448 config( ig,
"ignore_git" );
1450 if( !ig && m_gitAlert )
1452 m_stateAlert =
true;
1456 if(config.isSet(
"config.validate"))
1458 m_configOnly =
true;
1462 m_log.logName( m_configName );
1463 m_log.loadConfig( config );
1466 config( m_loopPause,
"loopPause" );
1469 if( m_powerMgtEnabled )
1471 config( m_powerDevice,
"power.device" );
1472 config( m_powerChannel,
"power.channel" );
1473 config( m_powerElement,
"power.element" );
1474 config( m_powerTargetElement,
"power.targetElement" );
1476 if( m_powerDevice !=
"" && m_powerChannel !=
"" )
1478 log<text_log>(
"enabling power management: " + m_powerDevice +
"." + m_powerChannel +
"." + m_powerElement +
1479 "/" + m_powerTargetElement );
1480 if( registerIndiPropertySet(
1481 m_indiP_powerChannel, m_powerDevice, m_powerChannel,
INDI_SETCALLBACK( m_indiP_powerChannel ) ) <
1484 log<software_error>( { __FILE__, __LINE__,
"failed to register set property" } );
1493 config( m_powerOnWait,
"power.powerOnWait" );
1494 if( m_powerOnWait > 3600 )
1496 log<text_log>(
"powerOnWait longer than 1 hour. Setting to 0.",
logPrio::LOG_ERROR );
1501template <
bool _useINDI>
1506 for(
auto it = config.m_targets.begin();
it != config.m_targets.end(); ++
it )
1508 if(
it->second.used ==
false )
1510 std::string
msg =
it->second.name;
1511 if( config.m_sources &&
it->second.sources.size() > 0 )
1513 msg +=
" [" +
it->second.sources[0] +
"]";
1521 if( config.m_unusedConfigs.size() > 0 )
1523 for(
auto it = config.m_unusedConfigs.begin();
it != config.m_unusedConfigs.end(); ++
it )
1525 if(
it->second.used ==
true )
1530 std::string
msg =
it->second.name;
1531 if( config.m_sources &&
it->second.sources.size() > 0 )
1533 msg +=
" [" +
it->second.sources[0] +
"]";
1542 if( config.nonOptions.size() > 0 )
1544 for(
size_t n = 0; n < config.nonOptions.size(); ++n )
1546 log<text_log>(
"Unrecognized command line argument: " + config.nonOptions[n],
logPrio::LOG_CRITICAL );
1553 if(m_shutdown ==
true)
1555 std::cerr <<
"\nThere were configuration errors.\n\n";
1559 std::cerr <<
"\nConfiguration is valid.\n\n";
1562 else if(m_shutdown ==
true)
1568template <
bool _useINDI>
1574#ifndef XWC_DISABLE_USER_CHECK
1575 struct stat logstat;
1577 if( stat( m_log.logPath().c_str(), &logstat ) < 0 )
1580 std::cerr <<
"\nCRITICAL: Can not stat the log path.\n\n";
1584 if( logstat.st_uid != geteuid() )
1587 std::cerr <<
"\nCRITICAL: You are running this app as the wrong user.\n\n";
1600 std::cerr <<
"\nCRITICAL: Failed to lock PID. Exiting.\n\n";
1609 m_log.logThreadStart();
1613 while( m_log.logThreadRunning() ==
false && w < 20 )
1616 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( 100000000 ) );
1620 if( m_log.logThreadRunning() ==
false )
1625 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1629 if( unlockPID() < 0 )
1631 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1640 if( m_shutdown == 0 )
1642 if( setSigTermHandler() < 0 )
1646 log<software_critical>( { __FILE__, __LINE__,
"error from setSigTermHandler()" } );
1650 if( unlockPID() < 0 )
1652 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1662 if( m_shutdown == 0 )
1666 if( appStartup() < 0 )
1670 log<software_critical>( { __FILE__, __LINE__,
"error from appStartup()" } );
1674 if( unlockPID() < 0 )
1676 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1684 if( m_useINDI && m_shutdown == 0 )
1686 if( startINDI() < 0 )
1690 log<software_critical>( { __FILE__, __LINE__,
"INDI failed to start." } );
1695 if( appShutdown() < 0 )
1697 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1700 if( unlockPID() < 0 )
1702 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1710 if( m_powerMgtEnabled && m_shutdown == 0 )
1713 while( m_powerState < 0 && !m_shutdown )
1716 if( m_powerState < 0 )
1718 if( !stateLogged() )
1719 log<text_log>(
"waiting for power state" );
1730 if( m_powerState > 0 )
1736 m_powerOnCounter = 0;
1738 if( onPowerOff() < 0 )
1740 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1755 while( m_shutdown == 0 )
1758 if( m_powerMgtEnabled )
1762 if( m_powerState == 1 )
1764 m_powerOnCounter = 0;
1770 if( m_powerState == 0 )
1773 if( onPowerOff() < 0 )
1775 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1785 if( !m_powerMgtEnabled || m_powerState > 0 )
1787 if( appLogic() < 0 )
1789 log<software_error>( { __FILE__, __LINE__,
"error from appLogic()" } );
1794 else if( m_powerState == 0 )
1796 if( whilePowerOff() < 0 )
1798 log<software_error>( { __FILE__, __LINE__,
"error from whilePowerOff()" } );
1813 sendGetPropertySetList(
false );
1821 if( m_shutdown == 0 )
1823 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( m_loopPause ) );
1827 if( appShutdown() < 0 )
1829 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1835 if( m_indiDriver !=
nullptr )
1837 pcf::IndiProperty ipSend;
1838 ipSend.setDevice( m_configName );
1841 m_indiDriver->sendDelProperty( ipSend );
1843 catch(
const std::exception &e )
1845 log<software_error>(
1846 { __FILE__, __LINE__, std::string(
"exception caught from sendDelProperty: " ) + e.what() } );
1849 m_indiDriver->quitProcess();
1850 m_indiDriver->deactivate();
1851 log<indidriver_stop>();
1854 if( unlockPID() < 0 )
1856 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1863template <
bool _useINDI>
1864template <
typename logT,
int retval>
1867 m_log.template log<logT>(
msg, level );
1871template <
bool _useINDI>
1872template <
typename logT,
int retval>
1875 m_log.template log<logT>( level );
1879template <
bool _useINDI>
1884 logStdFormat( std::cerr, b );
1890 state( m_state,
true );
1893 if( _useINDI && m_indiDriver )
1895 pcf::IndiProperty
msg;
1896 msg.setDevice( m_configName );
1898 std::stringstream logstdf;
1899 logMinStdFormat( logstdf, b );
1901 msg.setMessage( logstdf.str() );
1907 tv.tv_usec = (
long int)( ( (
double)ts.
time_ns ) / 1e3 );
1909 msg.setTimeStamp( pcf::TimeStamp( tv ) );
1913 m_indiDriver->sendMessage(
msg );
1915 catch(
const std::exception &e )
1917 log<software_error>(
1918 { __FILE__, __LINE__, std::string(
"exception caught from sendMessage: " ) + e.what() } );
1923template <
bool _useINDI>
1926 const std::string &value,
1927 const std::string &source )
1929 m_log.template log<config_log>( { name, code, value, source } );
1932template <
bool _useINDI>
1935 struct sigaction act;
1939 act.sa_flags = SA_SIGINFO;
1940 sigemptyset( &set );
1944 if( sigaction( SIGTERM, &act, 0 ) < 0 )
1946 std::string logss =
"Setting handler for SIGTERM failed. Errno says: ";
1947 logss += strerror( errno );
1949 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
1955 if( sigaction( SIGQUIT, &act, 0 ) < 0 )
1957 std::string logss =
"Setting handler for SIGQUIT failed. Errno says: ";
1958 logss += strerror( errno );
1960 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
1966 if( sigaction( SIGINT, &act, 0 ) < 0 )
1968 std::string logss =
"Setting handler for SIGINT failed. Errno says: ";
1969 logss += strerror( errno );
1971 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
1976 log<text_log>(
"Installed SIGTERM/SIGQUIT/SIGINT signal handler.",
logPrio::LOG_DEBUG );
1981template <
bool _useINDI>
1984 m_self->handlerSigTerm( signum, siginf, ucont );
1987template <
bool _useINDI>
1989 siginfo_t *siginf __attribute__( ( unused ) ),
1990 void *ucont __attribute__( ( unused ) ) )
1994 std::string signame;
1998 signame =
"SIGTERM";
2004 signame =
"SIGQUIT";
2010 std::string logss =
"Caught signal ";
2012 logss +=
". Shutting down.";
2014 std::cerr <<
"\n" << logss << std::endl;
2015 log<text_log>( logss );
2019void sigUsr1Handler(
int signum, siginfo_t *siginf,
void *ucont );
2021template <
bool _useINDI>
2027 std::string logss =
"Setting effective user id to euidCalled (";
2028 logss += mx::ioutils::convertToString<int>( m_euidCalled );
2029 logss +=
") failed. Errno says: ";
2030 logss += strerror( errno );
2032 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2040template <
bool _useINDI>
2046 std::string logss =
"Setting effective user id to euidReal (";
2047 logss += mx::ioutils::convertToString<int>( m_euidReal );
2048 logss +=
") failed. Errno says: ";
2049 logss += strerror( errno );
2051 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2059template <
bool _useINDI>
2064 std::string statusDir = sysPath;
2072 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2074 if( errno != EEXIST )
2076 std::stringstream logss;
2077 logss <<
"Failed to create root of statusDir (" << statusDir <<
"). Errno says: " << strerror( errno );
2078 log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
2084 statusDir += m_configName;
2086 pidFileName = statusDir +
"/pid";
2091 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2093 if( errno != EEXIST )
2095 std::stringstream logss;
2096 logss <<
"Failed to create statusDir (" << statusDir <<
"). Errno says: " << strerror( errno );
2097 log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
2103 std::ifstream pidIn;
2104 pidIn.open( pidFileName );
2114 std::stringstream procN;
2115 procN <<
"/proc/" << testPid <<
"/cmdline";
2117 std::ifstream procIn;
2118 std::string pidCmdLine;
2122 procIn.open( procN.str() );
2124 procIn >> pidCmdLine;
2129 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"exception caught testing /proc/pid" } );
2137 size_t invokedPos = pidCmdLine.find( invokedName );
2140 size_t configPos = std::string::npos;
2141 if( invokedPos != std::string::npos )
2142 configPos = pidCmdLine.find( m_configName );
2145 if( invokedPos != std::string::npos && configPos != std::string::npos )
2148 std::stringstream logss;
2149 logss <<
"PID already locked (" << testPid <<
"). Time to die.";
2150 std::cerr << logss.str() << std::endl;
2165 std::ofstream pidOut;
2166 pidOut.open( pidFileName );
2168 if( !pidOut.good() )
2170 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"could not open pid file for writing." } );
2179 std::stringstream logss;
2180 logss <<
"PID (" << m_pid <<
") locked.";
2181 log<text_log>( logss.str() );
2193template <
bool _useINDI>
2201 if( ::remove( pidFileName.c_str() ) < 0 )
2203 log<software_error>(
2204 { __FILE__, __LINE__, errno, 0, std::string(
"Failed to remove PID file: " ) + strerror( errno ) } );
2209 std::stringstream logss;
2210 logss <<
"PID (" << m_pid <<
") unlocked.";
2211 log<text_log>( logss.str() );
2216template <
bool _useINDI>
2217template <
class thisPtr,
class Function>
2221 pcf::IndiProperty &thProp,
2223 const std::string &cpuset,
2224 const std::string &thrdName,
2226 Function &&thrdStart )
2234 thrd = std::thread( thrdStart, thrdThis );
2236 catch(
const std::exception &e )
2238 log<software_error>(
2239 { __FILE__, __LINE__, std::string(
"Exception on " + thrdName +
" thread start: " ) + e.what() } );
2244 log<software_error>( { __FILE__, __LINE__,
"Unkown exception on " + thrdName +
" thread start" } );
2248 if( !thrd.joinable() )
2250 log<software_error>( { __FILE__, __LINE__, thrdName +
" thread did not start" } );
2262 sp.sched_priority = thrdPrio;
2276 rv = pthread_setschedparam( thrd.native_handle(), SCHED_OTHER, &sp );
2281 log<software_error>(
2285 "Setting " + thrdName +
" thread scheduler priority to " + std::to_string( thrdPrio ) +
" failed." } );
2289 log<text_log>( thrdName +
" thread scheduler priority set to " + std::to_string( thrdPrio ) );
2295 for(
int i = 0; i < 10; ++i )
2297 mx::sys::milliSleep( 100 );
2305 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"tpid for " + thrdName +
" not set." } );
2309 log<text_log>( thrdName +
" thread pid is " + std::to_string( tpid ) );
2313 thProp = pcf::IndiProperty( pcf::IndiProperty::Number );
2314 thProp.setDevice( configName() );
2315 thProp.setName( std::string(
"th-" ) + thrdName );
2316 thProp.setPerm( pcf::IndiProperty::ReadOnly );
2317 thProp.setState( pcf::IndiProperty::Idle );
2318 thProp.add( pcf::IndiElement(
"pid" ) );
2319 thProp[
"pid"] = tpid;
2320 thProp.add( pcf::IndiElement(
"prio" ) );
2321 thProp[
"prio"] = thrdPrio;
2322 registerIndiPropertyReadOnly( thProp );
2328 std::string cpuFile = m_cpusetPath;
2329 cpuFile +=
"/" + cpuset;
2330 cpuFile +=
"/tasks";
2331 int wfd = open( cpuFile.c_str(), O_WRONLY );
2334 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error from open for " + cpuFile } );
2338 snprintf( pids,
sizeof( pids ),
"%d", tpid );
2340 int w = write( wfd, pids, strnlen( pids,
sizeof( pids ) ) );
2341 if( w != (
int)strnlen( pids,
sizeof(pids) ) )
2343 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error on write" } );
2357template <
bool _useINDI>
2363template <
bool _useINDI>
2375 log<state_change>( { m_state, s }, lvl );
2381 if( m_stateAlert != stateAlert && stateAlert ==
true )
2383 m_stateAlert = stateAlert;
2388 std::unique_lock<std::mutex>
lock( m_indiMutex,
2392 if(
lock.owns_lock() )
2395 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2398 if( m_stateAlert ==
true )
2421template <
bool _useINDI>
2424 if( m_stateLogged > 0 )
2427 return m_stateLogged - 1;
2436template <
bool _useINDI>
2439 if( m_stateAlert ==
false )
2441 m_stateAlert =
false;
2444 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2466template <
bool _useINDI>
2468 const std::string &propName,
2469 const std::string &label,
2470 const std::string &group )
2472 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2473 prop.setDevice( configName() );
2474 prop.setName( propName );
2475 prop.setPerm( pcf::IndiProperty::ReadWrite );
2476 prop.setState( pcf::IndiProperty::Idle );
2477 prop.add( pcf::IndiElement(
"current" ) );
2478 prop.add( pcf::IndiElement(
"target" ) );
2483 prop.setLabel( label );
2488 prop.setGroup( group );
2494template <
bool _useINDI>
2496 const std::string &propName,
2497 const std::string &elName,
2498 const std::string &propLabel,
2499 const std::string &propGroup,
2500 const std::string &elLabel )
2502 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2503 prop.setDevice( configName() );
2504 prop.setName( propName );
2505 prop.setPerm( pcf::IndiProperty::ReadOnly );
2506 prop.setState( pcf::IndiProperty::Idle );
2509 if( propLabel !=
"" )
2511 prop.setLabel( propLabel );
2514 if( propGroup !=
"" )
2516 prop.setGroup( propGroup );
2519 prop.add( pcf::IndiElement( elName ) );
2523 prop[elName].setLabel( elLabel );
2529template <
bool _useINDI>
2530template <
typename T>
2532 const std::string &name,
2536 const std::string &format,
2537 const std::string &label,
2538 const std::string &group )
2540 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2541 prop.setDevice( configName() );
2542 prop.setName( name );
2543 prop.setPerm( pcf::IndiProperty::ReadWrite );
2544 prop.setState( pcf::IndiProperty::Idle );
2545 prop.add( pcf::IndiElement(
"current" ) );
2546 prop[
"current"].setMin( min );
2547 prop[
"current"].setMax( max );
2548 prop[
"current"].setStep( step );
2551 prop[
"current"].setFormat( format );
2554 prop.add( pcf::IndiElement(
"target" ) );
2555 prop[
"target"].setMin( min );
2556 prop[
"target"].setMax( max );
2557 prop[
"target"].setStep( step );
2560 prop[
"target"].setFormat( format );
2566 prop.setLabel( label );
2571 prop.setGroup( group );
2577template <
bool _useINDI>
2579 const std::string &propName,
2580 const std::string &propLabel,
2581 const std::string &propGroup )
2583 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2584 prop.setDevice( configName() );
2585 prop.setName( propName );
2586 prop.setPerm( pcf::IndiProperty::ReadOnly );
2587 prop.setState( pcf::IndiProperty::Idle );
2590 if( propLabel !=
"" )
2592 prop.setLabel( propLabel );
2595 if( propGroup !=
"" )
2597 prop.setGroup( propGroup );
2603template <
bool _useINDI>
2605 const std::string &name,
2606 const std::string &label,
2607 const std::string &group )
2609 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2610 prop.setDevice( configName() );
2611 prop.setName( name );
2612 prop.setPerm( pcf::IndiProperty::ReadWrite );
2613 prop.setState( pcf::IndiProperty::Idle );
2614 prop.setRule( pcf::IndiProperty::AtMostOne );
2617 prop.add( pcf::IndiElement(
"toggle", pcf::IndiElement::Off ) );
2622 prop.setLabel( label );
2627 prop.setGroup( group );
2633template <
bool _useINDI>
2635 const std::string &name,
2636 const std::string &label,
2637 const std::string &group )
2639 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2640 prop.setDevice( configName() );
2641 prop.setName( name );
2642 prop.setPerm( pcf::IndiProperty::ReadWrite );
2643 prop.setState( pcf::IndiProperty::Idle );
2644 prop.setRule( pcf::IndiProperty::AtMostOne );
2647 prop.add( pcf::IndiElement(
"request", pcf::IndiElement::Off ) );
2652 prop.setLabel( label );
2657 prop.setGroup( group );
2663template <
bool _useINDI>
2665 const std::string &name,
2666 const std::vector<std::string> &elements,
2667 const std::vector<std::string> &elementLabels,
2668 const std::string &label,
2669 const std::string &group )
2671 if( elements.size() == 0 )
2673 return log<
software_error, -1>( { __FILE__, __LINE__,
"elements vector has zero size" } );
2676 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2677 prop.setDevice( configName() );
2678 prop.setName( name );
2679 prop.setPerm( pcf::IndiProperty::ReadWrite );
2680 prop.setState( pcf::IndiProperty::Idle );
2681 prop.setRule( pcf::IndiProperty::OneOfMany );
2684 for(
size_t n = 0; n < elements.size(); ++n )
2686 pcf::IndiElement elem = pcf::IndiElement( elements[n], pcf::IndiElement::Off );
2687 elem.setLabel( elementLabels[n] );
2694 prop.setLabel( label );
2699 prop.setGroup( group );
2705template <
bool _useINDI>
2707 const std::string &name,
2708 const std::vector<std::string> &elements,
2709 const std::string &label,
2710 const std::string &group )
2712 return createStandardIndiSelectionSw( prop, name, elements, elements, label, group );
2715template <
bool _useINDI>
2722 m_indiNewCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, nullptr } ) );
2726 if( !result.second )
2729 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2732 catch( std::exception &e )
2734 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2738 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2744template <
bool _useINDI>
2746 const std::string &propName,
2747 const pcf::IndiProperty::Type &
propType,
2748 const pcf::IndiProperty::PropertyPermType &propPerm,
2749 const pcf::IndiProperty::PropertyStateType &propState )
2754 prop = pcf::IndiProperty(
propType );
2755 prop.setDevice( m_configName );
2756 prop.setName( propName );
2757 prop.setPerm( propPerm );
2758 prop.setState( propState );
2764 if( !result.second )
2767 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2770 catch( std::exception &e )
2772 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2776 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2781template <
bool _useINDI>
2783 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2790 callBackInsertResult result =
2791 m_indiNewCallBacks.insert( callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
2793 if( !result.second )
2796 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2799 catch( std::exception &e )
2801 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2805 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2811template <
bool _useINDI>
2813 const std::string &propName,
2814 const pcf::IndiProperty::Type &
propType,
2815 const pcf::IndiProperty::PropertyPermType &propPerm,
2816 const pcf::IndiProperty::PropertyStateType &propState,
2817 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2822 prop = pcf::IndiProperty(
propType );
2823 prop.setDevice( m_configName );
2824 prop.setName( propName );
2825 prop.setPerm( propPerm );
2826 prop.setState( propState );
2828 return registerIndiPropertyNew( prop, callBack );
2831template <
bool _useINDI>
2833 const std::string &propName,
2834 const pcf::IndiProperty::Type &
propType,
2835 const pcf::IndiProperty::PropertyPermType &propPerm,
2836 const pcf::IndiProperty::PropertyStateType &propState,
2837 const pcf::IndiProperty::SwitchRuleType &propRule,
2838 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2843 prop = pcf::IndiProperty(
propType );
2844 prop.setDevice( m_configName );
2845 prop.setName( propName );
2846 prop.setPerm( propPerm );
2847 prop.setState( propState );
2848 prop.setRule( propRule );
2849 return registerIndiPropertyNew( prop, callBack );
2852template <
bool _useINDI>
2854 const std::string &devName,
2855 const std::string &propName,
2856 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2861 prop = pcf::IndiProperty();
2862 prop.setDevice( devName );
2863 prop.setName( propName );
2866 m_indiSetCallBacks.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>
2896 driverFIFOPath +=
"/";
2899 m_driverInName = driverFIFOPath +
"/" + configName() +
".in";
2900 m_driverOutName = driverFIFOPath +
"/" + configName() +
".out";
2901 m_driverCtrlName = driverFIFOPath +
"/" + configName() +
".ctrl";
2907 mode_t prev = umask( 0 );
2910 if( mkfifo( m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
2912 if( errno != EEXIST )
2915 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
2922 if( mkfifo( m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
2924 if( errno != EEXIST )
2928 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
2935 if( mkfifo( m_driverCtrlName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
2937 if( errno != EEXIST )
2941 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
2952template <
bool _useINDI>
2959 if( createINDIFIFOS() < 0 )
2967 if( m_indiDriver !=
nullptr )
2969 m_indiDriver->quitProcess();
2970 m_indiDriver->deactivate();
2971 log<indidriver_stop>();
2972 delete m_indiDriver;
2973 m_indiDriver =
nullptr;
2980 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction exception." } );
2985 if( m_indiDriver ==
nullptr )
2987 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction failed." } );
2992 if( m_indiDriver->good() ==
false )
2994 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver failed to open FIFOs." } );
2995 delete m_indiDriver;
2996 m_indiDriver =
nullptr;
3001 m_indiDriver->activate();
3002 log<indidriver_start>();
3004 sendGetPropertySetList(
true );
3009template <
bool _useINDI>
3013 if( !all && m_allDefsReceived )
3019 while(
it != m_indiSetCallBacks.end() )
3021 if( all ||
it->second.m_defReceived ==
false )
3023 if(
it->second.property )
3027 m_indiDriver->sendGetProperties( *(
it->second.property ) );
3029 catch(
const std::exception &e )
3031 log<software_error>( { __FILE__,
3033 "exception caught from sendGetProperties for " +
3034 it->second.property->getName() +
": " + e.what() } );
3038 it->second.m_defReceived =
false;
3044 m_allDefsReceived =
false;
3046 m_allDefsReceived =
true;
3049template <
bool _useINDI>
3052 handleSetProperty(
ipRecv );
3055template <
bool _useINDI>
3060 if( m_indiDriver ==
nullptr )
3064 if(
ipRecv.hasValidDevice() &&
ipRecv.getDevice() != m_indiDriver->getName() )
3070 if( !
ipRecv.hasValidName() )
3074 while(
it != m_indiNewCallBacks.end() )
3076 if(
it->second.property )
3080 m_indiDriver->sendDefProperty( *(
it->second.property ) );
3082 catch(
const std::exception &e )
3084 log<software_error>( { __FILE__,
3086 "exception caught from sendDefProperty for " +
3087 it->second.property->getName() +
": " + e.what() } );
3094 sendGetPropertySetList(
true );
3100 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3106 if( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property )
3110 m_indiDriver->sendDefProperty( *( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property ) );
3112 catch(
const std::exception &e )
3114 log<software_error>( { __FILE__,
3116 "exception caught from sendDefProperty for " +
3117 m_indiNewCallBacks[
ipRecv.createUniqueKey()].property->getName() +
": " +
3124template <
bool _useINDI>
3129 if( m_indiDriver ==
nullptr )
3133 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3135 log<software_debug>( { __FILE__, __LINE__,
"invalid NewProperty request for " +
ipRecv.createUniqueKey() } );
3139 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiNewCallBacks[
ipRecv.createUniqueKey()].callBack;
3142 callBack(
this,
ipRecv );
3144 log<software_debug>( { __FILE__, __LINE__,
"NewProperty callback null for " +
ipRecv.createUniqueKey() } );
3149template <
bool _useINDI>
3154 if( m_indiDriver ==
nullptr )
3157 std::string key =
ipRecv.createUniqueKey();
3160 if( m_indiSetCallBacks.count( key ) > 0 )
3162 m_indiSetCallBacks[key].m_defReceived =
true;
3165 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiSetCallBacks[key].callBack;
3167 callBack(
this,
ipRecv );
3179template <
bool _useINDI>
3180template <
typename T>
3182 const std::string &el,
3184 pcf::IndiProperty::PropertyStateType ipState )
3195template <
bool _useINDI>
3197 const std::string &el,
3199 pcf::IndiProperty::PropertyStateType ipState )
3201 updateIfChanged<std::string>( p, el, std::string( newVal ), ipState );
3204template <
bool _useINDI>
3206 const std::string &el,
3207 const pcf::IndiElement::SwitchStateType &newVal,
3208 pcf::IndiProperty::PropertyStateType ipState )
3219template <
bool _useINDI>
3220template <
typename T>
3222 const std::string &el,
3223 const std::vector<T> &newVals,
3224 pcf::IndiProperty::PropertyStateType ipState )
3232 std::vector<std::string> descriptors( newVals.size(), el );
3233 for(
size_t index = 0; index < newVals.size(); ++index )
3235 descriptors[index] += std::to_string( index );
3240template <
bool _useINDI>
3241template <
typename T>
3243 const std::vector<std::string> &els,
3244 const std::vector<T> &newVals,
3245 pcf::IndiProperty::PropertyStateType newState )
3256template <
bool _useINDI>
3257template <
typename T>
3259 const std::vector<const char *> &els,
3260 const std::vector<T> &newVals,
3261 pcf::IndiProperty::PropertyStateType newState )
3276template <
bool _useINDI>
3277template <
typename T>
3280 const pcf::IndiProperty &remoteProperty,
3283 if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey() )
3288 if( !( remoteProperty.find(
"target" ) || remoteProperty.find(
"current" ) ) )
3295 if( remoteProperty.find(
"target" ) )
3297 localTarget = remoteProperty[
"target"].get<T>();
3303 if( remoteProperty.find(
"current" ) )
3305 localTarget = remoteProperty[
"current"].get<T>();
3329template <
typename T>
3332 return pcf::IndiProperty::Unknown;
3347template <
bool _useINDI>
3348template <
typename T>
3356 log<software_error>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3359 pcf::IndiProperty ipToSend = ipSend;
3363 ipToSend[el].setValue( newVal );
3367 log<software_error>(
3368 { __FILE__, __LINE__,
"Exception caught setting " + ipSend.createUniqueKey() +
"." + el } );
3372 int rv = m_indiDriver->sendNewProperty( ipToSend );
3375 log<software_error>( { __FILE__, __LINE__ } );
3382template <
bool _useINDI>
3390 return log<
software_error, -1>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3393 if( m_indiDriver->sendNewProperty( ipSend ) < 0 )
3401template <
bool _useINDI>
3407 pcf::IndiProperty ipSend( pcf::IndiProperty::Switch );
3411 ipSend.setDevice( device );
3412 ipSend.setName( property );
3413 ipSend.add( pcf::IndiElement(
"toggle" ) );
3415 catch( std::exception &e )
3417 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"exception: " ) + e.what() } );
3420 if( onoff ==
false )
3422 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::Off );
3426 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::On );
3429 if( sendNewProperty( ipSend ) < 0 )
3432 { __FILE__, __LINE__,
"sendNewProperty failed for " + device +
"." +
property } );
3438template <
bool _useINDI>
3444template <
bool _useINDI>
3448 if(
ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey() )
3450 return log<
software_error, -1>( { __FILE__, __LINE__,
"wrong indi property received" } );
3453 if( !
ipRecv.find(
"request" ) )
3456 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
3459 updateSwitchIfChanged( m_indiP_clearFSMAlert,
"request", pcf::IndiElement::Off,
INDI_IDLE );
3465template <
bool _useINDI>
3471template <
bool _useINDI>
3477template <
bool _useINDI>
3480 if( !m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0 )
3485 if( m_powerOnCounter * m_loopPause > ( (
double)m_powerOnWait ) * 1e9 )
3496template <
bool _useINDI>
3499 if( !m_powerMgtEnabled )
3502 return m_powerState;
3505template <
bool _useINDI>
3508 if( !m_powerMgtEnabled )
3511 return m_powerTargetState;
3514template <
bool _useINDI>
3522 ps =
ipRecv[m_powerElement].get<std::string>();
3528 else if( ps ==
"Off" )
3540 ps =
ipRecv[m_powerTargetElement].get<std::string>();
3544 m_powerTargetState = 1;
3546 else if( ps ==
"Off" )
3548 m_powerTargetState = 0;
3552 m_powerTargetState = -1;
3559template <
bool _useINDI>
3562 return m_configName;
3565template <
bool _useINDI>
3571template <
bool _useINDI>
3574 return m_driverInName;
3577template <
bool _useINDI>
3580 return m_driverOutName;
3583template <
bool _useINDI>
3586 return m_driverCtrlName;
3610#define XWCAPP_THREAD_START( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart ) \
3611 if( threadStart( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, this, thrdStart ) < 0 ) \
3613 log<software_error>( { __FILE__, __LINE__, "error from threadStart for " #thrdName } ); \
3624#define XWCAPP_THREAD_CHECK( thrdSt, thrdName ) \
3627 if( pthread_tryjoin_np( thrdSt.native_handle(), 0 ) == 0 ) \
3629 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3635 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3644#define XWCAPP_THREAD_STOP( thrdSt ) \
3645 if( thrdSt.joinable() ) \
3647 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.
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.
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.
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)
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::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.
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.
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.
std::string secretsPath
Path to the secrets directory, where passwords, etc, are stored.
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.
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.
std::string sysPath
The path to the system directory, for PID file, 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.
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 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 MagAOXPath
The base path of the MagAO-X system.
std::string m_driverCtrlName
Full path name of the INDI driver control FIFO.
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 the the cpuset mount point.
#define MAGAOX_env_path
Environment variable setting the MagAO-X path.
#define MAGAOX_env_calib
Environment variable setting the relative calib path.
#define MAGAOX_env_config
Environment variable setting the relative config path.
#define MAGAOX_env_cpuset
Environment variable setting the relative calib 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.