10 #include "../../libMagAOX/libMagAOX.hpp"
11 #include "../../magaox_git_version.h"
230 const std::string &arg);
232 int getCom(std::string &resp,
233 const std::string &com,
300 config.add(
"stage.naxes",
"",
"stage.naxes", argType::Required,
"stage",
"naxes",
false,
"int",
"Number of axes. Default is 2. Max is 3.");
302 config.add(
"stage.homePos1",
"",
"stage.homePos1", argType::Required,
"stage",
"homePos1",
false,
"float",
"Home position of axis 1. Default is 17.5.");
303 config.add(
"stage.homePos2",
"",
"stage.homePos2", argType::Required,
"stage",
"homePos2",
false,
"float",
"Home position of axis 2. Default is 17.5.");
304 config.add(
"stage.homePos3",
"",
"stage.homePos3", argType::Required,
"stage",
"homePos3",
false,
"float",
"Home position of axis 3. Default is 17.5.");
306 config.add(
"dm.calibRelDir",
"",
"dm.calibRelDir", argType::Required,
"dm",
"calibRelDir",
false,
"string",
"Used to find the default calib directory.");
328 config(
m_naxes,
"stage.naxes");
447 elevatedPrivileges elPriv(
this);
468 std::stringstream logs;
470 log<text_log>(logs.str());
503 if (!(atz == 0 || atz == 1))
554 log<software_error>({__FILE__, __LINE__});
560 mx::sys::milliSleep(1);
575 log<software_error>({__FILE__, __LINE__});
582 mx::sys::milliSleep(1);
587 log<software_error>({__FILE__, __LINE__});
592 mx::sys::milliSleep(1);
607 log<software_error>({__FILE__, __LINE__});
612 mx::sys::milliSleep(1);
621 log<software_error>({__FILE__, __LINE__});
626 mx::sys::milliSleep(1);
641 log<software_error>({__FILE__, __LINE__});
646 mx::sys::milliSleep(1);
694 if ((st = resp.find(
"E-727.3SDA")) == std::string::npos)
698 m_ctrl = mx::ioutils::removeWhiteSpace(resp.substr(st));
701 std::string resp1, resp2, resp3;
707 resp1 = mx::ioutils::removeWhiteSpace(resp1);
713 resp2 = mx::ioutils::removeWhiteSpace(resp2);
719 resp3 = mx::ioutils::removeWhiteSpace(resp3);
727 if (resp1.find(
"1=S-335") == 0 && resp2.find(
"2=S-335") == 0 && resp3.find(
"3=0") == 0)
733 else if (resp1.find(
"1=S-325") == 0 && resp2.find(
"2=S-325") == 0 && resp3.find(
"3=S-325") == 0)
756 log<text_log>(
"Found " +
m_stage +
" with " + std::to_string(
m_naxes) +
" axes");
767 if ((st = resp.find(
'=')) == std::string::npos)
772 m_min1 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
773 log<text_log>(
"axis 1 min: " + std::to_string(
m_min1));
780 if ((st = resp.find(
'=')) == std::string::npos)
785 m_max1 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
786 log<text_log>(
"axis 1 max: " + std::to_string(
m_max1));
793 if ((st = resp.find(
'=')) == std::string::npos)
798 m_min2 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
799 log<text_log>(
"axis 2 min: " + std::to_string(
m_min2));
806 if ((st = resp.find(
'=')) == std::string::npos)
811 m_max2 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
812 log<text_log>(
"axis 2 max: " + std::to_string(
m_max2));
821 if ((st = resp.find(
'=')) == std::string::npos)
826 m_min3 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
827 log<text_log>(
"axis 3 min: " + std::to_string(
m_min3));
834 if ((st = resp.find(
'=')) == std::string::npos)
839 m_max3 = mx::ioutils::convertFromString<float>(resp.substr(st + 1));
840 log<text_log>(
"axis 3 max: " + std::to_string(
m_max3));
949 if (
getCom(resp,
"ATZ?", axis) < 0)
951 log<software_error>({__FILE__, __LINE__});
957 size_t st = resp.find(
'=');
958 if (st == std::string::npos || st > resp.size() - 2)
960 log<software_error>({__FILE__, __LINE__,
"error parsing response"});
965 return mx::ioutils::convertFromString<double>(resp.substr(st));
980 log<text_log>(
"home_1 requested but not in correct homing state",
logPrio::LOG_ERROR);
997 log<text_log>(
"commenced homing x");
1014 log<text_log>(
"home_2 requested but not in correct homing state",
logPrio::LOG_ERROR);
1030 log<text_log>(
"commenced homing y");
1047 log<text_log>(
"home_3 requested but not in correct homing state",
logPrio::LOG_ERROR);
1063 log<text_log>(
"commenced homing z");
1081 log<text_log>(
"finishInit requested but not in correct homing state",
logPrio::LOG_ERROR);
1086 log<text_log>(
"finishInit requested but not in correct homing state",
logPrio::LOG_ERROR);
1098 mx::sys::milliSleep(2000);
1108 mx::sys::milliSleep(2000);
1120 mx::sys::milliSleep(2000);
1162 mx::sys::milliSleep(250);
1174 mx::sys::milliSleep(250);
1188 mx::sys::milliSleep(1000);
1193 std::string com =
"MOV 1 " + std::to_string(
m_homePos1) +
"\n";
1205 com =
"MOV 2 " + std::to_string(
m_homePos2) +
"\n";
1219 com =
"MOV 3 " + std::to_string(
m_homePos3) +
"\n";
1243 log<text_log>(
"DM zeroed");
1251 float pos1 = ((
float *)curr_src)[0];
1252 float pos2 = ((
float *)curr_src)[1];
1256 pos3 = ((
float *)curr_src)[2];
1261 if ((rv =
move_1(pos1)) < 0)
1264 if ((rv =
move_2(pos2)) < 0)
1268 if ((rv =
move_3(pos3)) < 0)
1320 const std::string &com,
1323 std::string sendcom = com;
1324 if (axis == 1 || axis == 2)
1327 sendcom += std::to_string(axis);
1346 if (
getCom(resp,
"POS?", n) < 0)
1348 log<software_error>({__FILE__, __LINE__});
1352 size_t st = resp.find(
'=');
1353 if (st == std::string::npos || st > resp.size() - 2)
1355 log<software_error>({__FILE__, __LINE__,
"error parsing response"});
1359 pos = mx::ioutils::convertFromString<double>(resp.substr(st));
1368 if (
getCom(resp,
"SVA?", n) < 0)
1370 log<software_error>({__FILE__, __LINE__});
1374 size_t st = resp.find(
'=');
1375 if (st == std::string::npos || st > resp.size() - 2)
1377 log<software_error>({__FILE__, __LINE__,
"error parsing response"});
1381 sva = mx::ioutils::convertFromString<double>(resp.substr(st));
1408 if (absPos < m_min1 || absPos >
m_max1)
1416 std::string com =
"MOV 1 " + std::to_string(absPos) +
"\n";
1432 if (absPos < m_min2 || absPos >
m_max2)
1439 std::string com =
"MOV 2 " + std::to_string(absPos) +
"\n";
1455 return log<
software_error, -1>({__FILE__, __LINE__,
"tried to move axis 3 but we don't have that"});
1460 if (absPos < m_min3 || absPos >
m_max3)
1467 std::string com =
"MOV 3 " + std::to_string(absPos) +
"\n";
1480 (
const pcf::IndiProperty &
ipRecv)
1482 if (
ipRecv.createUniqueKey() == m_indiP_pos1.createUniqueKey())
1484 float current = -999999, target = -999999;
1486 if (
ipRecv.find(
"current"))
1488 current =
ipRecv[
"current"].get<
float>();
1491 if (
ipRecv.find(
"target"))
1493 target =
ipRecv[
"target"].get<
float>();
1496 if (target == -999999)
1499 if (target == -999999)
1505 std::unique_lock<std::mutex>
lock(m_indiMutex);
1509 updateFlat(target, m_pos2Set, m_pos3Set);
1511 return move_1(target);
1515 return updateFlat(target, m_pos2Set, m_pos3Set);
1522 (
const pcf::IndiProperty &
ipRecv)
1524 if (
ipRecv.createUniqueKey() == m_indiP_pos2.createUniqueKey())
1526 float current = -999999, target = -999999;
1528 if (
ipRecv.find(
"current"))
1530 current =
ipRecv[
"current"].get<
float>();
1533 if (
ipRecv.find(
"target"))
1535 target =
ipRecv[
"target"].get<
float>();
1538 if (target == -999999)
1541 if (target == -999999)
1547 std::unique_lock<std::mutex>
lock(m_indiMutex);
1550 updateFlat(m_pos1Set, target, m_pos3Set);
1551 return move_2(target);
1555 return updateFlat(m_pos1Set, target, m_pos3Set);
1562 (
const pcf::IndiProperty &
ipRecv)
1565 if (
ipRecv.getName() == m_indiP_pos3.getName())
1567 float current = -999999, target = -999999;
1569 if (
ipRecv.find(
"current"))
1571 current =
ipRecv[
"current"].get<
float>();
1574 if (
ipRecv.find(
"target"))
1576 target =
ipRecv[
"target"].get<
float>();
1579 if (target == -999999)
1582 if (target == -999999)
1588 std::unique_lock<std::mutex>
lock(m_indiMutex);
1592 updateFlat(m_pos1Set, m_pos2Set, target);
1593 return move_3(target);
1597 return updateFlat(m_pos1Set, m_pos2Set, target);
1615 static float pos1Set = std::numeric_limits<float>::max();
1616 static float pos1 = std::numeric_limits<float>::max();
1617 static float sva1 = std::numeric_limits<float>::max();
1618 static float pos2Set = std::numeric_limits<float>::max();
1619 static float pos2 = std::numeric_limits<float>::max();
1620 static float sva2 = std::numeric_limits<float>::max();
1621 static float pos3Set = std::numeric_limits<float>::max();
1622 static float pos3 = std::numeric_limits<float>::max();
1623 static float sva3 = std::numeric_limits<float>::max();
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.
stateCodes::stateCodeT state()
Get the current state code.
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.
std::string m_calibRelDir
The directory relative to the calibPath. Set this before calling dm<derivedT,realT>::loadConfig().
bool m_flatSet
Flag indicating whether the flat command has been set.
bool m_flatLoaded
Flag indicating whether a flat is loaded in memory.
int setFlat(bool update=false)
Send the current flat command to the DM.
mx::improc::eigenImage< float > m_flatCommand
Data storage for the flat command.
The MagAO-X PI 335 Controller Interface.
virtual int appShutdown()
Shutdown the app.
INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos2)
dev::shmimMonitor< pi335Ctrl > shmimMonitorT
int testConnection()
Test the connection to the device.
virtual void loadConfig()
int commandDM(void *curr_src)
Send a command to the DM.
int home()
Start the homing procedure.
int m_naxes
The number of axes, default is 2. Max is 3.
int setCom(const std::string &com)
float m_max1
The maximum value for axis 1.
float m_posTol
The tolerance for reporting a raw position rather than the setpoint.
pcf::IndiProperty m_indiP_pos3
int getSva(float &sva, int n)
Get the open loop control value.
INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos1)
int getPos(float &pos, int n)
int recordTelem(const telem_pi335 *)
float m_homePos1
Home position of axis 1. Default is 17.5.
INDI_NEWCALLBACK_DECL(pi335Ctrl, m_indiP_pos3)
~pi335Ctrl() noexcept
D'tor, declared and defined for noexcept.
friend class pi335Ctrl_test
int home_1()
Begin homing (ATZ) axis 1.
int move_3(float absPos)
Send command to device to move axis 3.
std::string m_stage
The stage connected.
int getCom(std::string &resp, const std::string &com, int axis)
virtual void setupConfig()
float m_max3
The maximum value for axis 3.
int recordPI335(bool force=false)
int setCom(const std::string &com, int axis, const std::string &arg)
int homeState(int axis)
Get the status of homing on an axiz.
int releaseDM()
Release the DM, making it safe to turn off power.
int home_3()
Begin homing (ATZ) axis 3.
int updateFlat(float absPos1, float absPos2, float absPos3)
Update the flat command and propagate.
float m_min2
The minimum value for axis 2.
float m_homePos2
Home position of axis 2. Default is 17.5.
dev::telemeter< pi335Ctrl > telemeterT
float m_min1
The minimum value for axis 1.
std::string m_ctrl
The controller connected.
int home_2()
Begin homing (ATZ) axis 2.
pi335Ctrl()
Default c'tor.
int move_1(float absPos)
Send command to device to move axis 1.
float m_min3
The minimum value for axis 3.
float m_homePos3
Home position of axis 2. Default is 17.5.
virtual int appLogic()
Implementation of the FSM for pi335Ctrl.
int zeroDM()
Zero all commands on the DM.
float m_max2
The maximum value for axis 2.
virtual int appStartup()
Startup function.
pcf::IndiProperty m_indiP_pos2
int setCom(const std::string &com, int axis)
dev::dm< pi335Ctrl, float > dmT
pcf::IndiProperty m_indiP_pos1
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int move_2(float absPos)
Send command to device to move axis 2.
#define DM_SETUP_CONFIG(cfig)
Call dmT::setupConfig with error checking for dm.
#define DM_APP_SHUTDOWN
Call dmT::appShutdown with error checking for dm.
#define DM_LOAD_CONFIG(cfig)
Call dmT::loadConfig with error checking for dm.
#define DM_APP_STARTUP
Call shmimMonitorT::appStartup with error checking for dm.
#define DM_APP_LOGIC
Call dmT::appLogic with error checking for dm.
#define DM_UPDATE_INDI
Call dmT::updateINDI with error checking for dm.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
@ OPERATING
The device is operating, other than homing.
@ NODEVICE
No device exists for the application to control.
@ 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.
@ 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.
@ UNINITIALIZED
The application is unitialized, the default.
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
int ttyWriteRead(std::string &strRead, const std::string &strWrite, const std::string &eot, bool swallowEcho, int fd, int timeoutWrite, int timeoutRead)
Write to a tty on an open file descriptor, then get the result.
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
int ttyWrite(const std::string &buffWrite, int fd, int timeoutWrite)
Write to the tty console indicated by a file descriptor.
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
std::unique_lock< std::mutex > lock(m_indiMutex)
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_NOTICE
A normal but significant condition.
#define SHMIMMONITOR_APP_SHUTDOWN
Call shmimMonitorT::appShutdown with error checking for shmimMonitor.
#define SHMIMMONITOR_APP_LOGIC
Call shmimMonitorT::appLogic with error checking for shmimMonitor.
#define SHMIMMONITOR_APP_STARTUP
Call shmimMonitorT::appStartup with error checking for shmimMonitor.
#define SHMIMMONITOR_UPDATE_INDI
Call shmimMonitorT::updateINDI with error checking for shmimMonitor.
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 base class which saves telemetry.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Software CRITICAL log entry.
Log entry recording the build-time git state.
A simple text log, a string-type log.
A USB device as a TTY device.
std::string m_deviceName
The device path name, e.g. /dev/ttyUSB0.
int m_fileDescrip
The file descriptor.
int connect()
Connect to the device.
int getDeviceName()
Get the device name from udev using the vendor, product, and serial number.
std::string m_idProduct
The product id 4-digit code.
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the USB section.
std::string m_serial
The serial number.
int loadConfig(mx::app::appConfigurator &config)
Load the USB section from an application configurator.
speed_t m_baudRate
The baud rate specification.
std::string m_idVendor
The vendor id 4-digit code.
#define TELEMETER_APP_LOGIC
Call telemeter::appLogic with error checking.
#define TELEMETER_LOAD_CONFIG(cfig)
Call telemeter::loadConfig with error checking.
#define TELEMETER_APP_STARTUP
Call telemeter::appStartup with error checking.
#define TELEMETER_SETUP_CONFIG(cfig)
Call telemeter::setupConfig with error checking.
#define TELEMETER_APP_SHUTDOWN
Call telemeter::appShutdown with error checking.
#define TTY_E_DEVNOTFOUND