7 #ifndef closedLoopIndi_hpp
8 #define closedLoopIndi_hpp
11 #include "../../libMagAOX/libMagAOX.hpp"
12 #include "../../magaox_git_version.h"
177 config.add(
"input.device",
"",
"input.device", argType::Required,
"input",
"device",
false,
"string",
"The device with the input disturbances and frame counter.");
178 config.add(
"input.property",
"",
"input.property", argType::Required,
"input",
"property",
false,
"string",
"The property with the input disturbances and counter.");
179 config.add(
"input.elements",
"",
"input.elements", argType::Required,
"input",
"elements",
false,
"vector<string>",
" The elements with the input disturbances. Must be two, defaults are 'x' and 'y'.");
180 config.add(
"input.counterElement",
"",
"input.counterElement", argType::Required,
"input",
"counterElement",
false,
"string",
"The element with the frame counter, a monotonically increasing integer. Default is 'counter'.");
182 config.add(
"input.references",
"",
"input.references", argType::Required,
"input",
"references",
false,
"vector<float>",
"The reference values for the input disturbances.");
184 config.add(
"ctrl.devices",
"",
"ctrl.devices", argType::Required,
"ctrl",
"devices",
false,
"string",
"Device names of the controller(s). If only one, it's used for both properties. Max two.");
185 config.add(
"ctrl.properties",
"",
"ctrl.properties", argType::Required,
"ctrl",
"properties",
false,
"string",
"Properties of the ctrl device(s) to which to give the commands. Must specify two.");
186 config.add(
"ctrl.currents",
"",
"ctrl.currents", argType::Required,
"ctrl",
"currents",
false,
"vector<string>",
"current elements of the properties on which to base the commands. Must specify 0 or 2. Default is 'current'");
187 config.add(
"ctrl.targets",
"",
"ctrl.targets", argType::Required,
"ctrl",
"targets",
false,
"vector<string>",
"target elements of the properties to which to send the commands. Must specify 0 or 2. Default is 'target'.");
188 config.add(
"ctrl.operatingOK",
"",
"ctrl.operatingOK", argType::Required,
"ctrl",
"operatingOK",
false,
"bool",
"If true then it's ok for the ctrl device to be OPERATING when a command is sent. Default is false.");
191 config.add(
"loop.intMat00",
"",
"loop.intMat00", argType::Required,
"loop",
"intMat00",
false,
"float",
"element (0,0) of the interaction matrix. Default is 1.");
192 config.add(
"loop.intMat01",
"",
"loop.intMat01", argType::Required,
"loop",
"intMat01",
false,
"float",
"element (0,1) of the interaction matrix. Default is 0.");
193 config.add(
"loop.intMat10",
"",
"loop.intMat10", argType::Required,
"loop",
"intMat10",
false,
"float",
"element (1,0) of the interaction matrix. Default is 0.");
194 config.add(
"loop.intMat11",
"",
"loop.intMat11", argType::Required,
"loop",
"intMat11",
false,
"float",
"element (1,1) of the interaction matrix. Default is 1.");
196 config.add(
"loop.gain",
"",
"loop.gain", argType::Required,
"loop",
"gain",
false,
"float",
"default global loop gain.");
197 config.add(
"loop.gains",
"",
"loop.gains", argType::Required,
"loop",
"gains",
false,
"vector<float>",
"default loop gains. If single number, it is applied to all axes.");
198 config.add(
"loop.upstream",
"",
"loop.upstream", argType::Required,
"loop",
"upstream",
false,
"string",
"Upstream loop device name. This loop will open, and optionally close, with the upstream loop. Default none.");
199 config.add(
"loop.upstreamProperty",
"",
"loop.upstreamProperty", argType::Required,
"loop",
"upstreamProperty",
false,
"string",
"Property of upstream loop device to follow. Must be a toggle. Default is loop_state.");
213 return log<
software_error, -1>({__FILE__, __LINE__,
"no input device specified"});
219 return log<
software_error, -1>({__FILE__, __LINE__,
"no input property specified"});
225 return log<
software_error, -1>({__FILE__, __LINE__,
"must specify only two input.elements"});
228 std::vector<float> refs({0,0});
229 _config(refs,
"input.references");
233 return log<
software_error, -1>({__FILE__, __LINE__,
"input.references must have 2 elements"});
253 return log<
software_error, -1>({__FILE__, __LINE__,
"must specify two ctrl.devices"});
259 return log<
software_error, -1>({__FILE__, __LINE__,
"must specify two ctrl.properties"});
265 return log<
software_error, -1>({__FILE__, __LINE__,
"must specify two ctrl.targets"});
271 return log<
software_error, -1>({__FILE__, __LINE__,
"must specify two ctrl.currents"});
277 return log<
software_error, -1>({__FILE__, __LINE__,
"must specify two ctrl.targets"});
285 _config(im00,
"loop.intMat00");
286 _config(im01,
"loop.intMat01");
287 _config(im10,
"loop.intMat10");
288 _config(im11,
"loop.intMat11");
333 log<text_log>(
"Setting loop.gains gains to be same size as ctrl.Targets",
logPrio::LOG_NOTICE);
337 return log<
software_error, -1>({__FILE__, __LINE__,
"ctrl.Targets and loop.gains are not the same size"});
460 std::vector<float> commands;
488 pcf::IndiProperty ip(pcf::IndiProperty::Number);
507 if( indiTargetUpdate( m_indiP_reference0, target,
ipRecv,
true) < 0)
512 m_references(0,0) = target;
514 updateIfChanged(m_indiP_reference0, std::vector<std::string>({
"current",
"target"}), std::vector<float>({m_references(0,0), m_references(0,0)}));
516 log<text_log>(
"set reference0 to " + std::to_string(m_references(0,0)),
logPrio::LOG_NOTICE);
527 if( indiTargetUpdate( m_indiP_reference1, target,
ipRecv,
true) < 0)
532 m_references(1,0) = target;
534 updateIfChanged(m_indiP_reference1, std::vector<std::string>({
"current",
"target"}), std::vector<float>({m_references(1,0), m_references(1,0)}));
536 log<text_log>(
"set reference1 to " + std::to_string(m_references(1,0)),
logPrio::LOG_NOTICE);
545 if(!
ipRecv.find(m_inputElements[0]))
return -1;
546 if(!
ipRecv.find(m_inputElements[1]))
return -1;
547 if(!
ipRecv.find(m_inputCounterElement))
return -1;
549 int counter =
ipRecv[m_inputCounterElement].get<
int>();
551 if(counter != m_counter)
554 m_measurements(0,0) =
ipRecv[m_inputElements[0]].get<
float>();
555 m_measurements(1,0) =
ipRecv[m_inputElements[1]].get<
float>();
569 if( indiTargetUpdate( m_indiP_ggain, target,
ipRecv,
true) < 0)
571 log<software_error>({__FILE__,__LINE__});
590 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
592 return toggleLoop(
true);
596 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
598 return toggleLoop(
false);
609 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
624 m_fsmStates[
ipRecv.getDevice()] =
ipRecv[
"state"].get();
634 if(
ipRecv.find(m_ctrlCurrents[0]))
636 m_currents[0] =
ipRecv[m_ctrlCurrents[0]].get<
float>();
648 m_fsmStates[
ipRecv.getDevice()] =
ipRecv[
"state"].get();
658 if(
ipRecv.find(m_ctrlCurrents[1]))
660 m_currents[1] =
ipRecv[m_ctrlCurrents[1]].get<
float>();
670 if(!
ipRecv.find(
"toggle"))
return 0;
672 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
675 return toggleLoop(
true);
677 else if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
680 return toggleLoop(
false);
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 m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
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.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
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)
The MagAO-X application to do closed-loop control using INDI properties.
int toggleLoop(bool onoff)
Change the loop state.
~closedLoopIndi() noexcept
D'tor, declared and defined for noexcept.
pcf::IndiProperty m_indiP_upstream
Property used to report the upstream loop state
virtual void setupConfig()
float m_ggain
The global gain.
pcf::IndiProperty m_indiP_inputs
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_upstream)
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl0)
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_reference1)
pcf::IndiProperty m_indiP_ctrlEnabled
virtual int appShutdown()
Shutdown the app.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_inputs)
std::vector< float > m_gains
The axis gains.
std::string m_inputCounterElement
The element with the frame counter, a monotonically increasing integer. Default is "counter".
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_ctrlEnabled)
pcf::IndiProperty m_indiP_reference1
mx::improc::eigenImage< float > m_commands
The latest commands.
int64_t m_counter
The latest value of the loop counter.
std::string m_inputProperty
The property with the input disturbances and frame counter.
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_counterReset)
virtual int appStartup()
Startup function.
bool m_operatingOK
If true then it's ok for the ctrl device to be OPERATING when a command is sent.
bool m_loopClosed
Whether or not the loop is closed.
pcf::IndiProperty m_indiP_deltas
pcf::IndiProperty m_indiP_ctrl1_fsm
The INDI property for fsm state of axis 1.
pcf::IndiProperty m_indiP_reference0
virtual void loadConfig()
mx::improc::eigenImage< float > m_measurements
The latest value of the measurements.
std::string m_upstreamProperty
The name of the toggle switch to monitor.
pcf::IndiProperty m_indiP_ctrl1
The INDI property used for control of axis 1.
std::vector< std::string > m_ctrlTargets
target elements of the properties to which to send the commands. Must specify 0 or 2....
std::vector< std::string > m_inputElements
The elements with the input disturbances. Must be two, defaults are "x" and "y".
pcf::IndiProperty m_indiP_counterReset
std::string m_upstreamDevice
The upstream device to monitor to automatically open this loop if it's loop opens.
std::vector< std::string > m_ctrlProperties
Properties of the ctrl device(s) to which to give the commands. Must specify two.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int sendCommands(std::vector< float > &commands)
Send commands to the control devices.
std::vector< float > m_defaultGains
The default gains, per-axis.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl0_fsm)
std::string m_inputDevice
The device with the input disturbances and frame counter.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl1_fsm)
int updateLoop()
Update the loop with a new command.
pcf::IndiProperty m_indiP_ctrl0_fsm
The INDI property for fsm state of axis 0.
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_ggain)
mx::improc::eigenImage< float > m_intMat
The interaction matrix. Default is [1 0][0 1].
std::vector< std::string > m_ctrlCurrents
current elements of the properties on which to base the commands. Must specify 0 or 2....
std::vector< float > m_currents
The current commands.
virtual int appLogic()
Implementation of the FSM for closedLoopIndi.
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_reference0)
mx::improc::eigenImage< float > m_references
The reference values of the disturbances.
std::unordered_map< std::string, std::string > m_fsmStates
The FSM states of the control devices.
closedLoopIndi()
Default c'tor.
pcf::IndiProperty m_indiP_ctrl0
The INDI property used for control of axis 0.
pcf::IndiProperty m_indiP_ggain
std::vector< std::string > m_ctrlDevices
Device names of the controller(s). If only one, it's used for both properties. Max two.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl1)
#define CREATE_REG_INDI_NEW_NUMBERU(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as unsigned int, using the standard call...
#define CREATE_REG_INDI_NEW_TOGGLESWITCH(prop, name)
Create and register a NEW INDI property as a standard toggle switch, using the standard callback name...
#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 CREATE_REG_INDI_NEW_NUMBERF(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as float, using the standard callback na...
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
#define CREATE_REG_INDI_RO_NUMBER(prop, name, label, group)
Create and register a RO INDI property as a number, using the standard callback name.
@ OPERATING
The device is operating, other than homing.
@ READY
The device is ready for operation, but is not operating.
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
const pcf::IndiProperty & ipRecv
INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf
updateIfChanged(m_indiP_angle, "target", m_angle)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.