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;
111template <
bool _useINDI = true>
318 const std::
string &value,
367 bool m_elevated{
false };
491 template <
class thisPtr,
class Function>
496 pcf::IndiProperty &
thProp,
498 const std::string &
cpuset,
591 pcf::IndiProperty *
property{ 0 };
643 const std::string &
label =
"",
645 const std::string &
group =
""
656 const std::string &
elName,
659 const std::string &
elLabel =
""
667 template <
typename T>
669 const std::string &name,
676 const std::string &format,
679 const std::string &
label =
"",
681 const std::string &
group =
""
703 const std::string &name,
704 const std::string &
label =
"",
706 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::vector<std::string> &elements,
734 const std::string &
label =
"",
736 const std::string &
group =
""
747 const std::string &name,
748 const std::vector<std::string> &elements,
750 const std::string &
label =
"",
752 const std::string &
group =
""
777 const pcf::IndiProperty::Type &
propType,
779 const pcf::IndiProperty::PropertyPermType &
propPerm,
782 const pcf::IndiProperty::PropertyStateType &
propState
795 int ( * )(
void *,
const pcf::IndiProperty & )
807 pcf::IndiProperty &prop,
809 const pcf::IndiProperty::Type &
propType,
810 const pcf::IndiProperty::PropertyPermType &
propPerm,
811 const pcf::IndiProperty::PropertyStateType &
propState,
812 int ( * )(
void *,
const pcf::IndiProperty & )
823 pcf::IndiProperty &prop,
825 const pcf::IndiProperty::Type &
propType,
826 const pcf::IndiProperty::PropertyPermType &
propPerm,
827 const pcf::IndiProperty::PropertyStateType &
propState,
828 const pcf::IndiProperty::SwitchRuleType &
propRule,
829 int ( * )(
void *,
const pcf::IndiProperty & )
840 pcf::IndiProperty &prop,
843 int ( * )(
void *,
const pcf::IndiProperty & )
904 template <
typename T>
906 const std::string &el,
908 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
923 const std::string &el,
925 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
934 const std::string &el,
935 const pcf::IndiElement::SwitchStateType &
newVal,
936 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok );
950 template <
typename T>
952 pcf::IndiProperty &p,
953 const std::string &el,
955 pcf::IndiProperty::PropertyStateType
ipState = pcf::IndiProperty::Ok
967 template <
typename T>
969 const std::vector<std::string> &
els,
971 pcf::IndiProperty::PropertyStateType
newState =
972 pcf::IndiProperty::Ok
975 template <
typename T>
977 const std::vector<const char *> &
els,
979 pcf::IndiProperty::PropertyStateType
newState =
980 pcf::IndiProperty::Ok
988 template <
typename T>
1001 template <
typename T>
1003 const std::string &el,
1020 const std::string &property,
1037 const pcf::IndiProperty &
ipRecv
1172template <
bool _useINDI>
1176template <
bool _useINDI>
1179template <
bool _useINDI>
1182 if( m_self !=
nullptr )
1184 std::cerr <<
"Attempt to instantiate 2nd MagAOXApp. Exiting immediately.\n";
1191 getresuid( &m_euidReal, &m_euidCalled, &m_suid );
1194 m_log.parent(
this );
1197 config.m_sources =
true;
1198 config.configLog = configLog;
1210 if( MXLIB_UNCOMP_REPO_MODIFIED )
1216 log<git_state>(
git_state::messageT(
"mxlib", MXLIB_UNCOMP_CURRENT_SHA1, MXLIB_UNCOMP_REPO_MODIFIED ), gl );
1219template <
bool _useINDI>
1223 delete m_indiDriver;
1224 m_log.parent(
nullptr );
1229template <
bool _useINDI>
1235template <
bool _useINDI>
1244 MagAOXPath = tmpstr;
1257 m_configDir = MagAOXPath +
"/" + tmpstr;
1258 m_configPathGlobal = m_configDir +
"/magaox.conf";
1266 m_calibDir = MagAOXPath +
"/" + tmpstr;
1271 m_log.logPath( tmpstr );
1279 secretsPath = tmpstr;
1285 m_cpusetPath = tmpstr;
1289 if( m_configBase !=
"" )
1292 m_configPathUser = m_configDir +
"/" + m_configBase +
".conf";
1304 "The name of the application and its device name in INDI (if used), specifies the config file in the XWC config directory." );
1306 config.parseCommandLine( argc, argv,
"name" );
1307 config( m_configName,
"name" );
1309 if( m_configName ==
"" )
1311 m_configName = mx::ioutils::pathStem( invokedName );
1314 log<text_log>(
"Configuration Error: Application name (-n --name) not set." );
1320 m_configPathLocal = m_configDir +
"/" + m_configName +
".conf";
1323 if( registerIndiPropertyNew(
1324 m_indiP_state,
"fsm", pcf::IndiProperty::Text, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle, 0 ) <
1327 log<software_error>( { __FILE__, __LINE__,
"failed to register read only fsm_state property" } );
1330 m_indiP_state.add( pcf::IndiElement(
"state" ) );
1332 createStandardIndiRequestSw( m_indiP_clearFSMAlert,
"fsm_clear_alert",
"Clear FSM Alert",
"FSM" );
1333 if( registerIndiPropertyNew( m_indiP_clearFSMAlert, st_newCallBack_clearFSMAlert ) < 0 )
1335 log<software_error>( { __FILE__, __LINE__,
"failed to register new fsm_alert property" } );
1341template <
bool _useINDI>
1345 config.add(
"config.validate",
1353 "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." );
1356 config.add(
"loopPause",
1364 "The main loop pause time in ns" );
1367 "ignore_git",
"",
"ignore-git", argType::True,
"",
"",
false,
"bool",
"set to true to ignore git status to prevent the fsm_alert" );
1370 m_log.setupConfig( config );
1372 if( m_powerMgtEnabled )
1374 if( _useINDI ==
false )
1377 log<software_critical>( { __FILE__, __LINE__,
"power management is enabled but we are not using INDI" } );
1382 config.add(
"power.device",
1390 "Device controlling power for this app's device (INDI name)." );
1391 config.add(
"power.channel",
1399 "Channel on device for this app's device (INDI name)." );
1400 config.add(
"power.element",
1408 "INDI power state element name. Default is \"state\", only need to specify if different." );
1409 config.add(
"power.targetElement",
1411 "power.targetElement",
1417 "INDI power target element name. Default is \"target\", only need to specify if different." );
1418 config.add(
"power.powerOnWait",
1420 "power.powerOnWait",
1426 "Time after power-on to wait before continuing [sec]. Default is 0 sec, max is 3600 sec." );
1430template <
bool _useINDI>
1435 config( ig,
"ignore_git" );
1437 if( !ig && m_gitAlert )
1439 m_stateAlert =
true;
1443 if(config.isSet(
"config.validate"))
1445 m_configOnly =
true;
1449 m_log.logName( m_configName );
1450 m_log.loadConfig( config );
1453 config( m_loopPause,
"loopPause" );
1456 if( m_powerMgtEnabled )
1458 config( m_powerDevice,
"power.device" );
1459 config( m_powerChannel,
"power.channel" );
1460 config( m_powerElement,
"power.element" );
1461 config( m_powerTargetElement,
"power.targetElement" );
1463 if( m_powerDevice !=
"" && m_powerChannel !=
"" )
1465 log<text_log>(
"enabling power management: " + m_powerDevice +
"." + m_powerChannel +
"." + m_powerElement +
1466 "/" + m_powerTargetElement );
1467 if( registerIndiPropertySet(
1468 m_indiP_powerChannel, m_powerDevice, m_powerChannel,
INDI_SETCALLBACK( m_indiP_powerChannel ) ) <
1471 log<software_error>( { __FILE__, __LINE__,
"failed to register set property" } );
1480 config( m_powerOnWait,
"power.powerOnWait" );
1481 if( m_powerOnWait > 3600 )
1483 log<text_log>(
"powerOnWait longer than 1 hour. Setting to 0.",
logPrio::LOG_ERROR );
1488template <
bool _useINDI>
1493 for(
auto it = config.m_targets.begin();
it != config.m_targets.end(); ++
it )
1495 if(
it->second.used ==
false )
1497 std::string
msg =
it->second.name;
1498 if( config.m_sources &&
it->second.sources.size() > 0 )
1500 msg +=
" [" +
it->second.sources[0] +
"]";
1508 if( config.m_unusedConfigs.size() > 0 )
1510 for(
auto it = config.m_unusedConfigs.begin();
it != config.m_unusedConfigs.end(); ++
it )
1512 if(
it->second.used ==
true )
1517 std::string
msg =
it->second.name;
1518 if( config.m_sources &&
it->second.sources.size() > 0 )
1520 msg +=
" [" +
it->second.sources[0] +
"]";
1529 if( config.nonOptions.size() > 0 )
1531 for(
size_t n = 0; n < config.nonOptions.size(); ++n )
1533 log<text_log>(
"Unrecognized command line argument: " + config.nonOptions[n],
logPrio::LOG_CRITICAL );
1540 if(m_shutdown ==
true)
1542 std::cerr <<
"\nThere were configuration errors.\n\n";
1546 std::cerr <<
"\nConfiguration is valid.\n\n";
1549 else if(m_shutdown ==
true)
1555template <
bool _useINDI>
1561#ifndef XWC_DISABLE_USER_CHECK
1562 struct stat logstat;
1564 if( stat( m_log.logPath().c_str(), &logstat ) < 0 )
1567 std::cerr <<
"\nCRITICAL: Can not stat the log path.\n\n";
1571 if( logstat.st_uid != geteuid() )
1574 std::cerr <<
"\nCRITICAL: You are running this app as the wrong user.\n\n";
1587 std::cerr <<
"\nCRITICAL: Failed to lock PID. Exiting.\n\n";
1596 m_log.logThreadStart();
1600 while( m_log.logThreadRunning() ==
false && w < 20 )
1603 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( 100000000 ) );
1607 if( m_log.logThreadRunning() ==
false )
1612 std::cerr <<
"\nCRITICAL: log thread not running. Exiting.\n\n";
1616 if( unlockPID() < 0 )
1618 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1627 if( m_shutdown == 0 )
1629 if( setSigTermHandler() < 0 )
1633 log<software_critical>( { __FILE__, __LINE__,
"error from setSigTermHandler()" } );
1637 if( unlockPID() < 0 )
1639 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1649 if( m_shutdown == 0 )
1653 if( appStartup() < 0 )
1657 log<software_critical>( { __FILE__, __LINE__,
"error from appStartup()" } );
1661 if( unlockPID() < 0 )
1663 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1671 if( m_useINDI && m_shutdown == 0 )
1673 if( startINDI() < 0 )
1677 log<software_critical>( { __FILE__, __LINE__,
"INDI failed to start." } );
1682 if( appShutdown() < 0 )
1684 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1687 if( unlockPID() < 0 )
1689 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1697 if( m_powerMgtEnabled && m_shutdown == 0 )
1700 while( m_powerState < 0 && !m_shutdown )
1703 if( m_powerState < 0 )
1705 if( !stateLogged() )
1706 log<text_log>(
"waiting for power state" );
1717 if( m_powerState > 0 )
1723 m_powerOnCounter = 0;
1725 if( onPowerOff() < 0 )
1727 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1742 while( m_shutdown == 0 )
1745 if( m_powerMgtEnabled )
1749 if( m_powerState == 1 )
1751 m_powerOnCounter = 0;
1757 if( m_powerState == 0 )
1760 if( onPowerOff() < 0 )
1762 log<software_error>( { __FILE__, __LINE__,
"error from onPowerOff()" } );
1772 if( !m_powerMgtEnabled || m_powerState > 0 )
1774 if( appLogic() < 0 )
1776 log<software_error>( { __FILE__, __LINE__,
"error from appLogic()" } );
1781 else if( m_powerState == 0 )
1783 if( whilePowerOff() < 0 )
1785 log<software_error>( { __FILE__, __LINE__,
"error from whilePowerOff()" } );
1800 sendGetPropertySetList(
false );
1808 if( m_shutdown == 0 )
1810 std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>( m_loopPause ) );
1814 if( appShutdown() < 0 )
1816 log<software_error>( { __FILE__, __LINE__,
"error from appShutdown()" } );
1822 if( m_indiDriver !=
nullptr )
1824 pcf::IndiProperty ipSend;
1825 ipSend.setDevice( m_configName );
1828 m_indiDriver->sendDelProperty( ipSend );
1830 catch(
const std::exception &e )
1832 log<software_error>(
1833 { __FILE__, __LINE__, std::string(
"exception caught from sendDelProperty: " ) + e.what() } );
1836 m_indiDriver->quitProcess();
1837 m_indiDriver->deactivate();
1838 log<indidriver_stop>();
1841 if( unlockPID() < 0 )
1843 log<software_error>( { __FILE__, __LINE__,
"error from unlockPID()" } );
1850template <
bool _useINDI>
1851template <
typename logT,
int retval>
1854 m_log.template log<logT>(
msg, level );
1858template <
bool _useINDI>
1859template <
typename logT,
int retval>
1862 m_log.template log<logT>( level );
1866template <
bool _useINDI>
1871 logStdFormat( std::cerr, b );
1877 state( m_state,
true );
1880 if( _useINDI && m_indiDriver )
1882 pcf::IndiProperty
msg;
1883 msg.setDevice( m_configName );
1885 std::stringstream logstdf;
1886 logMinStdFormat( logstdf, b );
1888 msg.setMessage( logstdf.str() );
1894 tv.tv_usec = (
long int)( ( (
double)ts.
time_ns ) / 1e3 );
1896 msg.setTimeStamp( pcf::TimeStamp( tv ) );
1900 m_indiDriver->sendMessage(
msg );
1902 catch(
const std::exception &e )
1904 log<software_error>(
1905 { __FILE__, __LINE__, std::string(
"exception caught from sendMessage: " ) + e.what() } );
1910template <
bool _useINDI>
1913 const std::string &value,
1914 const std::string &source )
1916 m_log.template log<config_log>( { name, code, value, source } );
1919template <
bool _useINDI>
1922 struct sigaction act;
1926 act.sa_flags = SA_SIGINFO;
1927 sigemptyset( &set );
1931 if( sigaction( SIGTERM, &act, 0 ) < 0 )
1933 std::string logss =
"Setting handler for SIGTERM failed. Errno says: ";
1934 logss += strerror( errno );
1936 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
1942 if( sigaction( SIGQUIT, &act, 0 ) < 0 )
1944 std::string logss =
"Setting handler for SIGQUIT failed. Errno says: ";
1945 logss += strerror( errno );
1947 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
1953 if( sigaction( SIGINT, &act, 0 ) < 0 )
1955 std::string logss =
"Setting handler for SIGINT failed. Errno says: ";
1956 logss += strerror( errno );
1958 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
1963 log<text_log>(
"Installed SIGTERM/SIGQUIT/SIGINT signal handler.",
logPrio::LOG_DEBUG );
1968template <
bool _useINDI>
1971 m_self->handlerSigTerm( signum, siginf, ucont );
1974template <
bool _useINDI>
1976 siginfo_t *siginf __attribute__( ( unused ) ),
1977 void *ucont __attribute__( ( unused ) ) )
1981 std::string signame;
1985 signame =
"SIGTERM";
1991 signame =
"SIGQUIT";
1997 std::string logss =
"Caught signal ";
1999 logss +=
". Shutting down.";
2001 std::cerr <<
"\n" << logss << std::endl;
2002 log<text_log>( logss );
2006void sigUsr1Handler(
int signum, siginfo_t *siginf,
void *ucont );
2008template <
bool _useINDI>
2014 std::string logss =
"Setting effective user id to euidCalled (";
2015 logss += mx::ioutils::convertToString<int>( m_euidCalled );
2016 logss +=
") failed. Errno says: ";
2017 logss += strerror( errno );
2019 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2027template <
bool _useINDI>
2033 std::string logss =
"Setting effective user id to euidReal (";
2034 logss += mx::ioutils::convertToString<int>( m_euidReal );
2035 logss +=
") failed. Errno says: ";
2036 logss += strerror( errno );
2038 log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
2046template <
bool _useINDI>
2051 std::string statusDir = sysPath;
2059 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2061 if( errno != EEXIST )
2063 std::stringstream logss;
2064 logss <<
"Failed to create root of statusDir (" << statusDir <<
"). Errno says: " << strerror( errno );
2065 log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
2071 statusDir += m_configName;
2073 pidFileName = statusDir +
"/pid";
2078 if( mkdir( statusDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
2080 if( errno != EEXIST )
2082 std::stringstream logss;
2083 logss <<
"Failed to create statusDir (" << statusDir <<
"). Errno says: " << strerror( errno );
2084 log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
2090 std::ifstream pidIn;
2091 pidIn.open( pidFileName );
2101 std::stringstream procN;
2102 procN <<
"/proc/" << testPid <<
"/cmdline";
2104 std::ifstream procIn;
2105 std::string pidCmdLine;
2109 procIn.open( procN.str() );
2111 procIn >> pidCmdLine;
2116 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"exception caught testing /proc/pid" } );
2124 size_t invokedPos = pidCmdLine.find( invokedName );
2127 size_t configPos = std::string::npos;
2128 if( invokedPos != std::string::npos )
2129 configPos = pidCmdLine.find( m_configName );
2132 if( invokedPos != std::string::npos && configPos != std::string::npos )
2135 std::stringstream logss;
2136 logss <<
"PID already locked (" << testPid <<
"). Time to die.";
2137 std::cerr << logss.str() << std::endl;
2152 std::ofstream pidOut;
2153 pidOut.open( pidFileName );
2155 if( !pidOut.good() )
2157 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"could not open pid file for writing." } );
2166 std::stringstream logss;
2167 logss <<
"PID (" << m_pid <<
") locked.";
2168 log<text_log>( logss.str() );
2180template <
bool _useINDI>
2188 if( ::remove( pidFileName.c_str() ) < 0 )
2190 log<software_error>(
2191 { __FILE__, __LINE__, errno, 0, std::string(
"Failed to remove PID file: " ) + strerror( errno ) } );
2196 std::stringstream logss;
2197 logss <<
"PID (" << m_pid <<
") unlocked.";
2198 log<text_log>( logss.str() );
2203template <
bool _useINDI>
2204template <
class thisPtr,
class Function>
2208 pcf::IndiProperty &thProp,
2210 const std::string &cpuset,
2211 const std::string &thrdName,
2213 Function &&thrdStart )
2221 thrd = std::thread( thrdStart, thrdThis );
2223 catch(
const std::exception &e )
2225 log<software_error>(
2226 { __FILE__, __LINE__, std::string(
"Exception on " + thrdName +
" thread start: " ) + e.what() } );
2231 log<software_error>( { __FILE__, __LINE__,
"Unkown exception on " + thrdName +
" thread start" } );
2235 if( !thrd.joinable() )
2237 log<software_error>( { __FILE__, __LINE__, thrdName +
" thread did not start" } );
2249 sp.sched_priority = thrdPrio;
2263 rv = pthread_setschedparam( thrd.native_handle(), SCHED_OTHER, &sp );
2268 log<software_error>(
2272 "Setting " + thrdName +
" thread scheduler priority to " + std::to_string( thrdPrio ) +
" failed." } );
2276 log<text_log>( thrdName +
" thread scheduler priority set to " + std::to_string( thrdPrio ) );
2282 for(
int i = 0; i < 10; ++i )
2284 mx::sys::milliSleep( 100 );
2292 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"tpid for " + thrdName +
" not set." } );
2296 log<text_log>( thrdName +
" thread pid is " + std::to_string( tpid ) );
2300 thProp = pcf::IndiProperty( pcf::IndiProperty::Number );
2301 thProp.setDevice( configName() );
2302 thProp.setName( std::string(
"th-" ) + thrdName );
2303 thProp.setPerm( pcf::IndiProperty::ReadOnly );
2304 thProp.setState( pcf::IndiProperty::Idle );
2305 thProp.add( pcf::IndiElement(
"pid" ) );
2306 thProp[
"pid"] = tpid;
2307 thProp.add( pcf::IndiElement(
"prio" ) );
2308 thProp[
"prio"] = thrdPrio;
2309 registerIndiPropertyReadOnly( thProp );
2315 std::string cpuFile = m_cpusetPath;
2316 cpuFile +=
"/" + cpuset;
2317 cpuFile +=
"/tasks";
2318 int wfd = open( cpuFile.c_str(), O_WRONLY );
2321 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error from open for " + cpuFile } );
2325 snprintf( pids,
sizeof( pids ),
"%d", tpid );
2327 int w = write( wfd, pids, strnlen( pids,
sizeof( pids ) ) );
2328 if( w != (
int)strnlen( pids,
sizeof(pids) ) )
2330 return log<
software_error, -1>( { __FILE__, __LINE__, errno,
"error on write" } );
2344template <
bool _useINDI>
2350template <
bool _useINDI>
2362 log<state_change>( { m_state, s }, lvl );
2368 if( m_stateAlert != stateAlert && stateAlert ==
true )
2370 m_stateAlert = stateAlert;
2375 std::unique_lock<std::mutex>
lock( m_indiMutex,
2379 if(
lock.owns_lock() )
2382 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2385 if( m_stateAlert ==
true )
2408template <
bool _useINDI>
2411 if( m_stateLogged > 0 )
2414 return m_stateLogged - 1;
2423template <
bool _useINDI>
2426 if( m_stateAlert ==
false )
2428 m_stateAlert =
false;
2431 pcf::IndiProperty::PropertyStateType stst =
INDI_IDLE;
2453template <
bool _useINDI>
2455 const std::string &propName,
2456 const std::string &label,
2457 const std::string &group )
2459 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2460 prop.setDevice( configName() );
2461 prop.setName( propName );
2462 prop.setPerm( pcf::IndiProperty::ReadWrite );
2463 prop.setState( pcf::IndiProperty::Idle );
2464 prop.add( pcf::IndiElement(
"current" ) );
2465 prop.add( pcf::IndiElement(
"target" ) );
2470 prop.setLabel( label );
2475 prop.setGroup( group );
2481template <
bool _useINDI>
2483 const std::string &propName,
2484 const std::string &elName,
2485 const std::string &propLabel,
2486 const std::string &propGroup,
2487 const std::string &elLabel )
2489 prop = pcf::IndiProperty( pcf::IndiProperty::Text );
2490 prop.setDevice( configName() );
2491 prop.setName( propName );
2492 prop.setPerm( pcf::IndiProperty::ReadOnly );
2493 prop.setState( pcf::IndiProperty::Idle );
2496 if( propLabel !=
"" )
2498 prop.setLabel( propLabel );
2501 if( propGroup !=
"" )
2503 prop.setGroup( propGroup );
2506 prop.add( pcf::IndiElement( elName ) );
2510 prop[elName].setLabel( elLabel );
2516template <
bool _useINDI>
2517template <
typename T>
2519 const std::string &name,
2523 const std::string &format,
2524 const std::string &label,
2525 const std::string &group )
2527 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2528 prop.setDevice( configName() );
2529 prop.setName( name );
2530 prop.setPerm( pcf::IndiProperty::ReadWrite );
2531 prop.setState( pcf::IndiProperty::Idle );
2532 prop.add( pcf::IndiElement(
"current" ) );
2533 prop[
"current"].setMin( min );
2534 prop[
"current"].setMax( max );
2535 prop[
"current"].setStep( step );
2538 prop[
"current"].setFormat( format );
2541 prop.add( pcf::IndiElement(
"target" ) );
2542 prop[
"target"].setMin( min );
2543 prop[
"target"].setMax( max );
2544 prop[
"target"].setStep( step );
2547 prop[
"target"].setFormat( format );
2553 prop.setLabel( label );
2558 prop.setGroup( group );
2564template <
bool _useINDI>
2566 const std::string &propName,
2567 const std::string &propLabel,
2568 const std::string &propGroup )
2570 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
2571 prop.setDevice( configName() );
2572 prop.setName( propName );
2573 prop.setPerm( pcf::IndiProperty::ReadOnly );
2574 prop.setState( pcf::IndiProperty::Idle );
2577 if( propLabel !=
"" )
2579 prop.setLabel( propLabel );
2582 if( propGroup !=
"" )
2584 prop.setGroup( propGroup );
2590template <
bool _useINDI>
2592 const std::string &name,
2593 const std::string &label,
2594 const std::string &group )
2596 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2597 prop.setDevice( configName() );
2598 prop.setName( name );
2599 prop.setPerm( pcf::IndiProperty::ReadWrite );
2600 prop.setState( pcf::IndiProperty::Idle );
2601 prop.setRule( pcf::IndiProperty::AtMostOne );
2604 prop.add( pcf::IndiElement(
"toggle", pcf::IndiElement::Off ) );
2609 prop.setLabel( label );
2614 prop.setGroup( group );
2620template <
bool _useINDI>
2622 const std::string &name,
2623 const std::string &label,
2624 const std::string &group )
2626 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2627 prop.setDevice( configName() );
2628 prop.setName( name );
2629 prop.setPerm( pcf::IndiProperty::ReadWrite );
2630 prop.setState( pcf::IndiProperty::Idle );
2631 prop.setRule( pcf::IndiProperty::AtMostOne );
2634 prop.add( pcf::IndiElement(
"request", pcf::IndiElement::Off ) );
2639 prop.setLabel( label );
2644 prop.setGroup( group );
2650template <
bool _useINDI>
2652 const std::string &name,
2653 const std::vector<std::string> &elements,
2654 const std::vector<std::string> &elementLabels,
2655 const std::string &label,
2656 const std::string &group )
2658 if( elements.size() == 0 )
2660 return log<
software_error, -1>( { __FILE__, __LINE__,
"elements vector has zero size" } );
2663 prop = pcf::IndiProperty( pcf::IndiProperty::Switch );
2664 prop.setDevice( configName() );
2665 prop.setName( name );
2666 prop.setPerm( pcf::IndiProperty::ReadWrite );
2667 prop.setState( pcf::IndiProperty::Idle );
2668 prop.setRule( pcf::IndiProperty::OneOfMany );
2671 for(
size_t n = 0; n < elements.size(); ++n )
2673 pcf::IndiElement elem = pcf::IndiElement( elements[n], pcf::IndiElement::Off );
2674 elem.setLabel( elementLabels[n] );
2681 prop.setLabel( label );
2686 prop.setGroup( group );
2692template <
bool _useINDI>
2694 const std::string &name,
2695 const std::vector<std::string> &elements,
2696 const std::string &label,
2697 const std::string &group )
2699 return createStandardIndiSelectionSw( prop, name, elements, elements, label, group );
2702template <
bool _useINDI>
2709 m_indiNewCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, nullptr } ) );
2713 if( !result.second )
2716 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2719 catch( std::exception &e )
2721 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2725 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2731template <
bool _useINDI>
2733 const std::string &propName,
2734 const pcf::IndiProperty::Type &
propType,
2735 const pcf::IndiProperty::PropertyPermType &propPerm,
2736 const pcf::IndiProperty::PropertyStateType &propState )
2741 prop = pcf::IndiProperty(
propType );
2742 prop.setDevice( m_configName );
2743 prop.setName( propName );
2744 prop.setPerm( propPerm );
2745 prop.setState( propState );
2751 if( !result.second )
2754 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2757 catch( std::exception &e )
2759 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2763 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2768template <
bool _useINDI>
2770 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2777 callBackInsertResult result =
2778 m_indiNewCallBacks.insert( callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
2780 if( !result.second )
2783 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2786 catch( std::exception &e )
2788 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2792 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2798template <
bool _useINDI>
2800 const std::string &propName,
2801 const pcf::IndiProperty::Type &
propType,
2802 const pcf::IndiProperty::PropertyPermType &propPerm,
2803 const pcf::IndiProperty::PropertyStateType &propState,
2804 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2809 prop = pcf::IndiProperty(
propType );
2810 prop.setDevice( m_configName );
2811 prop.setName( propName );
2812 prop.setPerm( propPerm );
2813 prop.setState( propState );
2815 return registerIndiPropertyNew( prop, callBack );
2818template <
bool _useINDI>
2820 const std::string &propName,
2821 const pcf::IndiProperty::Type &
propType,
2822 const pcf::IndiProperty::PropertyPermType &propPerm,
2823 const pcf::IndiProperty::PropertyStateType &propState,
2824 const pcf::IndiProperty::SwitchRuleType &propRule,
2825 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2830 prop = pcf::IndiProperty(
propType );
2831 prop.setDevice( m_configName );
2832 prop.setName( propName );
2833 prop.setPerm( propPerm );
2834 prop.setState( propState );
2835 prop.setRule( propRule );
2836 return registerIndiPropertyNew( prop, callBack );
2839template <
bool _useINDI>
2841 const std::string &devName,
2842 const std::string &propName,
2843 int ( *callBack )(
void *,
const pcf::IndiProperty &
ipRecv ) )
2848 prop = pcf::IndiProperty();
2849 prop.setDevice( devName );
2850 prop.setName( propName );
2853 m_indiSetCallBacks.insert(
callBackValueType( prop.createUniqueKey(), { &prop, callBack } ) );
2857 if( !result.second )
2860 { __FILE__, __LINE__,
"failed to insert INDI property: " + prop.createUniqueKey() } );
2863 catch( std::exception &e )
2865 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"Exception caught: " ) + e.what() } );
2869 return log<
software_error, -1>( { __FILE__, __LINE__,
"Unknown exception caught." } );
2875template <
bool _useINDI>
2883 driverFIFOPath +=
"/";
2886 m_driverInName = driverFIFOPath +
"/" + configName() +
".in";
2887 m_driverOutName = driverFIFOPath +
"/" + configName() +
".out";
2888 m_driverCtrlName = driverFIFOPath +
"/" + configName() +
".ctrl";
2894 mode_t prev = umask( 0 );
2897 if( mkfifo( m_driverInName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
2899 if( errno != EEXIST )
2902 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
2909 if( mkfifo( m_driverOutName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) != 0 )
2911 if( errno != EEXIST )
2915 log<software_critical>( { __FILE__, __LINE__, errno, 0,
"mkfifo failed" } );
2922 if( mkfifo( m_driverCtrlName.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" } );
2939template <
bool _useINDI>
2946 if( createINDIFIFOS() < 0 )
2954 if( m_indiDriver !=
nullptr )
2956 m_indiDriver->quitProcess();
2957 m_indiDriver->deactivate();
2958 log<indidriver_stop>();
2959 delete m_indiDriver;
2960 m_indiDriver =
nullptr;
2967 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction exception." } );
2972 if( m_indiDriver ==
nullptr )
2974 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver construction failed." } );
2979 if( m_indiDriver->good() ==
false )
2981 log<software_critical>( { __FILE__, __LINE__, 0, 0,
"INDI Driver failed to open FIFOs." } );
2982 delete m_indiDriver;
2983 m_indiDriver =
nullptr;
2988 m_indiDriver->activate();
2989 log<indidriver_start>();
2991 sendGetPropertySetList(
true );
2996template <
bool _useINDI>
3000 if( !all && m_allDefsReceived )
3006 while(
it != m_indiSetCallBacks.end() )
3008 if( all ||
it->second.m_defReceived ==
false )
3010 if(
it->second.property )
3014 m_indiDriver->sendGetProperties( *(
it->second.property ) );
3016 catch(
const std::exception &e )
3018 log<software_error>( { __FILE__,
3020 "exception caught from sendGetProperties for " +
3021 it->second.property->getName() +
": " + e.what() } );
3025 it->second.m_defReceived =
false;
3031 m_allDefsReceived =
false;
3033 m_allDefsReceived =
true;
3036template <
bool _useINDI>
3039 handleSetProperty(
ipRecv );
3042template <
bool _useINDI>
3047 if( m_indiDriver ==
nullptr )
3051 if(
ipRecv.hasValidDevice() &&
ipRecv.getDevice() != m_indiDriver->getName() )
3057 if( !
ipRecv.hasValidName() )
3061 while(
it != m_indiNewCallBacks.end() )
3063 if(
it->second.property )
3067 m_indiDriver->sendDefProperty( *(
it->second.property ) );
3069 catch(
const std::exception &e )
3071 log<software_error>( { __FILE__,
3073 "exception caught from sendDefProperty for " +
3074 it->second.property->getName() +
": " + e.what() } );
3081 sendGetPropertySetList(
true );
3087 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3093 if( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property )
3097 m_indiDriver->sendDefProperty( *( m_indiNewCallBacks[
ipRecv.createUniqueKey()].property ) );
3099 catch(
const std::exception &e )
3101 log<software_error>( { __FILE__,
3103 "exception caught from sendDefProperty for " +
3104 m_indiNewCallBacks[
ipRecv.createUniqueKey()].property->getName() +
": " +
3111template <
bool _useINDI>
3116 if( m_indiDriver ==
nullptr )
3120 if( m_indiNewCallBacks.count(
ipRecv.createUniqueKey() ) == 0 )
3122 log<software_debug>( { __FILE__, __LINE__,
"invalid NewProperty request for " +
ipRecv.createUniqueKey() } );
3126 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiNewCallBacks[
ipRecv.createUniqueKey()].callBack;
3129 callBack(
this,
ipRecv );
3131 log<software_debug>( { __FILE__, __LINE__,
"NewProperty callback null for " +
ipRecv.createUniqueKey() } );
3136template <
bool _useINDI>
3141 if( m_indiDriver ==
nullptr )
3144 std::string key =
ipRecv.createUniqueKey();
3147 if( m_indiSetCallBacks.count( key ) > 0 )
3149 m_indiSetCallBacks[key].m_defReceived =
true;
3152 int ( *callBack )(
void *,
const pcf::IndiProperty & ) = m_indiSetCallBacks[key].callBack;
3154 callBack(
this,
ipRecv );
3166template <
bool _useINDI>
3167template <
typename T>
3169 const std::string &el,
3171 pcf::IndiProperty::PropertyStateType ipState )
3182template <
bool _useINDI>
3184 const std::string &el,
3186 pcf::IndiProperty::PropertyStateType ipState )
3188 updateIfChanged<std::string>( p, el, std::string( newVal ), ipState );
3191template <
bool _useINDI>
3193 const std::string &el,
3194 const pcf::IndiElement::SwitchStateType &newVal,
3195 pcf::IndiProperty::PropertyStateType ipState )
3206template <
bool _useINDI>
3207template <
typename T>
3209 const std::string &el,
3210 const std::vector<T> &newVals,
3211 pcf::IndiProperty::PropertyStateType ipState )
3219 std::vector<std::string> descriptors( newVals.size(), el );
3220 for(
size_t index = 0; index < newVals.size(); ++index )
3222 descriptors[index] += std::to_string( index );
3227template <
bool _useINDI>
3228template <
typename T>
3230 const std::vector<std::string> &els,
3231 const std::vector<T> &newVals,
3232 pcf::IndiProperty::PropertyStateType newState )
3243template <
bool _useINDI>
3244template <
typename T>
3246 const std::vector<const char *> &els,
3247 const std::vector<T> &newVals,
3248 pcf::IndiProperty::PropertyStateType newState )
3263template <
bool _useINDI>
3264template <
typename T>
3267 const pcf::IndiProperty &remoteProperty,
3270 if( remoteProperty.createUniqueKey() != localProperty.createUniqueKey() )
3275 if( !( remoteProperty.find(
"target" ) || remoteProperty.find(
"current" ) ) )
3282 if( remoteProperty.find(
"target" ) )
3284 localTarget = remoteProperty[
"target"].get<T>();
3290 if( remoteProperty.find(
"current" ) )
3292 localTarget = remoteProperty[
"current"].get<T>();
3316template <
typename T>
3319 return pcf::IndiProperty::Unknown;
3334template <
bool _useINDI>
3335template <
typename T>
3343 log<software_error>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3346 pcf::IndiProperty ipToSend = ipSend;
3350 ipToSend[el].setValue( newVal );
3354 log<software_error>(
3355 { __FILE__, __LINE__,
"Exception caught setting " + ipSend.createUniqueKey() +
"." + el } );
3359 int rv = m_indiDriver->sendNewProperty( ipToSend );
3362 log<software_error>( { __FILE__, __LINE__ } );
3369template <
bool _useINDI>
3377 return log<
software_error, -1>( { __FILE__, __LINE__,
"INDI communications not initialized." } );
3380 if( m_indiDriver->sendNewProperty( ipSend ) < 0 )
3388template <
bool _useINDI>
3394 pcf::IndiProperty ipSend( pcf::IndiProperty::Switch );
3398 ipSend.setDevice( device );
3399 ipSend.setName( property );
3400 ipSend.add( pcf::IndiElement(
"toggle" ) );
3402 catch( std::exception &e )
3404 return log<
software_error, -1>( { __FILE__, __LINE__, std::string(
"exception: " ) + e.what() } );
3407 if( onoff ==
false )
3409 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::Off );
3413 ipSend[
"toggle"].setSwitchState( pcf::IndiElement::On );
3416 if( sendNewProperty( ipSend ) < 0 )
3419 { __FILE__, __LINE__,
"sendNewProperty failed for " + device +
"." +
property } );
3425template <
bool _useINDI>
3431template <
bool _useINDI>
3435 if(
ipRecv.createUniqueKey() != m_indiP_clearFSMAlert.createUniqueKey() )
3437 return log<
software_error, -1>( { __FILE__, __LINE__,
"wrong indi property received" } );
3440 if( !
ipRecv.find(
"request" ) )
3443 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
3446 updateSwitchIfChanged( m_indiP_clearFSMAlert,
"request", pcf::IndiElement::Off,
INDI_IDLE );
3452template <
bool _useINDI>
3458template <
bool _useINDI>
3464template <
bool _useINDI>
3467 if( !m_powerMgtEnabled || m_powerOnWait == 0 || m_powerOnCounter < 0 )
3472 if( m_powerOnCounter * m_loopPause > ( (
double)m_powerOnWait ) * 1e9 )
3483template <
bool _useINDI>
3486 if( !m_powerMgtEnabled )
3489 return m_powerState;
3492template <
bool _useINDI>
3495 if( !m_powerMgtEnabled )
3498 return m_powerTargetState;
3501template <
bool _useINDI>
3509 ps =
ipRecv[m_powerElement].get<std::string>();
3515 else if( ps ==
"Off" )
3527 ps =
ipRecv[m_powerTargetElement].get<std::string>();
3531 m_powerTargetState = 1;
3533 else if( ps ==
"Off" )
3535 m_powerTargetState = 0;
3539 m_powerTargetState = -1;
3546template <
bool _useINDI>
3549 return m_configName;
3552template <
bool _useINDI>
3558template <
bool _useINDI>
3561 return m_driverInName;
3564template <
bool _useINDI>
3567 return m_driverOutName;
3570template <
bool _useINDI>
3573 return m_driverCtrlName;
3597#define XWCAPP_THREAD_START( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart ) \
3598 if( threadStart( thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, this, thrdStart ) < 0 ) \
3600 log<software_error>( { __FILE__, __LINE__, "error from threadStart for " #thrdName } ); \
3611#define XWCAPP_THREAD_CHECK( thrdSt, thrdName ) \
3614 if( pthread_tryjoin_np( thrdSt.native_handle(), 0 ) == 0 ) \
3616 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3622 log<software_error>( { __FILE__, __LINE__, #thrdName " thread has exited" } ); \
3631#define XWCAPP_THREAD_STOP( thrdSt ) \
3632 if( thrdSt.joinable() ) \
3634 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.