API
closedLoopIndi.hpp
Go to the documentation of this file.
1 /** \file closedLoopIndi.hpp
2  * \brief The MagAO-X INDI Closed Loop header file
3  *
4  * \ingroup closedLoopIndi_files
5  */
6 
7 #ifndef closedLoopIndi_hpp
8 #define closedLoopIndi_hpp
9 
10 
11 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
12 #include "../../magaox_git_version.h"
13 
14 /** \defgroup closedLoopIndi
15  * \brief The MagAO-X application to do closed-loop control using INDI properties
16  *
17  * <a href="../handbook/operating/software/apps/closedLoopIndi.html">Application Documentation</a>
18  *
19  * \ingroup apps
20  *
21  */
22 
23 /** \defgroup closedLoopIndi_files
24  * \ingroup closedLoopIndi
25  */
26 
27 namespace MagAOX
28 {
29 namespace app
30 {
31 
32 /// The MagAO-X application to do closed-loop control using INDI properties
33 /**
34  * \ingroup closedLoopIndi
35  */
36 class closedLoopIndi : public MagAOXApp<true>
37 {
38  //Give the test harness access.
39  friend class closedLoopIndi_test;
40 
41 protected:
42 
43  /** \name Configurable Parameters
44  *@{
45  */
46 
47  std::string m_inputDevice; ///< The device with the input disturbances and frame counter.
48  std::string m_inputProperty; ///< The property with the input disturbances and frame counter.
49  std::vector<std::string> m_inputElements {"x", "y"}; ///< The elements with the input disturbances. Must be two, defaults are "x" and "y".
50  std::string m_inputCounterElement {"counter"}; ///< The element with the frame counter, a monotonically increasing integer. Default is "counter".
51 
52  std::vector<std::string> m_ctrlDevices; ///< Device names of the controller(s). If only one, it's used for both properties. Max two.
53  std::vector<std::string> m_ctrlProperties; ///< Properties of the ctrl device(s) to which to give the commands. Must specify two.
54  std::vector<std::string> m_ctrlCurrents {"current", "current"}; ///< current elements of the properties on which to base the commands. Must specify 0 or 2. Default is 'current'.
55  std::vector<std::string> m_ctrlTargets {"target", "target"}; ///< target elements of the properties to which to send the commands. Must specify 0 or 2. Default is 'target'.
56 
57  mx::improc::eigenImage<float> m_references; ///< The reference values of the disturbances
58 
59  std::unordered_map<std::string, std::string> m_fsmStates; ///< The FSM states of the control devices.
60 
61  std::vector<float> m_currents; ///< The current commands
62 
63  mx::improc::eigenImage<float> m_intMat; ///< The interaction matrix. Default is [1 0][0 1].
64 
65  std::vector<float> m_defaultGains; ///< The default gains, per-axis
66 
67  std::string m_upstreamDevice; ///< The upstream device to monitor to automatically open this loop if it's loop opens
68  std::string m_upstreamProperty {"loop_state"}; ///< The name of the toggle switch to monitor
69 
70  ///@}
71 
72  int64_t m_counter = -1; ///< The latest value of the loop counter
73  mx::improc::eigenImage<float> m_measurements; ///< The latest value of the measurements
74  mx::improc::eigenImage<float> m_commands; ///< The latest commands
75 
76  float m_ggain {0}; ///< The global gain
77 
78  std::vector<float> m_gains; ///< The axis gains
79 
80  bool m_loopClosed {false}; ///< Whether or not the loop is closed
81 
82 public:
83  /// Default c'tor.
85 
86  /// D'tor, declared and defined for noexcept.
87  ~closedLoopIndi() noexcept
88  {}
89 
90  virtual void setupConfig();
91 
92  /// Implementation of loadConfig logic, separated for testing.
93  /** This is called by loadConfig().
94  */
95  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
96 
97  virtual void loadConfig();
98 
99  /// Startup function
100  /**
101  *
102  */
103  virtual int appStartup();
104 
105  /// Implementation of the FSM for closedLoopIndi.
106  /**
107  * \returns 0 on no critical error
108  * \returns -1 on an error requiring shutdown
109  */
110  virtual int appLogic();
111 
112  /// Shutdown the app.
113  /**
114  *
115  */
116  virtual int appShutdown();
117 
118  /// Change the loop state
119  int toggleLoop( bool onoff );
120 
121  /// Update the loop with a new command
122  int updateLoop();
123 
124  /// Send commands to the control devices
125  int sendCommands(std::vector<float> & commands);
126 
127  //INDI
128 
129  pcf::IndiProperty m_indiP_reference0;
131 
132  pcf::IndiProperty m_indiP_reference1;
134 
135  pcf::IndiProperty m_indiP_inputs;
137 
138  pcf::IndiProperty m_indiP_ggain;
140 
141  pcf::IndiProperty m_indiP_ctrlEnabled;
143 
144  pcf::IndiProperty m_indiP_counterReset;
146 
147  pcf::IndiProperty m_indiP_ctrl0_fsm; ///< The INDI property for fsm state of axis 0
149 
150  pcf::IndiProperty m_indiP_ctrl0; ///< The INDI property used for control of axis 0
152 
153  pcf::IndiProperty m_indiP_ctrl1_fsm; ///< The INDI property for fsm state of axis 1
155 
156  pcf::IndiProperty m_indiP_ctrl1; ///< The INDI property used for control of axis 1
158 
159  pcf::IndiProperty m_indiP_upstream; ///< Property used to report the upstream loop state
161 };
162 
163 closedLoopIndi::closedLoopIndi() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
164 {
165  return;
166 }
167 
169 {
170  config.add("input.device", "", "input.device", argType::Required, "input", "device", false, "string", "The device with the input disturbances and frame counter.");
171  config.add("input.property", "", "input.property", argType::Required, "input", "property", false, "string", "The property with the input disturbances and counter.");
172  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'.");
173  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'.");
174 
175  config.add("input.references", "", "input.references", argType::Required, "input", "references", false, "vector<float>", "The reference values for the input disturbances.");
176 
177  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.");
178  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.");
179  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'");
180  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'.");
181 
182  config.add("loop.intMat00", "", "loop.intMat00", argType::Required, "loop", "intMat00", false, "float", "element (0,0) of the interaction matrix. Default is 1.");
183  config.add("loop.intMat01", "", "loop.intMat01", argType::Required, "loop", "intMat01", false, "float", "element (0,1) of the interaction matrix. Default is 0.");
184  config.add("loop.intMat10", "", "loop.intMat10", argType::Required, "loop", "intMat10", false, "float", "element (1,0) of the interaction matrix. Default is 0.");
185  config.add("loop.intMat11", "", "loop.intMat11", argType::Required, "loop", "intMat11", false, "float", "element (1,1) of the interaction matrix. Default is 1.");
186 
187  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.");
188  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.");
189  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.");
190 
191 }
192 
193 int closedLoopIndi::loadConfigImpl( mx::app::appConfigurator & _config )
194 {
195  _config(m_inputDevice, "input.device");
196  _config(m_inputProperty, "input.property");
197  _config(m_inputElements, "input.elements");
198  _config(m_inputCounterElement, "input.counterElement");
199 
200  if(m_inputDevice == "")
201  {
202  m_shutdown = 1;
203  return log<software_error, -1>({__FILE__, __LINE__, "no input device specified"});
204  }
205 
206  if(m_inputProperty == "")
207  {
208  m_shutdown = 1;
209  return log<software_error, -1>({__FILE__, __LINE__, "no input property specified"});
210  }
211 
212  if(m_inputElements.size() != 2)
213  {
214  m_shutdown = 1;
215  return log<software_error, -1>({__FILE__, __LINE__, "must specify only two input.elements"});
216  }
217 
218  std::vector<float> refs({0,0});
219  _config(refs, "input.references");
220  if(refs.size() != 2)
221  {
222  m_shutdown = 1;
223  return log<software_error, -1>({__FILE__, __LINE__, "input.references must have 2 elements"});
224  }
225 
226  m_references.resize(2,1);
227  m_references(0,0) = refs[0];
228  m_references(1,0) = refs[1];
229 
230  _config(m_ctrlDevices, "ctrl.devices");
231  _config(m_ctrlProperties, "ctrl.properties");
232  _config(m_ctrlCurrents, "ctrl.currents");
233  _config(m_ctrlTargets, "ctrl.targets");
234 
235  if(m_ctrlDevices.size() == 1)
236  {
237  m_ctrlDevices.push_back(m_ctrlDevices[0]);
238  }
239  else if(m_ctrlDevices.size() != 2)
240  {
241  m_shutdown = 1;
242  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.devices"});
243  }
244 
245  if(m_ctrlProperties.size() != 2)
246  {
247  m_shutdown = 1;
248  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.properties"});
249  }
250 
251  if(m_ctrlTargets.size() != 2)
252  {
253  m_shutdown = 1;
254  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
255  }
256 
257  if(m_ctrlCurrents.size() != 2)
258  {
259  m_shutdown = 1;
260  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.currents"});
261  }
262 
263  if(m_ctrlTargets.size() != 2)
264  {
265  m_shutdown = 1;
266  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
267  }
268 
269  float im00 = 1;
270  float im01 = 0;
271  float im10 = 0;
272  float im11 = 1;
273 
274  _config(im00, "loop.intMat00");
275  _config(im01, "loop.intMat01");
276  _config(im10, "loop.intMat10");
277  _config(im11, "loop.intMat11");
278 
279  m_intMat.resize(2, 2);
280  m_intMat(0,0) = im00;
281  m_intMat(0,1) = im01;
282  m_intMat(1,0) = im10;
283  m_intMat(1,1) = im11;
284 
285  _config(m_defaultGains, "loop.gains");
286  _config(m_upstreamDevice, "loop.upstream");
287  _config(m_upstreamProperty, "loop.upstreamProperty");
288 
289  return 0;
290 }
291 
293 {
294  loadConfigImpl(config);
295 }
296 
298 {
300 
301  CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference0, "reference0", -1e15, 1e15, 1, "%g", "reference0", "references");
302  m_indiP_reference0["current"] = m_references(0,0);
303  m_indiP_reference0["target"] = m_references(0,0);
304 
305  CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference1, "reference1", -1e15, 1e15, 1, "%g", "reference1", "references");
306  m_indiP_reference1["current"] = m_references(1,0);
307  m_indiP_reference1["target"] = m_references(1,0);
308 
309  if(m_ctrlTargets.size() != m_defaultGains.size())
310  {
311  if(m_defaultGains.size()==1)
312  {
313  m_defaultGains.push_back(m_defaultGains[0]);
314  log<text_log>("Setting loop.gains gains to be same size as ctrl.Targets", logPrio::LOG_NOTICE);
315  }
316  else
317  {
318  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and loop.gains are not the same size"});
319  }
320  }
321 
322  m_gains.resize(m_defaultGains.size());
323  for(size_t n=0; n < m_defaultGains.size(); ++n) m_gains[n] = m_defaultGains[n];
324 
325  CREATE_REG_INDI_NEW_NUMBERU( m_indiP_ggain, "loop_gain", 0, 1, 0, "%0.2f", "gain", "loop");
326  m_indiP_ggain["current"] = m_ggain;
327  m_indiP_ggain["target"] = m_ggain;
328 
329 
331 
333 
334  m_measurements.resize(2,1);
335  m_measurements.setZero();
336 
337  m_currents.resize(m_ctrlDevices.size(), -1e15);
338 
340 
342 
343  if(m_ctrlDevices[1] != m_ctrlDevices[0])
344  {
346  }
347 
349 
350  m_commands.resize(2, 2);
351  m_commands.setZero();
352 
353  //Get the loop state for managing offloading
354  if(m_upstreamDevice != "")
355  {
357  }
358 
359  return 0;
360 }
361 
363 {
364  if(m_loopClosed)
365  {
367  }
368  else
369  {
371  }
372 
373  return 0;
374 }
375 
377 {
378  return 0;
379 }
380 
382 {
383  if(!m_loopClosed && onoff) //not enabled so change
384  {
385  m_loopClosed = true;
386  log<loop_closed>();
387  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::On, INDI_OK);
388  return 0;
389  }
390 
391  if(m_loopClosed && !onoff)
392  {
393  m_loopClosed = false;
394  log<loop_open>();
395  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::Off, INDI_IDLE);
396 
397  return 0;
398  }
399 
400  return 0;
401 }
402 
403 
404 inline
406 {
407  bool ready = false;
408 
409  //This should only give ready == true if all devices exist and are ready
410  for(size_t n = 0; n < m_ctrlDevices.size(); ++n)
411  {
412  if(m_fsmStates.count(m_ctrlDevices[n]) > 0)
413  {
414  if(m_fsmStates[m_ctrlDevices[n]] == "READY")
415  {
416  ready = true;
417  }
418  else
419  {
420  ready = false;
421  break;
422  }
423  }
424  else
425  {
426  ready = false;
427  break;
428  }
429  }
430 
431  if(ready != true)
432  {
433  return 0;
434  }
435 
436  m_commands.matrix() = m_intMat.matrix() * (m_measurements - m_references).matrix();
437 
438  std::vector<float> commands;
439  commands.resize(m_measurements.rows());
440 
441  for(int cc = 0; cc < m_measurements.rows(); ++cc)
442  {
443  commands[cc] = m_currents[cc] - m_ggain*m_gains[cc]*m_commands(cc,0);
444  }
445 
446  //And send commands.
447  if(m_loopClosed)
448  {
449  return sendCommands(commands);
450  }
451  else
452  {
453  return 0;
454  }
455 }
456 
457 inline
458 int closedLoopIndi::sendCommands(std::vector<float> & commands)
459 {
460  for(size_t n=0; n < m_ctrlDevices.size(); ++n)
461  {
462  pcf::IndiProperty ip(pcf::IndiProperty::Number);
463 
464  ip.setDevice(m_ctrlDevices[n]);
465  ip.setName(m_ctrlProperties[n]);
466  ip.add(pcf::IndiElement(m_ctrlTargets[n]));
467  ip[m_ctrlTargets[n]] = commands[n];
468 
469  sendNewProperty(ip);
470  }
471 
472  return 0;
473 }
474 
475 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference0)(const pcf::IndiProperty &ipRecv)
476 {
477  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_reference0);
478 
479  float target;
480 
481  if( indiTargetUpdate( m_indiP_reference0, target, ipRecv, true) < 0)
482  {
483  return log<software_error, -1>({__FILE__,__LINE__});
484  }
485 
486  m_references(0,0) = target;
487 
488  updateIfChanged(m_indiP_reference0, std::vector<std::string>({"current", "target"}), std::vector<float>({m_references(0,0), m_references(0,0)}));
489 
490  log<text_log>("set reference0 to " + std::to_string(m_references(0,0)), logPrio::LOG_NOTICE);
491 
492  return 0;
493 }
494 
495 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference1)(const pcf::IndiProperty &ipRecv)
496 {
497  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_reference1);
498 
499  float target;
500 
501  if( indiTargetUpdate( m_indiP_reference1, target, ipRecv, true) < 0)
502  {
503  return log<software_error, -1>({__FILE__,__LINE__});
504  }
505 
506  m_references(1,0) = target;
507 
508  updateIfChanged(m_indiP_reference1, std::vector<std::string>({"current", "target"}), std::vector<float>({m_references(1,0), m_references(1,0)}));
509 
510  log<text_log>("set reference1 to " + std::to_string(m_references(1,0)), logPrio::LOG_NOTICE);
511 
512  return 0;
513 }
514 
515 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_inputs)(const pcf::IndiProperty &ipRecv)
516 {
517  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_inputs)
518 
519  if(!ipRecv.find(m_inputElements[0])) return -1;
520  if(!ipRecv.find(m_inputElements[1])) return -1;
521  if(!ipRecv.find(m_inputCounterElement)) return -1;
522 
523  int counter = ipRecv[m_inputCounterElement].get<int>();
524 
525  if(counter != m_counter)
526  {
527  m_counter = counter;
528  m_measurements(0,0) = ipRecv[m_inputElements[0]].get<float>();
529  m_measurements(1,0) = ipRecv[m_inputElements[1]].get<float>();
530 
531  return updateLoop();
532  }
533 
534  return 0;
535 }
536 
537 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ggain)(const pcf::IndiProperty &ipRecv)
538 {
539  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ggain);
540 
541  float target;
542 
543  if( indiTargetUpdate( m_indiP_ggain, target, ipRecv, true) < 0)
544  {
545  log<software_error>({__FILE__,__LINE__});
546  return -1;
547  }
548 
549  m_ggain = target;
550 
551  updateIfChanged(m_indiP_ggain, "current", m_ggain);
552  updateIfChanged(m_indiP_ggain, "target", m_ggain);
553 
554  log<text_log>("set global gain to " + std::to_string(m_ggain), logPrio::LOG_NOTICE);
555 
556  return 0;
557 }
558 
559 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrlEnabled)(const pcf::IndiProperty &ipRecv)
560 {
561  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrlEnabled);
562 
563  //switch is toggled to on
564  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
565  {
566  return toggleLoop(true);
567  }
568 
569  //switch is toggle to off
570  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
571  {
572  return toggleLoop(false);
573  }
574 
575  return 0;
576 }
577 
578 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_counterReset)(const pcf::IndiProperty &ipRecv)
579 {
580  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_counterReset);
581 
582  //switch is toggled to on
583  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
584  {
585  m_counter = -1;
586  }
587 
588 
589  return 0;
590 }
591 
592 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0_fsm)(const pcf::IndiProperty &ipRecv)
593 {
594  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl0_fsm);
595 
596  if(ipRecv.find("state"))
597  {
598  m_fsmStates[ipRecv.getDevice()] = ipRecv["state"].get();
599  }
600 
601  return 0;
602 }
603 
604 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0)(const pcf::IndiProperty &ipRecv)
605 {
606  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl0);
607 
608  if(ipRecv.find(m_ctrlCurrents[0]))
609  {
610  m_currents[0] = ipRecv[m_ctrlCurrents[0]].get<float>();
611  }
612 
613  return 0;
614 }
615 
616 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1_fsm)(const pcf::IndiProperty &ipRecv)
617 {
618  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl1_fsm);
619 
620  if(ipRecv.find("state"))
621  {
622  m_fsmStates[ipRecv.getDevice()] = ipRecv["state"].get();
623  }
624 
625  return 0;
626 }
627 
628 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1)(const pcf::IndiProperty &ipRecv)
629 {
630  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl1);
631 
632  if(ipRecv.find(m_ctrlCurrents[1]))
633  {
634  m_currents[1] = ipRecv[m_ctrlCurrents[1]].get<float>();
635  }
636 
637  return 0;
638 }
639 
640 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_upstream)(const pcf::IndiProperty &ipRecv)
641 {
642  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_upstream);
643 
644  if(!ipRecv.find("toggle")) return 0;
645 
646  if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
647  {
648  std::cerr << "upstream on\n";
649  return toggleLoop(true);
650  }
651  else if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
652  {
653  std::cerr << "upstream off\n";
654  return toggleLoop(false);
655  }
656 
657  return 0;
658 }
659 
660 
661 } //namespace app
662 } //namespace MagAOX
663 
664 #endif //closedLoopIndi_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:102
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.
Definition: MagAOXApp.hpp:2901
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
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)
Definition: MagAOXApp.hpp:3031
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
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_loopClosed
Whether or not the loop is closed.
pcf::IndiProperty m_indiP_ctrl1_fsm
The INDI property for fsm state of axis 1.
pcf::IndiProperty m_indiP_reference0
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.
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...
Definition: indiMacros.hpp:339
#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...
Definition: indiMacros.hpp:359
#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...
Definition: indiMacros.hpp:399
#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...
Definition: indiMacros.hpp:285
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:264
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:51
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
const pcf::IndiProperty & ipRecv
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
INDI_SETCALLBACK_DEFN(MagAOXApp< _useINDI >, m_indiP_powerChannel)(const pcf
Definition: MagAOXApp.hpp:3195
Definition: dm.hpp:24
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.