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  bool m_operatingOK {false}; ///< If true then it's ok for the ctrl device to be OPERATING when a command is sent
60 
61  std::unordered_map<std::string, std::string> m_fsmStates; ///< The FSM states of the control devices.
62 
63  std::vector<float> m_currents; ///< The current commands
64 
65  mx::improc::eigenImage<float> m_intMat; ///< The interaction matrix. Default is [1 0][0 1].
66 
67  std::vector<float> m_defaultGains; ///< The default gains, per-axis
68 
69  std::string m_upstreamDevice; ///< The upstream device to monitor to automatically open this loop if it's loop opens
70  std::string m_upstreamProperty {"loop_state"}; ///< The name of the toggle switch to monitor
71 
72  ///@}
73 
74  int64_t m_counter = -1; ///< The latest value of the loop counter
75  mx::improc::eigenImage<float> m_measurements; ///< The latest value of the measurements
76  float m_delta0 {0};
77  float m_delta1 {0};
78 
79  mx::improc::eigenImage<float> m_commands; ///< The latest commands
80 
81  float m_ggain {0}; ///< The global gain
82 
83  std::vector<float> m_gains; ///< The axis gains
84 
85  bool m_loopClosed {false}; ///< Whether or not the loop is closed
86 
87 public:
88  /// Default c'tor.
90 
91  /// D'tor, declared and defined for noexcept.
92  ~closedLoopIndi() noexcept
93  {}
94 
95  virtual void setupConfig();
96 
97  /// Implementation of loadConfig logic, separated for testing.
98  /** This is called by loadConfig().
99  */
100  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
101 
102  virtual void loadConfig();
103 
104  /// Startup function
105  /**
106  *
107  */
108  virtual int appStartup();
109 
110  /// Implementation of the FSM for closedLoopIndi.
111  /**
112  * \returns 0 on no critical error
113  * \returns -1 on an error requiring shutdown
114  */
115  virtual int appLogic();
116 
117  /// Shutdown the app.
118  /**
119  *
120  */
121  virtual int appShutdown();
122 
123  /// Change the loop state
124  int toggleLoop( bool onoff );
125 
126  /// Update the loop with a new command
127  int updateLoop();
128 
129  /// Send commands to the control devices
130  int sendCommands(std::vector<float> & commands);
131 
132  //INDI
133 
134  pcf::IndiProperty m_indiP_deltas;
135 
136  pcf::IndiProperty m_indiP_reference0;
138 
139  pcf::IndiProperty m_indiP_reference1;
141 
142  pcf::IndiProperty m_indiP_inputs;
144 
145  pcf::IndiProperty m_indiP_ggain;
147 
148  pcf::IndiProperty m_indiP_ctrlEnabled;
150 
151  pcf::IndiProperty m_indiP_counterReset;
153 
154  pcf::IndiProperty m_indiP_ctrl0_fsm; ///< The INDI property for fsm state of axis 0
156 
157  pcf::IndiProperty m_indiP_ctrl0; ///< The INDI property used for control of axis 0
159 
160  pcf::IndiProperty m_indiP_ctrl1_fsm; ///< The INDI property for fsm state of axis 1
162 
163  pcf::IndiProperty m_indiP_ctrl1; ///< The INDI property used for control of axis 1
165 
166  pcf::IndiProperty m_indiP_upstream; ///< Property used to report the upstream loop state
168 };
169 
170 closedLoopIndi::closedLoopIndi() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
171 {
172  return;
173 }
174 
176 {
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'.");
181 
182  config.add("input.references", "", "input.references", argType::Required, "input", "references", false, "vector<float>", "The reference values for the input disturbances.");
183 
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.");
189 
190 
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.");
195 
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.");
200 
201 }
202 
203 int closedLoopIndi::loadConfigImpl( mx::app::appConfigurator & _config )
204 {
205  _config(m_inputDevice, "input.device");
206  _config(m_inputProperty, "input.property");
207  _config(m_inputElements, "input.elements");
208  _config(m_inputCounterElement, "input.counterElement");
209 
210  if(m_inputDevice == "")
211  {
212  m_shutdown = 1;
213  return log<software_error, -1>({__FILE__, __LINE__, "no input device specified"});
214  }
215 
216  if(m_inputProperty == "")
217  {
218  m_shutdown = 1;
219  return log<software_error, -1>({__FILE__, __LINE__, "no input property specified"});
220  }
221 
222  if(m_inputElements.size() != 2)
223  {
224  m_shutdown = 1;
225  return log<software_error, -1>({__FILE__, __LINE__, "must specify only two input.elements"});
226  }
227 
228  std::vector<float> refs({0,0});
229  _config(refs, "input.references");
230  if(refs.size() != 2)
231  {
232  m_shutdown = 1;
233  return log<software_error, -1>({__FILE__, __LINE__, "input.references must have 2 elements"});
234  }
235 
236  m_references.resize(2,1);
237  m_references(0,0) = refs[0];
238  m_references(1,0) = refs[1];
239 
240  _config(m_ctrlDevices, "ctrl.devices");
241  _config(m_ctrlProperties, "ctrl.properties");
242  _config(m_ctrlCurrents, "ctrl.currents");
243  _config(m_ctrlTargets, "ctrl.targets");
244  _config(m_operatingOK, "ctrl.operatingOK");
245 
246  if(m_ctrlDevices.size() == 1)
247  {
248  m_ctrlDevices.push_back(m_ctrlDevices[0]);
249  }
250  else if(m_ctrlDevices.size() != 2)
251  {
252  m_shutdown = 1;
253  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.devices"});
254  }
255 
256  if(m_ctrlProperties.size() != 2)
257  {
258  m_shutdown = 1;
259  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.properties"});
260  }
261 
262  if(m_ctrlTargets.size() != 2)
263  {
264  m_shutdown = 1;
265  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
266  }
267 
268  if(m_ctrlCurrents.size() != 2)
269  {
270  m_shutdown = 1;
271  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.currents"});
272  }
273 
274  if(m_ctrlTargets.size() != 2)
275  {
276  m_shutdown = 1;
277  return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
278  }
279 
280  float im00 = 1;
281  float im01 = 0;
282  float im10 = 0;
283  float im11 = 1;
284 
285  _config(im00, "loop.intMat00");
286  _config(im01, "loop.intMat01");
287  _config(im10, "loop.intMat10");
288  _config(im11, "loop.intMat11");
289 
290  m_intMat.resize(2, 2);
291  m_intMat(0,0) = im00;
292  m_intMat(0,1) = im01;
293  m_intMat(1,0) = im10;
294  m_intMat(1,1) = im11;
295 
296  _config(m_ggain, "loop.gain");
297 
298  _config(m_defaultGains, "loop.gains");
299  _config(m_upstreamDevice, "loop.upstream");
300  _config(m_upstreamProperty, "loop.upstreamProperty");
301 
302  return 0;
303 }
304 
306 {
307  loadConfigImpl(config);
308 }
309 
311 {
313 
314  CREATE_REG_INDI_RO_NUMBER( m_indiP_deltas, "deltas", "Deltas", "Deltas");
315  m_indiP_deltas.add(pcf::IndiElement("delta0"));
316  m_indiP_deltas["delta0"] = 0;
317  m_indiP_deltas.add(pcf::IndiElement("delta1"));
318  m_indiP_deltas["delta1"] = 0;
319 
320  CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference0, "reference0", -1e15, 1e15, 1, "%g", "reference0", "references");
321  m_indiP_reference0["current"] = m_references(0,0);
322  m_indiP_reference0["target"] = m_references(0,0);
323 
324  CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference1, "reference1", -1e15, 1e15, 1, "%g", "reference1", "references");
325  m_indiP_reference1["current"] = m_references(1,0);
326  m_indiP_reference1["target"] = m_references(1,0);
327 
328  if(m_ctrlTargets.size() != m_defaultGains.size())
329  {
330  if(m_defaultGains.size()==1)
331  {
332  m_defaultGains.push_back(m_defaultGains[0]);
333  log<text_log>("Setting loop.gains gains to be same size as ctrl.Targets", logPrio::LOG_NOTICE);
334  }
335  else
336  {
337  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and loop.gains are not the same size"});
338  }
339  }
340 
341  m_gains.resize(m_defaultGains.size());
342  for(size_t n=0; n < m_defaultGains.size(); ++n) m_gains[n] = m_defaultGains[n];
343 
344  CREATE_REG_INDI_NEW_NUMBERU( m_indiP_ggain, "loop_gain", 0, 1, 0, "%0.2f", "gain", "loop");
345  m_indiP_ggain["current"] = m_ggain;
346  m_indiP_ggain["target"] = m_ggain;
347 
348 
350 
352 
353  m_measurements.resize(2,1);
354  m_measurements.setZero();
355 
356  m_currents.resize(m_ctrlDevices.size(), -1e15);
357 
359 
361 
362  if(m_ctrlDevices[1] != m_ctrlDevices[0])
363  {
365  }
366 
368 
369  m_commands.resize(2, 2);
370  m_commands.setZero();
371 
372  //Get the loop state for managing offloading
373  if(m_upstreamDevice != "")
374  {
376  }
377 
378  return 0;
379 }
380 
382 {
383  if(m_loopClosed)
384  {
386  }
387  else
388  {
390  }
391 
392  return 0;
393 }
394 
396 {
397  return 0;
398 }
399 
401 {
402  if(!m_loopClosed && onoff) //not enabled so change
403  {
404  m_loopClosed = true;
405  log<loop_closed>();
406  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::On, INDI_OK);
407  return 0;
408  }
409 
410  if(m_loopClosed && !onoff)
411  {
412  m_loopClosed = false;
413  log<loop_open>();
414  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::Off, INDI_IDLE);
415 
416  return 0;
417  }
418 
419  return 0;
420 }
421 
422 
423 inline
425 {
426  bool ready = false;
427 
428  //This should only give ready == true if all devices exist and are ready
429  for(size_t n = 0; n < m_ctrlDevices.size(); ++n)
430  {
431  if(m_fsmStates.count(m_ctrlDevices[n]) > 0)
432  {
433  if(m_fsmStates[m_ctrlDevices[n]] == "READY" || (m_operatingOK && m_fsmStates[m_ctrlDevices[n]] == "OPERATING") )
434  {
435  ready = true;
436  }
437  else
438  {
439  ready = false;
440  break;
441  }
442  }
443  else
444  {
445  ready = false;
446  break;
447  }
448  }
449 
450  if(ready != true)
451  {
452  return 0;
453  }
454 
455  m_delta0 = m_measurements(0,0) - m_references(0,0);
456  m_delta1 = m_measurements(1,0) - m_references(1,0);
457 
458  m_commands.matrix() = m_intMat.matrix() * (m_measurements - m_references).matrix();
459 
460  std::vector<float> commands;
461  commands.resize(m_measurements.rows());
462 
463  for(int cc = 0; cc < m_measurements.rows(); ++cc)
464  {
465  commands[cc] = m_currents[cc] - m_ggain*m_gains[cc]*m_commands(cc,0);
466  }
467 
468  //And send commands.
469  int rv;
470  if(m_loopClosed)
471  {
472  rv = sendCommands(commands);
473  }
474  else
475  {
476  rv = 0;
477  }
478 
479  updateIfChanged(m_indiP_deltas, std::vector<std::string>({"delta0", "delta1"}), std::vector<float>({m_delta0, m_delta1}));
480  return rv;
481 }
482 
483 inline
484 int closedLoopIndi::sendCommands(std::vector<float> & commands)
485 {
486  for(size_t n=0; n < m_ctrlDevices.size(); ++n)
487  {
488  pcf::IndiProperty ip(pcf::IndiProperty::Number);
489 
490  ip.setDevice(m_ctrlDevices[n]);
491  ip.setName(m_ctrlProperties[n]);
492  ip.add(pcf::IndiElement(m_ctrlTargets[n]));
493  ip[m_ctrlTargets[n]] = commands[n];
494 
495  sendNewProperty(ip);
496  }
497 
498  return 0;
499 }
500 
501 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference0)(const pcf::IndiProperty &ipRecv)
502 {
503  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_reference0);
504 
505  float target;
506 
507  if( indiTargetUpdate( m_indiP_reference0, target, ipRecv, true) < 0)
508  {
509  return log<software_error, -1>({__FILE__,__LINE__});
510  }
511 
512  m_references(0,0) = target;
513 
514  updateIfChanged(m_indiP_reference0, std::vector<std::string>({"current", "target"}), std::vector<float>({m_references(0,0), m_references(0,0)}));
515 
516  log<text_log>("set reference0 to " + std::to_string(m_references(0,0)), logPrio::LOG_NOTICE);
517 
518  return 0;
519 }
520 
521 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference1)(const pcf::IndiProperty &ipRecv)
522 {
523  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_reference1);
524 
525  float target;
526 
527  if( indiTargetUpdate( m_indiP_reference1, target, ipRecv, true) < 0)
528  {
529  return log<software_error, -1>({__FILE__,__LINE__});
530  }
531 
532  m_references(1,0) = target;
533 
534  updateIfChanged(m_indiP_reference1, std::vector<std::string>({"current", "target"}), std::vector<float>({m_references(1,0), m_references(1,0)}));
535 
536  log<text_log>("set reference1 to " + std::to_string(m_references(1,0)), logPrio::LOG_NOTICE);
537 
538  return 0;
539 }
540 
541 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_inputs)(const pcf::IndiProperty &ipRecv)
542 {
543  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_inputs)
544 
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;
548 
549  int counter = ipRecv[m_inputCounterElement].get<int>();
550 
551  if(counter != m_counter)
552  {
553  m_counter = counter;
554  m_measurements(0,0) = ipRecv[m_inputElements[0]].get<float>();
555  m_measurements(1,0) = ipRecv[m_inputElements[1]].get<float>();
556 
557  return updateLoop();
558  }
559 
560  return 0;
561 }
562 
563 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ggain)(const pcf::IndiProperty &ipRecv)
564 {
565  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ggain);
566 
567  float target;
568 
569  if( indiTargetUpdate( m_indiP_ggain, target, ipRecv, true) < 0)
570  {
571  log<software_error>({__FILE__,__LINE__});
572  return -1;
573  }
574 
575  m_ggain = target;
576 
577  updateIfChanged(m_indiP_ggain, "current", m_ggain);
578  updateIfChanged(m_indiP_ggain, "target", m_ggain);
579 
580  log<text_log>("set global gain to " + std::to_string(m_ggain), logPrio::LOG_NOTICE);
581 
582  return 0;
583 }
584 
585 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrlEnabled)(const pcf::IndiProperty &ipRecv)
586 {
587  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrlEnabled);
588 
589  //switch is toggled to on
590  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
591  {
592  return toggleLoop(true);
593  }
594 
595  //switch is toggle to off
596  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
597  {
598  return toggleLoop(false);
599  }
600 
601  return 0;
602 }
603 
604 INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_counterReset)(const pcf::IndiProperty &ipRecv)
605 {
606  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_counterReset);
607 
608  //switch is toggled to on
609  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
610  {
611  m_counter = -1;
612  }
613 
614 
615  return 0;
616 }
617 
618 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0_fsm)(const pcf::IndiProperty &ipRecv)
619 {
620  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl0_fsm);
621 
622  if(ipRecv.find("state"))
623  {
624  m_fsmStates[ipRecv.getDevice()] = ipRecv["state"].get();
625  }
626 
627  return 0;
628 }
629 
630 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0)(const pcf::IndiProperty &ipRecv)
631 {
632  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl0);
633 
634  if(ipRecv.find(m_ctrlCurrents[0]))
635  {
636  m_currents[0] = ipRecv[m_ctrlCurrents[0]].get<float>();
637  }
638 
639  return 0;
640 }
641 
642 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1_fsm)(const pcf::IndiProperty &ipRecv)
643 {
644  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl1_fsm);
645 
646  if(ipRecv.find("state"))
647  {
648  m_fsmStates[ipRecv.getDevice()] = ipRecv["state"].get();
649  }
650 
651  return 0;
652 }
653 
654 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1)(const pcf::IndiProperty &ipRecv)
655 {
656  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl1);
657 
658  if(ipRecv.find(m_ctrlCurrents[1]))
659  {
660  m_currents[1] = ipRecv[m_ctrlCurrents[1]].get<float>();
661  }
662 
663  return 0;
664 }
665 
666 INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_upstream)(const pcf::IndiProperty &ipRecv)
667 {
668  INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_upstream);
669 
670  if(!ipRecv.find("toggle")) return 0;
671 
672  if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
673  {
674  std::cerr << "upstream on\n";
675  return toggleLoop(true);
676  }
677  else if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
678  {
679  std::cerr << "upstream off\n";
680  return toggleLoop(false);
681  }
682 
683  return 0;
684 }
685 
686 
687 } //namespace app
688 } //namespace MagAOX
689 
690 #endif //closedLoopIndi_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
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.
Definition: MagAOXApp.hpp:3120
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:100
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:3144
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
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:3268
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_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
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:363
#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:405
#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:445
#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:309
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:282
#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.
Definition: indiMacros.hpp:385
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:56
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf
Definition: adcTracker.hpp:461
updateIfChanged(m_indiP_angle, "target", m_angle)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
Definition: dm.hpp:24
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.