Line data Source code
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.
89 : closedLoopIndi();
90 :
91 : /// D'tor, declared and defined for noexcept.
92 33 : ~closedLoopIndi() noexcept
93 33 : {}
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;
137 0 : INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_reference0);
138 :
139 : pcf::IndiProperty m_indiP_reference1;
140 0 : INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_reference1);
141 :
142 : pcf::IndiProperty m_indiP_inputs;
143 0 : INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_inputs);
144 :
145 : pcf::IndiProperty m_indiP_ggain;
146 0 : INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_ggain);
147 :
148 : pcf::IndiProperty m_indiP_ctrlEnabled;
149 0 : INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_ctrlEnabled);
150 :
151 : pcf::IndiProperty m_indiP_counterReset;
152 0 : INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_counterReset);
153 :
154 : pcf::IndiProperty m_indiP_ctrl0_fsm; ///< The INDI property for fsm state of axis 0
155 0 : INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl0_fsm);
156 :
157 : pcf::IndiProperty m_indiP_ctrl0; ///< The INDI property used for control of axis 0
158 0 : INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl0);
159 :
160 : pcf::IndiProperty m_indiP_ctrl1_fsm; ///< The INDI property for fsm state of axis 1
161 0 : INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl1_fsm);
162 :
163 : pcf::IndiProperty m_indiP_ctrl1; ///< The INDI property used for control of axis 1
164 0 : INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl1);
165 :
166 : pcf::IndiProperty m_indiP_upstream; ///< Property used to report the upstream loop state
167 0 : INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_upstream);
168 : };
169 :
170 429 : closedLoopIndi::closedLoopIndi() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
171 : {
172 33 : return;
173 0 : }
174 :
175 0 : void closedLoopIndi::setupConfig()
176 : {
177 0 : config.add("input.device", "", "input.device", argType::Required, "input", "device", false, "string", "The device with the input disturbances and frame counter.");
178 0 : config.add("input.property", "", "input.property", argType::Required, "input", "property", false, "string", "The property with the input disturbances and counter.");
179 0 : 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 0 : 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 0 : config.add("input.references", "", "input.references", argType::Required, "input", "references", false, "vector<float>", "The reference values for the input disturbances.");
183 :
184 0 : 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 0 : 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 0 : 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 0 : 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 0 : 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 0 : config.add("loop.intMat00", "", "loop.intMat00", argType::Required, "loop", "intMat00", false, "float", "element (0,0) of the interaction matrix. Default is 1.");
192 0 : config.add("loop.intMat01", "", "loop.intMat01", argType::Required, "loop", "intMat01", false, "float", "element (0,1) of the interaction matrix. Default is 0.");
193 0 : config.add("loop.intMat10", "", "loop.intMat10", argType::Required, "loop", "intMat10", false, "float", "element (1,0) of the interaction matrix. Default is 0.");
194 0 : config.add("loop.intMat11", "", "loop.intMat11", argType::Required, "loop", "intMat11", false, "float", "element (1,1) of the interaction matrix. Default is 1.");
195 :
196 0 : config.add("loop.gain", "", "loop.gain", argType::Required, "loop", "gain", false, "float", "default global loop gain.");
197 0 : 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 0 : 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 0 : 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 0 : }
202 :
203 0 : int closedLoopIndi::loadConfigImpl( mx::app::appConfigurator & _config )
204 : {
205 0 : _config(m_inputDevice, "input.device");
206 0 : _config(m_inputProperty, "input.property");
207 0 : _config(m_inputElements, "input.elements");
208 0 : _config(m_inputCounterElement, "input.counterElement");
209 :
210 0 : if(m_inputDevice == "")
211 : {
212 0 : m_shutdown = 1;
213 0 : return log<software_error, -1>({__FILE__, __LINE__, "no input device specified"});
214 : }
215 :
216 0 : if(m_inputProperty == "")
217 : {
218 0 : m_shutdown = 1;
219 0 : return log<software_error, -1>({__FILE__, __LINE__, "no input property specified"});
220 : }
221 :
222 0 : if(m_inputElements.size() != 2)
223 : {
224 0 : m_shutdown = 1;
225 0 : return log<software_error, -1>({__FILE__, __LINE__, "must specify only two input.elements"});
226 : }
227 :
228 0 : std::vector<float> refs({0,0});
229 0 : _config(refs, "input.references");
230 0 : if(refs.size() != 2)
231 : {
232 0 : m_shutdown = 1;
233 0 : return log<software_error, -1>({__FILE__, __LINE__, "input.references must have 2 elements"});
234 : }
235 :
236 0 : m_references.resize(2,1);
237 0 : m_references(0,0) = refs[0];
238 0 : m_references(1,0) = refs[1];
239 :
240 0 : _config(m_ctrlDevices, "ctrl.devices");
241 0 : _config(m_ctrlProperties, "ctrl.properties");
242 0 : _config(m_ctrlCurrents, "ctrl.currents");
243 0 : _config(m_ctrlTargets, "ctrl.targets");
244 0 : _config(m_operatingOK, "ctrl.operatingOK");
245 :
246 0 : if(m_ctrlDevices.size() == 1)
247 : {
248 0 : m_ctrlDevices.push_back(m_ctrlDevices[0]);
249 : }
250 0 : else if(m_ctrlDevices.size() != 2)
251 : {
252 0 : m_shutdown = 1;
253 0 : return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.devices"});
254 : }
255 :
256 0 : if(m_ctrlProperties.size() != 2)
257 : {
258 0 : m_shutdown = 1;
259 0 : return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.properties"});
260 : }
261 :
262 0 : if(m_ctrlTargets.size() != 2)
263 : {
264 0 : m_shutdown = 1;
265 0 : return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
266 : }
267 :
268 0 : if(m_ctrlCurrents.size() != 2)
269 : {
270 0 : m_shutdown = 1;
271 0 : return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.currents"});
272 : }
273 :
274 0 : if(m_ctrlTargets.size() != 2)
275 : {
276 0 : m_shutdown = 1;
277 0 : return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
278 : }
279 :
280 0 : float im00 = 1;
281 0 : float im01 = 0;
282 0 : float im10 = 0;
283 0 : float im11 = 1;
284 :
285 0 : _config(im00, "loop.intMat00");
286 0 : _config(im01, "loop.intMat01");
287 0 : _config(im10, "loop.intMat10");
288 0 : _config(im11, "loop.intMat11");
289 :
290 0 : m_intMat.resize(2, 2);
291 0 : m_intMat(0,0) = im00;
292 0 : m_intMat(0,1) = im01;
293 0 : m_intMat(1,0) = im10;
294 0 : m_intMat(1,1) = im11;
295 :
296 0 : _config(m_ggain, "loop.gain");
297 :
298 0 : _config(m_defaultGains, "loop.gains");
299 0 : _config(m_upstreamDevice, "loop.upstream");
300 0 : _config(m_upstreamProperty, "loop.upstreamProperty");
301 :
302 0 : return 0;
303 0 : }
304 :
305 0 : void closedLoopIndi::loadConfig()
306 : {
307 0 : loadConfigImpl(config);
308 0 : }
309 :
310 0 : int closedLoopIndi::appStartup()
311 : {
312 0 : REG_INDI_SETPROP(m_indiP_inputs, m_inputDevice, m_inputProperty);
313 :
314 0 : CREATE_REG_INDI_RO_NUMBER( m_indiP_deltas, "deltas", "Deltas", "Deltas");
315 0 : m_indiP_deltas.add(pcf::IndiElement("delta0"));
316 0 : m_indiP_deltas["delta0"] = 0;
317 0 : m_indiP_deltas.add(pcf::IndiElement("delta1"));
318 0 : m_indiP_deltas["delta1"] = 0;
319 :
320 0 : CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference0, "reference0", -1e15, 1e15, 1, "%g", "reference0", "references");
321 0 : m_indiP_reference0["current"] = m_references(0,0);
322 0 : m_indiP_reference0["target"] = m_references(0,0);
323 :
324 0 : CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference1, "reference1", -1e15, 1e15, 1, "%g", "reference1", "references");
325 0 : m_indiP_reference1["current"] = m_references(1,0);
326 0 : m_indiP_reference1["target"] = m_references(1,0);
327 :
328 0 : if(m_ctrlTargets.size() != m_defaultGains.size())
329 : {
330 0 : if(m_defaultGains.size()==1)
331 : {
332 0 : m_defaultGains.push_back(m_defaultGains[0]);
333 0 : log<text_log>("Setting loop.gains gains to be same size as ctrl.Targets", logPrio::LOG_NOTICE);
334 : }
335 : else
336 : {
337 0 : return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and loop.gains are not the same size"});
338 : }
339 : }
340 :
341 0 : m_gains.resize(m_defaultGains.size());
342 0 : for(size_t n=0; n < m_defaultGains.size(); ++n) m_gains[n] = m_defaultGains[n];
343 :
344 0 : CREATE_REG_INDI_NEW_NUMBERU( m_indiP_ggain, "loop_gain", 0, 1, 0, "%0.2f", "gain", "loop");
345 0 : m_indiP_ggain["current"] = m_ggain;
346 0 : m_indiP_ggain["target"] = m_ggain;
347 :
348 :
349 0 : CREATE_REG_INDI_NEW_TOGGLESWITCH( m_indiP_ctrlEnabled, "loop_state");
350 :
351 0 : CREATE_REG_INDI_NEW_REQUESTSWITCH( m_indiP_counterReset, "counter_reset");
352 :
353 0 : m_measurements.resize(2,1);
354 0 : m_measurements.setZero();
355 :
356 0 : m_currents.resize(m_ctrlDevices.size(), -1e15);
357 :
358 0 : REG_INDI_SETPROP(m_indiP_ctrl0_fsm, m_ctrlDevices[0], "fsm");
359 :
360 0 : REG_INDI_SETPROP(m_indiP_ctrl0, m_ctrlDevices[0], m_ctrlProperties[0]);
361 :
362 0 : if(m_ctrlDevices[1] != m_ctrlDevices[0])
363 : {
364 0 : REG_INDI_SETPROP(m_indiP_ctrl1_fsm, m_ctrlDevices[1], "fsm");
365 : }
366 :
367 0 : REG_INDI_SETPROP(m_indiP_ctrl1, m_ctrlDevices[1], m_ctrlProperties[1]);
368 :
369 0 : m_commands.resize(2, 2);
370 0 : m_commands.setZero();
371 :
372 : //Get the loop state for managing offloading
373 0 : if(m_upstreamDevice != "")
374 : {
375 0 : REG_INDI_SETPROP(m_indiP_upstream, m_upstreamDevice, m_upstreamProperty);
376 : }
377 :
378 0 : return 0;
379 : }
380 :
381 0 : int closedLoopIndi::appLogic()
382 : {
383 0 : if(m_loopClosed)
384 : {
385 0 : state(stateCodes::OPERATING);
386 : }
387 : else
388 : {
389 0 : state(stateCodes::READY);
390 : }
391 :
392 0 : return 0;
393 : }
394 :
395 0 : int closedLoopIndi::appShutdown()
396 : {
397 0 : return 0;
398 : }
399 :
400 0 : int closedLoopIndi::toggleLoop(bool onoff)
401 : {
402 0 : if(!m_loopClosed && onoff) //not enabled so change
403 : {
404 0 : m_loopClosed = true;
405 0 : log<loop_closed>();
406 0 : updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::On, INDI_OK);
407 0 : return 0;
408 : }
409 :
410 0 : if(m_loopClosed && !onoff)
411 : {
412 0 : m_loopClosed = false;
413 0 : log<loop_open>();
414 0 : updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::Off, INDI_IDLE);
415 :
416 0 : return 0;
417 : }
418 :
419 0 : return 0;
420 : }
421 :
422 :
423 : inline
424 : int closedLoopIndi::updateLoop()
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 3 : INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference0)(const pcf::IndiProperty &ipRecv)
502 : {
503 3 : 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 3 : INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference1)(const pcf::IndiProperty &ipRecv)
522 : {
523 3 : 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 3 : INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_inputs)(const pcf::IndiProperty &ipRecv)
542 : {
543 3 : 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 3 : INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ggain)(const pcf::IndiProperty &ipRecv)
564 : {
565 3 : 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 3 : INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrlEnabled)(const pcf::IndiProperty &ipRecv)
586 : {
587 3 : 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 3 : INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_counterReset)(const pcf::IndiProperty &ipRecv)
605 : {
606 3 : 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 3 : INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0_fsm)(const pcf::IndiProperty &ipRecv)
619 : {
620 3 : 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 3 : INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0)(const pcf::IndiProperty &ipRecv)
631 : {
632 3 : 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 3 : INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1_fsm)(const pcf::IndiProperty &ipRecv)
643 : {
644 3 : 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 3 : INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1)(const pcf::IndiProperty &ipRecv)
655 : {
656 3 : 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 3 : INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_upstream)(const pcf::IndiProperty &ipRecv)
667 : {
668 3 : 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
|