7#ifndef closedLoopIndi_hpp
8#define closedLoopIndi_hpp
10#include "../../libMagAOX/libMagAOX.hpp"
11#include "../../magaox_git_version.h"
52 std::vector<std::string>
54 std::vector<std::string>
110 mx::app::appConfigurator &
_config );
188 config.add(
"input.device",
196 "The device with the input disturbances and frame counter." );
197 config.add(
"input.property",
205 "The property with the input disturbances and counter." );
206 config.add(
"input.elements",
214 " The elements with the input disturbances. Must be two, defaults are 'x' and 'y'." );
215 config.add(
"input.counterElement",
217 "input.counterElement",
223 "The element with the frame counter, a monotonically increasing integer. Default is 'counter'." );
225 config.add(
"input.references",
233 "The reference values for the input disturbances." );
235 config.add(
"ctrl.devices",
243 "Device names of the controller(s). If only one, it's used for both properties. Max two." );
244 config.add(
"ctrl.properties",
252 "Properties of the ctrl device(s) to which to give the commands. Must specify two." );
262 "current elements of the properties on which to base the commands. Must specify 0 or 2. Default is 'current'" );
272 "target elements of the properties to which to send the commands. Must specify 0 or 2. Default is 'target'." );
273 config.add(
"ctrl.operatingOK",
281 "If true then it's ok for the ctrl device to be OPERATING when a command is sent. Default is false." );
283 config.add(
"loop.intMat00",
291 "element (0,0) of the interaction matrix. Default is 1." );
292 config.add(
"loop.intMat01",
300 "element (0,1) of the interaction matrix. Default is 0." );
301 config.add(
"loop.intMat10",
309 "element (1,0) of the interaction matrix. Default is 0." );
310 config.add(
"loop.intMat11",
318 "element (1,1) of the interaction matrix. Default is 1." );
321 "loop.gain",
"",
"loop.gain", argType::Required,
"loop",
"gain",
false,
"float",
"default global loop gain." );
322 config.add(
"loop.gains",
330 "default loop gains. If single number, it is applied to all axes." );
331 config.add(
"loop.upstream",
339 "Upstream loop device name. This loop will open, and optionally close, with the upstream loop. "
341 config.add(
"loop.upstreamProperty",
343 "loop.upstreamProperty",
349 "Property of upstream loop device to follow. Must be a toggle. Default is loop_state." );
350 config.add(
"loop.upstreamFollowClosed",
352 "loop.upstreamFollowClosed",
355 "upstreamFollowClosed",
358 "If true, this loop also closes when the upstream loop closes. Default is false." );
386 std::vector<float>
refs( { 0, 0 } );
388 if(
refs.size() != 2 )
497 {
__FILE__,
__LINE__,
"ctrl.Targets and loop.gains are not the same size" } );
639 std::vector<std::string>( {
"delta0",
"delta1" } ),
648 pcf::IndiProperty
ip( pcf::IndiProperty::Number );
667 if( indiTargetUpdate( m_indiP_reference0,
target,
ipRecv,
true ) < 0 )
672 m_references( 0, 0 ) =
target;
675 std::vector<std::string>( {
"current",
"target" } ),
676 std::vector<float>( { m_references( 0, 0 ), m_references( 0, 0 ) } ) );
678 log<text_log>(
"set reference0 to " + std::to_string( m_references( 0, 0 ) ),
logPrio::LOG_NOTICE );
689 if( indiTargetUpdate( m_indiP_reference1,
target,
ipRecv,
true ) < 0 )
694 m_references( 1, 0 ) =
target;
697 std::vector<std::string>( {
"current",
"target" } ),
698 std::vector<float>( { m_references( 1, 0 ), m_references( 1, 0 ) } ) );
700 log<text_log>(
"set reference1 to " + std::to_string( m_references( 1, 0 ) ),
logPrio::LOG_NOTICE );
709 if( !
ipRecv.find( m_inputElements[0] ) )
711 if( !
ipRecv.find( m_inputElements[1] ) )
713 if( !
ipRecv.find( m_inputCounterElement ) )
716 int counter =
ipRecv[m_inputCounterElement].get<
int>();
718 if( counter != m_counter )
721 m_measurements( 0, 0 ) =
ipRecv[m_inputElements[0]].get<
float>();
722 m_measurements( 1, 0 ) =
ipRecv[m_inputElements[1]].get<
float>();
736 if( indiTargetUpdate( m_indiP_ggain,
target,
ipRecv,
true ) < 0 )
738 log<software_error>( { __FILE__, __LINE__ } );
757 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
759 return toggleLoop(
true );
763 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
765 return toggleLoop(
false );
776 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
788 if(
ipRecv.find(
"state" ) )
790 m_fsmStates[
ipRecv.getDevice()] =
ipRecv[
"state"].get();
800 if(
ipRecv.find( m_ctrlCurrents[0] ) )
802 m_currents[0] =
ipRecv[m_ctrlCurrents[0]].get<
float>();
812 if(
ipRecv.find(
"state" ) )
814 m_fsmStates[
ipRecv.getDevice()] =
ipRecv[
"state"].get();
824 if(
ipRecv.find( m_ctrlCurrents[1] ) )
826 m_currents[1] =
ipRecv[m_ctrlCurrents[1]].get<
float>();
836 if( !
ipRecv.find(
"toggle" ) )
839 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On && m_upstreamFollowClosed )
841 std::cerr <<
"upstream on\n";
842 return toggleLoop(
true );
844 else if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
846 std::cerr <<
"upstream off\n";
847 return toggleLoop(
false );
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.
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.
friend class closedLoopIndi_test
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 monitor the upstream loop state.
virtual void setupConfig()
Set up the app configuration interface.
float m_ggain
The global gain.
pcf::IndiProperty m_indiP_inputs
INDI property monitored for incoming disturbances.
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
INDI property exposing the loop open/closed state.
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
INDI property exposing the second reference value.
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.
float m_delta0
Latest first-axis residual.
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
Published loop residual values.
pcf::IndiProperty m_indiP_ctrl1_fsm
The INDI property for fsm state of axis 1.
pcf::IndiProperty m_indiP_reference0
INDI property exposing the first reference value.
virtual void loadConfig()
Load the application configuration.
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
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
INDI property used to request a counter reset.
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)
bool m_upstreamFollowClosed
If true, this loop also closes when the upstream loop closes.
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
std::vector< float > m_currents
The current commands.
virtual int appLogic()
Implementation of the FSM for closedLoopIndi.
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_reference0)
float m_delta1
Latest second-axis residual.
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
INDI property exposing the global loop gain.
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 INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#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 INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#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.
#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)
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
@ OPERATING
The device is operating, other than homing.
@ READY
The device is ready for operation, but is not operating.