7 #ifndef picoMotorCtrl_hpp
8 #define picoMotorCtrl_hpp
12 #include "../../libMagAOX/libMagAOX.hpp"
13 #include "../../magaox_git_version.h"
88 const std::string & n,
193 const pcf::IndiProperty &
ipRecv
211 const pcf::IndiProperty &
ipRecv
248 if(
it->second.m_thread !=
nullptr)
250 if(
it->second.m_thread->joinable())
it->second.m_thread->join();
251 delete it->second.m_thread;
259 config.add(
"device.address",
"",
"device.address", argType::Required,
"device",
"address",
false,
"string",
"The controller IP address.");
260 config.add(
"device.nChannels",
"",
"device.nChannels", argType::Required,
"device",
"nChannels",
false,
"int",
"Number of motoro channels. Default is 4.");
267 #define PICOMOTORCTRL_E_NOMOTORS (-5)
268 #define PICOMOTORCTRL_E_BADCHANNEL (-6)
269 #define PICOMOTORCTRL_E_DUPMOTOR (-7)
270 #define PICOMOTORCTRL_E_INDIREG (-20)
280 std::vector<std::string> sections;
282 _config.unusedSections(sections);
284 if( sections.size() == 0 )
292 for(
size_t i=0;i<sections.size(); ++i)
295 _config.configUnused(channel, mx::app::iniFile::makeKey(sections[i],
"channel" ) );
304 log<text_log>(
"Bad channel specificiation: " + sections[i] +
" " + std::to_string(channel),
logPrio::LOG_CRITICAL);
310 std::pair<channelMapT::iterator, bool> insert =
m_channels.insert(std::pair<std::string, motorChannel>(sections[i],
motorChannel(
this,sections[i],channel)));
312 if(insert.second ==
false)
314 log<text_log>(
"Duplicate motor specificiation: " + sections[i] +
" " + std::to_string(channel),
logPrio::LOG_CRITICAL);
319 _config.configUnused(insert.first->second.m_presetNames, mx::app::iniFile::makeKey(sections[i],
"names" ));
320 _config.configUnused(insert.first->second.m_presetPositions, mx::app::iniFile::makeKey(sections[i],
"positions" ));
323 log<pico_channel>({sections[i], (uint8_t) channel});
359 createStandardIndiNumber(
it->second.m_property,
it->first+
"_pos", std::numeric_limits<posT>::lowest(), std::numeric_limits<posT>::max(),
static_cast<posT>(1),
"%d",
"Position",
it->first);
360 it->second.m_property[
"current"].set(
it->second.m_currCounts);
361 it->second.m_property[
"target"].set(
it->second.m_currCounts);
366 #ifndef PICOMOTORCTRL_TEST_NOLOG
367 log<software_error>({__FILE__,__LINE__});
372 if(
it->second.m_presetNames.size() > 0)
376 log<software_critical>({__FILE__, __LINE__});
381 log<software_error>({__FILE__,__LINE__});
391 struct sigaction act;
395 act.sa_flags = SA_SIGINFO;
400 if( sigaction(SIGUSR1, &act, 0) < 0 )
402 std::string logss =
"Setting handler for SIGUSR1 failed. Errno says: ";
403 logss += strerror(errno);
405 log<software_error>({__FILE__, __LINE__, errno, 0, logss});
480 log<software_error>({__FILE__, __LINE__,
"wrong response to IDN query"});
500 std::string query = std::to_string(
it->second.m_channel) +
"QM?";
524 log<text_log>(
"No motor connected on channel " + std::to_string(
it->second.m_channel) +
" [" +
it->second.m_name +
"]",
logPrio::LOG_CRITICAL);
528 else if (moType != 3)
531 log<text_log>(
"Wrong motor type connected on channel " + std::to_string(
it->second.m_channel) +
" [" +
it->second.m_name +
"]",
logPrio::LOG_CRITICAL);
572 log<software_error>({__FILE__, __LINE__,
"wrong response to IDN query"});
579 bool anymoving =
false;
586 std::string query = std::to_string(
it->second.m_channel) +
"MD?";
610 it->second.m_moving =
true;
614 it->second.m_moving =
false;
617 if(
it->second.m_moving ==
false &&
it->second.m_doMove ==
true)
619 it->second.m_currCounts =
it->second.m_property[
"target"].get<
long>();
620 log<text_log>(
"moved " +
it->second.m_name +
" to " + std::to_string(
it->second.m_currCounts) +
" counts");
621 it->second.m_doMove =
false;
635 for(
size_t n=0; n <
it->second.m_presetNames.size(); ++n)
637 bool changed =
false;
638 if(
it->second.m_currCounts ==
it->second.m_presetPositions[n])
640 if(
it->second.m_indiP_presetName[
it->second.m_presetNames[n]] == pcf::IndiElement::Off) changed =
true;
641 it->second.m_indiP_presetName[
it->second.m_presetNames[n]] = pcf::IndiElement::On;
645 if(
it->second.m_indiP_presetName[
it->second.m_presetNames[n]] == pcf::IndiElement::On) changed =
true;
646 it->second.m_indiP_presetName[
it->second.m_presetNames[n]] = pcf::IndiElement::Off;
649 if(changed)
m_indiDriver->sendSetProperty(
it->second.m_indiP_presetName);
654 log<software_error>({__FILE__, __LINE__});
660 log<software_error>({__FILE__, __LINE__});
686 if(
it->second.m_thread->joinable())
688 pthread_kill(
it->second.m_thread->native_handle(), SIGUSR1);
691 it->second.m_thread->join();
706 std::string statusDir =
sysPath;
710 std::string fileName = statusDir +
"/" + chName;
713 posIn.open( fileName );
717 log<text_log>(
"no position file for " + chName +
" found. initializing to 0.");
726 log<text_log>(
"initializing " + chName +
" to " + std::to_string(pos));
735 std::string statusDir =
sysPath;
739 std::string fileName = statusDir +
"/" + chName;
743 std::ofstream posOut;
744 posOut.open( fileName );
748 log<text_log>(
"could not open counts file for " + chName +
" -- can not store position.",
logPrio::LOG_ERROR);
787 log<text_log>(
"moving " + mc->
m_name +
" by " + std::to_string(dr) +
" counts");
789 std::string comm = std::to_string(mc->
m_channel) +
"PR" + std::to_string(dr);
816 const pcf::IndiProperty &
ipRecv
826 std::string propName =
ipRecv.getName();
827 size_t nend = propName.rfind(
"_pos");
829 if(nend == std::string::npos)
831 log<software_error>({__FILE__, __LINE__,
"Channel without _pos received"});
835 std::string chName = propName.substr(0, nend);
840 log<software_error>({__FILE__, __LINE__,
"Unknown channel name received"});
844 if(
it->second.m_doMove ==
true)
861 it->second.m_doMove=
true;
863 pthread_kill(
it->second.m_thread->native_handle(), SIGUSR1);
869 const pcf::IndiProperty &
ipRecv
881 log<software_error>({__FILE__, __LINE__,
"Unknown channel name received"});
885 if(
it->second.m_doMove ==
true)
894 for(i=0; i<
it->second.m_presetNames.size(); ++i)
896 if(!
ipRecv.find(
it->second.m_presetNames[i]))
continue;
898 if(
ipRecv[
it->second.m_presetNames[i]].getSwitchState() == pcf::IndiElement::On)
906 counts =
it->second.m_presetPositions[i];
907 std::cerr <<
"selected: " <<
it->second.m_presetNames[i] <<
" " << counts <<
"\n";
915 it->second.m_property[
"target"].set(counts);
918 it->second.m_doMove=
true;
920 pthread_kill(
it->second.m_thread->native_handle(), SIGUSR1);
937 static std::vector<int64_t> lastpos(
m_nChannels, std::numeric_limits<long>::max());
939 bool changed =
false;
942 if(
it->second.m_currCounts != lastpos[
it->second.m_channel-1]) changed =
true;
945 if( changed || force )
949 lastpos[
it->second.m_channel-1] =
it->second.m_currCounts;
952 telem<telem_pico>(lastpos);
Internal class to manage setuid privilege escalation with RAII.
The base-class for MagAO-X applications.
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.
std::string m_configName
The name of the configuration file (minus .conf).
stateCodes::stateCodeT state()
Get the current state code.
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 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.
int powerState()
Returns the current power state.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
int powerStateTarget()
Returns the target power state.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
bool powerOnWaitElapsed()
This method tests whether the power on wait time has elapsed.
std::mutex m_indiMutex
Mutex for locking INDI communications.
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.
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 indiTargetUpdate(pcf::IndiProperty &localProperty, T &localTarget, const pcf::IndiProperty &remoteProperty, bool setBusy=true)
Get the target element value from an new property.
bool m_powerMgtEnabled
Flag controls whether power mgt is used. Set this in the constructor of a derived app....
int newCallBack_picopos(const pcf::IndiProperty &ipRecv)
The handler function for relative position requests, called by the static callback.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int m_nChannels
The number of motor channels total on the hardware. Number of attached motors inferred from config.
virtual int onPowerOff()
Implementation of the on-power-off FSM logic.
tty::telnetConn m_telnetConn
The telnet connection manager.
static int st_newCallBack_presetName(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for position presets.
std::map< std::string, motorChannel > channelMapT
~picoMotorCtrl() noexcept
D'tor, declared and defined for noexcept.
static void channelThreadStart(motorChannel *mc)
Channel thread starter function.
posT readChannelCounts(const std::string &chName)
Read the current channel counts from disk at startup.
std::string m_deviceAddr
The device address.
virtual int appStartup()
Startup functions.
virtual void loadConfig()
load the configuration system results (called by MagAOXApp::setup())
virtual int appLogic()
Implementation of the FSM.
int recordPico(bool force=false)
dev::telemeter< picoMotorCtrl > telemeterT
virtual int whilePowerOff()
Implementation of the while-powered-off FSM.
int recordTelem(const telem_pico *)
static int st_newCallBack_picopos(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for relative position requests.
picoMotorCtrl()
Default c'tor.
std::string m_devicePort
The device port.
virtual int appShutdown()
Do any needed shutdown tasks.
std::mutex m_telnetMutex
Mutex for locking telnet communications.
int writeChannelCounts(const std::string &chName, posT counts)
virtual void setupConfig()
Setup the configuration system (called by MagAOXApp::setup())
void channelThreadExec(motorChannel *mc)
Channel thread execution function.
int newCallBack_presetName(const pcf::IndiProperty &ipRecv)
The handler function for position presets, called by the static callback.
channelMapT m_channels
Map of motor names to channel.
std::vector< pcf::IndiProperty > m_indiP_counts
@ OPERATING
The device is operating, other than homing.
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
@ 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.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
const pcf::IndiProperty & ipRecv
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
#define PICOMOTORCTRL_E_INDIREG
#define PICOMOTORCTRL_E_NOMOTORS
#define PICOMOTORCTRL_E_BADCHANNEL
#define PICOMOTORCTRL_E_DUPMOTOR
An input/output capable device.
unsigned m_writeTimeout
The write timeout [msec].
int loadConfig(mx::app::appConfigurator &config)
Load the device section from an application configurator.
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the device section.
unsigned m_readTimeout
The read timeout [msec].
A device which saves telemetry.
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int appLogic()
Perform telemeter application logic.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
int appStartup()
Starts the telemetry log thread.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
picoMotorCtrl * m_parent
A pointer to this for thread starting.
int m_channel
The number of this channel, where the motor is plugged in.
std::vector< std::string > m_presetNames
bool m_doMove
Flag indicating that a move is requested.
pid_t m_threadID
The ID of the thread.
pcf::IndiProperty m_threadProp
The property to hold the thread details.
posT m_currCounts
The current counts, the cumulative position.
bool m_threadInit
Thread initialization flag.
pcf::IndiProperty m_indiP_presetName
std::vector< posT > m_presetPositions
pcf::IndiProperty m_property
motorChannel(picoMotorCtrl *p)
std::string m_name
The name of this channel, from the config section.
bool m_moving
Flag to indicate that we are actually moving.
std::thread * m_thread
Thread for managing this channel. A pointer to allow copying, but must be deleted in d'tor of parent.
motorChannel(picoMotorCtrl *p, const std::string &n, int ch)
Log entry recording CPU temperatures.
A Telnet connection manager, wrapping libtelnet.
int write(const std::string &buffWrite, int timeoutWrite)
Write to a telnet connection.
std::string m_strRead
The accumulated string read from the device.
int noLogin()
Set flags as if we're logged in, used when device doesn't require it.
std::string m_prompt
The device's prompt, used for detecting end of transmission.
int read(const std::string &eot, int timeoutRead, bool clear=true)
Read from a telnet connection, until end-of-transmission string is read.
int connect(const std::string &host, const std::string &port)
Connect to the device.