8#ifndef zaberLowLevel_hpp
9#define zaberLowLevel_hpp
13#include "../../libMagAOX/libMagAOX.hpp"
14#include "../../magaox_git_version.h"
22#define ZC_CONNECTED ( 0 )
23#define ZC_ERROR ( -1 )
24#define ZC_NOT_CONNECTED ( 10 )
69 std::vector<zaberStage<zaberLowLevel>>
m_stages;
228 if( config.isSetUnused( mx::app::iniFile::makeKey(
sections[
n],
"serial" ) ) )
238 config.configUnused(
tmp, mx::app::iniFile::makeKey(
sections[
n],
"serial" ) );
265 elevatedPrivileges
elPriv(
this );
305 std::string
renum =
"/ renumber";
326 std::string
gss =
"/ get system.serial";
361 std::vector<std::string>
serials;
377 std::vector<std::string>
serials;
443 log<text_log>( std::format(
"stage {} with s/n {} not found in system.",
475 std::string
gss =
"/ get system.serial";
663 : pcf::IndiElement::Off );
665 : pcf::IndiElement::Off );
727 std::stringstream
logs;
886 (
m_stages[
i].knobEnabled() ? pcf::IndiElement::On : pcf::IndiElement::Off ) );
901 (
m_stages[
i].ledEnabled() ? pcf::IndiElement::On : pcf::IndiElement::Off ) );
944 else if(
m_stages[
i].deviceStatus() ==
'I' )
949 "state homing. bug.",
977 elevatedPrivileges
ep(
this );
1076 log<text_log>(
"Recovering from stage communication error by resetting the connection.",
1115 (
m_stages[
i].knobEnabled() ? pcf::IndiElement::On : pcf::IndiElement::Off ) );
1118 (
m_stages[
i].ledEnabled() ? pcf::IndiElement::On : pcf::IndiElement::Off ) );
1151 for(
size_t n = 0; n < m_stages.size(); ++n )
1153 if(
ipRecv.find( m_stages[n].name() ) )
1155 long tgt =
ipRecv[m_stages[n].name()].get<
long>();
1158 if( m_stages[n].deviceAddress() < 1 )
1161 "stage {} with with s/n {} not found in system.", m_stages[n].name(), m_stages[n].serial() ) );
1164 std::lock_guard<std::mutex> guard( m_indiMutex );
1166 if( m_stages[n].moveAbs( m_port, tgt ) < 0 )
1168 return log<
software_error, -1>( {
"error from moveAbs for " + m_stages[n].name() } );
1171 updateIfChanged( m_indiP_tgt_pos, m_stages[n].name(), m_stages[n].tgtPos() );
1172 updateIfChanged( m_indiP_parked, m_stages[n].name(), m_stages[n].parked() );
1173 updateIfChanged( m_indiP_curr_state, m_stages[n].name(), std::string(
"OPERATING" ) );
1186 size_t stageno = std::numeric_limits<size_t>::max();
1190 for(
size_t n = 0; n < m_stages.size(); ++n )
1192 if(
ipRecv.find( m_stages[n].name() ) )
1196 log<software_error>( {
"more than one stage specified in req_home, rejecting request" } );
1200 if( m_stages[n].deviceAddress() < 1 )
1202 return log<
software_error, -1>( std::format(
"stage {} with with "
1205 m_stages[n].serial() ) );
1213 if( !found || stageno >= m_stages.size() )
1215 log<software_error>(
"no valid stage specified in req_home, rejecting request" );
1219 if(
ipRecv[m_stages[stageno].name()].getSwitchState() != pcf::IndiElement::On )
1221 return log<software_warning, 0>( std::format(
"request off for stage {} "
1223 m_stages[stageno].name() ) );
1226 std::lock_guard<std::mutex> guard( m_indiMutex );
1228 if( m_stages[stageno].homing() )
1230 return log<software_warning, 0>( std::format(
"stage {} is already "
1231 "homing in req_home",
1232 m_stages[stageno].name() ) );
1235 if( m_stages[stageno].home( m_port ) < 0 )
1237 return log<
software_error, -1>( std::format(
"error from home for {}", m_stages[stageno].name() ) );
1241 updateIfChanged( m_indiP_parked, m_stages[stageno].name(), m_stages[stageno].parked() );
1242 updateIfChanged( m_indiP_curr_state, m_stages[stageno].name(), std::string(
"HOMING" ) );
1251 if( !
ipRecv.find(
"request" ) )
1256 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
1258 for(
size_t n = 0; n < m_stages.size(); ++n )
1260 if( m_stages[n].deviceAddress() < 1 )
1264 std::lock_guard<std::mutex> guard( m_indiMutex );
1266 if( m_stages[n].homing() )
1271 if( m_stages[n].home( m_port ) < 0 )
1273 return log<
software_error, -1>( {
"error from home for " + m_stages[n].name() } );
1277 updateIfChanged( m_indiP_parked, m_stages[n].name(), m_stages[n].parked() );
1278 updateIfChanged( m_indiP_curr_state, m_stages[n].name(), std::string(
"HOMING" ) );
1290 size_t stageno = std::numeric_limits<size_t>::max();
1294 for(
size_t n = 0; n < m_stages.size(); ++n )
1296 if(
ipRecv.find( m_stages[n].name() ) )
1300 return log<
software_error, -1>(
"more than one stage specified in req_halt, rejecting request" );
1303 if( m_stages[n].deviceAddress() < 1 )
1305 return log<
software_error, -1>( std::format(
"stage {} with with "
1306 "s/n {} not present",
1308 m_stages[n].serial() ) );
1316 if( !found || stageno == std::numeric_limits<size_t>::max() )
1318 return log<
software_error, -1>(
"no valid stage specified in req_halt, rejecting request" );
1321 if(
ipRecv[m_stages[stageno].name()].getSwitchState() != pcf::IndiElement::On )
1323 return log<software_warning, 0>( std::format(
"request off for stage {} "
1325 m_stages[stageno].name() ) );
1328 std::lock_guard<std::mutex> guard( m_indiMutex );
1330 if( m_stages[stageno].stop( m_port ) < 0 )
1332 return log<
software_error, -1>( std::format(
"error from stop for {}", m_stages[stageno].name() ) );
1344 for(
size_t n = 0; n < m_stages.size(); ++n )
1346 if(
ipRecv.find( m_stages[n].name() ) )
1348 if(
ipRecv[m_stages[n].name()].getSwitchState() == pcf::IndiElement::On )
1350 if( m_stages[n].deviceAddress() < 1 )
1352 log<software_error>( std::format(
"stage {} with s/n {} "
1355 m_stages[n].serial() ) );
1359 std::lock_guard<std::mutex> guard( m_indiMutex );
1361 if( m_stages[n].estop( m_port ) < 0 )
1363 log<software_error>( {
"error from estop for " + m_stages[n].name() } );
1377 size_t stageno = std::numeric_limits<size_t>::max();
1381 for(
size_t n = 0; n < m_stages.size(); ++n )
1383 if(
ipRecv.find( m_stages[n].name() ) )
1387 return log<
software_error, -1>(
"more than one stage specified in req_halt, rejecting request" );
1390 if( m_stages[n].deviceAddress() < 1 )
1392 return log<
software_error, -1>( std::format(
"stage {} with with "
1393 "s/n {} not present",
1395 m_stages[n].serial() ) );
1403 if( !found || stageno == std::numeric_limits<size_t>::max() )
1405 return log<
software_error, -1>(
"no valid stage specified in req_knob, rejecting request" );
1408 bool enable_knob =
ipRecv[m_stages[stageno].name()].getSwitchState() == pcf::IndiElement::On;
1410 std::lock_guard<std::mutex> guard( m_indiMutex );
1412 if( m_stages[stageno].enableKnob( m_port, enable_knob ) < 0 )
1414 return log<
software_error, -1>( std::format(
"error from enable knob for {}", m_stages[stageno].name() ) );
1425 size_t stageno = std::numeric_limits<size_t>::max();
1429 for(
size_t n = 0; n < m_stages.size(); ++n )
1431 if(
ipRecv.find( m_stages[n].name() ) )
1435 return log<
software_error, -1>(
"more than one stage specified in req_halt, rejecting request" );
1438 if( m_stages[n].deviceAddress() < 1 )
1440 return log<
software_error, -1>( std::format(
"stage {} with with "
1441 "s/n {} not present",
1443 m_stages[n].serial() ) );
1451 if( !found || stageno == std::numeric_limits<size_t>::max() )
1453 return log<
software_error, -1>(
"no valid stage specified in req_led, rejecting request" );
1456 bool enable_led =
ipRecv[m_stages[stageno].name()].getSwitchState() == pcf::IndiElement::On;
1458 std::lock_guard<std::mutex> guard( m_indiMutex );
1460 if( m_stages[stageno].enableLED( m_port, enable_led ) < 0 )
1462 return log<
software_error, -1>( std::format(
"error from enable led for {}", m_stages[stageno].name() ) );
The base-class for XWCTk 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 powerState()
Returns the current power state.
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.
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.
std::mutex m_indiMutex
Mutex for locking INDI communications.
std::string m_sysPath
The path to the system directory, for PID file, etc.
The low-level ASCII-protocol Zaber controller.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_led_enable)
zaberLowLevel()
Default constructor.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_ehalt)
friend class zaberLowLevel_test
int recoverFromError(bool devicePresent)
Recover from an ASCII-transport error without terminating the app.
pcf::IndiProperty m_indiP_req_home
Command a stage to home.
pcf::IndiProperty m_indiP_warn
Whether the stage has existing warnings.
pcf::IndiProperty m_indiP_led_enable
Enable or disable a stage's LED.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_halt)
z_port m_port
Connected ASCII protocol port.
virtual int appLogic()
Execute the main FSM for zaberLowLevel.
pcf::IndiProperty m_indiP_max_pos
Maximum raw position of the stage.
pcf::IndiProperty m_indiP_req_halt
Command a stage to safely halt.
virtual int onPowerOff()
Handle the transition into the powered-off state.
int refreshStageDiscovery()
Refresh discovery on an already-connected ASCII bus.
pcf::IndiProperty m_indiP_tgt_pos
Target raw position of the stage.
pcf::IndiProperty m_indiP_temp
Current temperature of the stage.
int connect()
Connect to the ASCII-protocol stage chain and discover configured devices.
int resetConnection()
Reset the active ASCII connection bookkeeping.
pcf::IndiProperty m_indiP_knob_enable
Enable or disable a stage's potentiometer.
std::unordered_map< std::string, size_t > m_stageSerial
Map from configured serial number to configured stage index.
std::unordered_map< int, size_t > m_stageAddress
Map from ASCII device address to configured stage index.
pcf::IndiProperty m_indiP_curr_state
Current state of the stage.
pcf::IndiProperty m_indiP_parked
Parked state of the stage.
int loadStages(std::string &serialRes)
Apply a parsed system.serial snapshot to the configured stages.
int m_numStages
Number of configured stages.
virtual int whilePowerOff()
Execute the powered-off loop.
virtual void loadConfig()
Load application configuration.
pcf::IndiProperty m_indiP_req_home_all
Command all stages to home.
pcf::IndiProperty m_indiP_curr_pos
Current raw position of the stage.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_tgt_pos)
virtual int appStartup()
Set up the INDI properties and restore retained stage state.
virtual int appShutdown()
Perform any shutdown tasks before exit.
std::vector< zaberStage< zaberLowLevel > > m_stages
Stage helpers in configuration order.
~zaberLowLevel() noexcept
Destructor, declared and defined for noexcept.
std::unordered_map< std::string, size_t > m_stageName
Map from configured stage name to configured stage index.
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_home)
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_req_home_all)
INDI_NEWCALLBACK_DECL(zaberLowLevel, m_indiP_knob_enable)
pcf::IndiProperty m_indiP_lastHomed
Time of last homing for the stage.
pcf::IndiProperty m_indiP_req_ehalt
Command a stage to safely immediately halt.
virtual void setupConfig()
Set up application configuration.
bool m_stageDiscoveryInitialized
Whether the active connection has completed an initial discovery pass.
A class to manage the details of one stage in a Zaber system.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define CREATE_REG_INDI_NEW_REQUESTSWITCH(prop, name)
Create and register a NEW INDI property as a standard request switch, using the standard callback nam...
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
int parseSystemSerial(std::vector< int > &address, std::vector< std::string > &serial, const std::string &response)
Parse the system.serial query.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
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.
@ NODEVICE
No device exists for the application to control.
@ 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.
@ INITIALIZED
The application has been initialized, set just before calling appStartup().
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
Software CRITICAL log entry.
A USB device as a TTY device.
std::string m_deviceName
The device path name, e.g. /dev/ttyUSB0.
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 TTY_E_DEVNOTFOUND
int za_connect(z_port *port, const char *port_name)
int za_receive(z_port port, char *destination, int length)
int za_send(z_port port, const char *command, size_t sMaxSz)
int za_drain(z_port port)
int za_disconnect(z_port port)
Provides a set of functions for interacting with Zaber devices in the ASCII protocol.
MagAOX::app::MagAOXApp< true > MagAOXAppT
A class with details of a single zaber stage.
utilties for working with zaber stages
#define ZUTILS_E_BADSERIAL