API
 
Loading...
Searching...
No Matches
stdMotionNode.hpp
Go to the documentation of this file.
1/** \file stdMotionNode.hpp
2 * \brief The MagAO-X Instrument Graph stdMotionNode header file
3 *
4 * \ingroup instGraph_files
5 */
6
7#ifndef stdMotionNode_hpp
8#define stdMotionNode_hpp
9
10#include "fsmNode.hpp"
11
12///
13/**
14 *
15 * The key assumption of this node is that it should be in a valid, not-`none`, preset position
16 * for its ioputs to be `on`. It also supports triggering an alternate `on` state, which is used for
17 * stages which have a continuous tracking mode (k-mirror and ADC).
18 *
19 * The preset is specified by an INDI property with signature `<device>.<presetPrefix>Name` where device
20 * and presetPrefix are part of the configuration. This INDI property is a switch vector.
21 *
22 * The device and prefix can only be set once.
23 */
24class stdMotionNode : public fsmNode
25{
26
27 protected:
28 /// The prefix for preset naes. Usually either "preset" or "filter", to which "Name" is appended.
29 std::string m_presetPrefix;
30
31 /// The INDI key (device.property) for the presets. This is, say, `fwpupil.filterName`. It is set automatically.
32 std::string m_presetKey;
33
34 /// The current value of the preset property. Corresponds to the element name of the selected preset.
35 std::string m_curVal;
36
37 /// The current value of the put label.
38 std::string m_curLabel;
39
40 std::vector<std::string> m_presetPutName{ "out" };
41
42 /// This sets whether the multi-put selector is on the input or the output (default)
43 /** If this is a multi-put node (m_presetPutName.size() > 1) then the value of the preset switch
44 * controls which input or output is on, with the others off.
45 */
46 ingr::ioDir m_presetDir{ ingr::ioDir::output };
47
48 /// Contains the names of any puts which are always on if any are on.
49 std::set<std::string> m_alwaysOn;
50
51 /// Contains the names of any puts which are not automatically turned on if they are off.
52 std::set<std::string> m_noAutoOn;
53
54 /// The INDI key (device.property) for the switch denoting that this stage should be or should not be tracking
55 std::string m_trackingReqKey;
56
57 /// The element of the INDI property denoted by m_trackingReqKey to follow.
59
60 /// The INDI key (device.property) for the switch denoting that this stage is tracking
61 std::string m_trackerKey;
62
63 /// The element of the INDI property denoted by m_trackerKey to follow.
64 std::string m_trackerElement;
65
66 /// Flag indicating if the stage should be (true) or should not be (false, default) tracking.
67 bool m_trackingReq{ false };
68
69 /// Flag indicating whether or not the stage is currently tracking (default false).
70 bool m_tracking{ false };
71
72 public:
73 /// Only c'tor. Must be constructed with node name and a parent graph.
74 stdMotionNode( const std::string &name, /** [in] the name of this node*/
75 ingr::instGraphXML *parentGraph /** [in] the graph which this node belongs to*/ );
76
77 /// Set the device name. This can only be done once.
78 /**
79 * \throws
80 */
81 virtual void device( const std::string &dev /**< [in] */ );
82
83 using fsmNode::device;
84
85 virtual void presetPrefix( const std::string &pp /**< [in] */ );
86
87 const std::string &presetPrefix();
88
89 /// Get the current label text
90 /**
91 * \returns the current value of m_curLabel.
92 */
93 const std::string &curLabel();
94
95 void presetPutName( const std::vector<std::string> &ppp /**< [in] */ );
96
97 const std::vector<std::string> &presetPutName();
98
99 void presetDir( const ingr::ioDir &dir /**< [in] */ );
100
101 const ingr::ioDir &presetDir();
102
103 void trackingReqKey( const std::string &tk /**< [in] */ );
104
105 const std::string &trackingReqKey();
106
107 void trackingReqElement( const std::string &te /**< [in] */ );
108
109 const std::string &trackingReqElement();
110
111 void trackerKey( const std::string &tk /**< [in] */ );
112
113 const std::string &trackerKey();
114
115 void trackerElement( const std::string &te /**< [in] */ );
116
117 const std::string &trackerElement();
118
119 /// INDI SetProperty callback
120 virtual int handleSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the received INDI property to handle*/ );
121
122 virtual void togglePutsOn();
123
124 virtual void togglePutsOff();
125
126 void loadConfig(
127 mx::app::appConfigurator &config /**< [in] the application configurator loaded with this node's options*/ );
128};
129
130inline stdMotionNode::stdMotionNode( const std::string &name, ingr::instGraphXML *parentGraph )
131 : fsmNode( name, parentGraph )
132{
133}
134
135inline void stdMotionNode::device( const std::string &dev )
136{
137 // This will enforce the one-time only rule
138 fsmNode::device( dev );
139
140 // If presetPrefix is set, then we can make the key
141 if( m_presetPrefix != "" )
142 {
143 m_presetKey = m_device + "." + m_presetPrefix + "Name";
144 key( m_presetKey );
145 }
146}
147
148inline void stdMotionNode::presetPrefix( const std::string &pp )
149{
150 // Set it one time only
151 if( m_presetPrefix != "" && pp != m_presetPrefix )
152 {
153 std::string msg =
154 "stdMotionNode::presetPrefix: attempt to change preset prefix from " + m_presetPrefix + " to " + pp;
155 msg += " at ";
156 msg += __FILE__;
157 msg += " " + std::to_string( __LINE__ );
158 throw std::runtime_error( msg );
159 }
160
161 m_presetPrefix = pp;
162
163 // If device has been set then we can create the key
164 if( m_device != "" )
165 {
166 m_presetKey = m_device + "." + m_presetPrefix + "Name";
167 key( m_presetKey );
168 }
169}
170
171inline const std::string &stdMotionNode::presetPrefix()
172{
173 return m_presetPrefix;
174}
175
176inline const std::string &stdMotionNode::curLabel()
177{
178 return m_curLabel;
179}
180
181inline void stdMotionNode::presetPutName( const std::vector<std::string> &ppp )
182{
183 m_presetPutName = ppp;
184}
185
186inline const std::vector<std::string> &stdMotionNode::presetPutName()
187{
188 return m_presetPutName;
189}
190
191inline void stdMotionNode::presetDir( const ingr::ioDir &dir )
192{
193 m_presetDir = dir;
194}
195
196inline const ingr::ioDir &stdMotionNode::presetDir()
197{
198 return m_presetDir;
199}
200
201inline void stdMotionNode::trackingReqKey( const std::string &tk )
202{
203 m_trackingReqKey = tk;
204
205 if( m_trackingReqKey != "" )
206 {
208 }
209}
210
211inline const std::string &stdMotionNode::trackingReqKey()
212{
213 return m_trackingReqKey;
214}
215
216inline void stdMotionNode::trackingReqElement( const std::string &te )
217{
219}
220
221inline const std::string &stdMotionNode::trackingReqElement()
222{
224}
225
226inline void stdMotionNode::trackerKey( const std::string &tk )
227{
228 m_trackerKey = tk;
229
230 if( m_trackerKey != "" )
231 {
232 key( m_trackerKey );
233 }
234}
235
236inline const std::string &stdMotionNode::trackerKey()
237{
238 return m_trackerKey;
239}
240
241inline void stdMotionNode::trackerElement( const std::string &te )
242{
243 m_trackerElement = te;
244}
245
246inline const std::string &stdMotionNode::trackerElement()
247{
248 return m_trackerElement;
249}
250
251inline int stdMotionNode::handleSetProperty( const pcf::IndiProperty &ipRecv )
252{
253 int rv = fsmNode::handleSetProperty( ipRecv );
254
255 if( rv < 0 )
256 {
257 return rv;
258 }
259
260 if( ipRecv.createUniqueKey() == m_trackingReqKey )
261 {
262 if( ipRecv.find( m_trackingReqElement ) )
263 {
264 if( ipRecv[m_trackingReqElement].getSwitchState() == pcf::IndiElement::On )
265 {
266 if( !m_trackingReq )
267 {
268 ++m_changes;
269 }
270
271 m_trackingReq = true;
272 }
273 else
274 {
275 if( m_trackingReq )
276 {
277 ++m_changes;
278 }
279 m_trackingReq = false;
280 }
281 }
282 }
283 else if( ipRecv.createUniqueKey() == m_trackerKey )
284 {
285 if( ipRecv.find( m_trackerElement ) )
286 {
287 if( ipRecv[m_trackerElement].getSwitchState() == pcf::IndiElement::On )
288 {
289 if( !m_tracking )
290 {
291 ++m_changes;
292 }
293
294 m_tracking = true;
295 }
296 else
297 {
298 if( m_tracking )
299 {
300 ++m_changes;
301 }
302 m_tracking = false;
303 }
304 }
305 }
306 else if( ipRecv.createUniqueKey() == m_presetKey )
307 {
308 if( m_node != nullptr )
309 {
310 bool nothingIsOn = true;
311 for( auto &&it : ipRecv.getElements() )
312 {
313 if( it.second.getSwitchState() == pcf::IndiElement::On )
314 {
315 if( m_curVal != it.second.getName() && !m_tracking ) // we only update if not tracking
316 {
317 ++m_changes;
318 }
319
320 m_curVal = it.second.getName();
321 nothingIsOn = false;
322 }
323 }
324
325 if( nothingIsOn )
326 {
327 if( m_curVal != "" && !m_tracking ) // we only update if not tracking
328 {
329 ++m_changes;
330 }
331 m_curVal = "";
332 }
333 }
334 }
335
336 if( m_changes > 0 )
337 {
338 m_changes = 0;
339
340 if( m_trackingReq )
341 {
342 if( m_tracking &&
344 {
345 togglePutsOn();
346 }
347 else
348 { // Either we aren't tracking or we aren't READY || OPERATING
350 }
351 }
352 else
353 {
354 if( m_state != MagAOX::app::stateCodes::READY || m_tracking || m_curVal == "none" || m_curVal == "" )
355 {
357 }
358 else
359 {
360 togglePutsOn();
361 }
362 }
363 }
364
365 return 0;
366}
367
369{
370 if( m_node == nullptr || !m_parentGraph || !m_node->auxDataValid() )
371 {
372 return;
373 }
374
375 if( m_trackingReq )
376 {
377 if( m_tracking )
378 {
379 m_curLabel = "tracking";
380 m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "tracking" );
381 m_parentGraph->valueExtra( m_node->name(), "state", "tracking" );
383 }
384 else
385 {
386 m_curLabel = "not tracking";
387 m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "not tracking" );
388 m_parentGraph->valueExtra( m_node->name(), "state", "not tracking" );
389 m_parentGraph->stateChange();
390 }
391 }
393 {
395
396 m_parentGraph->valueExtra( m_node->name(), "state", m_curLabel );
397
398 if( m_presetPutName.size() == 1 ) // There's only one put, it's just on or off with a value
399 {
402 }
403 else // There is more than one put, and which one is on is selected by the value of the switch
404 {
405 if( m_presetDir == ingr::ioDir::output )
406 {
407 ingr::instIOPut *pptr; // We get this pointer using the node accessors
408 // which throw if there's a nullptr
409
410 // first deal with the single input
411 try
412 {
413 pptr = m_node->inputs().begin()->second;
414 }
415 catch( ... )
416 {
417 return;
418 }
419
420 // the single node is always on if any are on
421 pptr->enabled( true );
422 pptr->state( ingr::putState::on );
423
424 ingr::putState inst = pptr->state();
425 if( inst != ingr::putState::on )
426 {
427 inst = ingr::putState::waiting;
428 }
429
430 // Now deal with the many
431 for( auto s : m_presetPutName )
432 {
433 try
434 {
435 pptr = m_node->output( s );
436 }
437 catch( ... )
438 {
439 return;
440 }
441
442 if( s == m_curVal || m_alwaysOn.count( s ) == 1 )
443 {
444 pptr->enabled( true );
445 pptr->state( inst );
446 }
447 else
448 {
449 pptr->state( ingr::putState::off );
450
451 if( m_noAutoOn.count( s ) == 1 ) // if we turn it off, we disable it
452 {
453 pptr->enabled( false );
454 }
455 }
456 }
457 }
458 else // m_presetDir == ingr::ioDir::input )
459 {
460 ingr::instIOPut *pptr; // We get this pointer using the node accessors
461 // which throw if there's a nullptr
462
463 // first deal with the single input
464 try
465 {
466 pptr = m_node->outputs().begin()->second;
467 }
468 catch( ... )
469 {
470 return;
471 }
472
473 // the single node is always on if any are on
474 pptr->enabled( true );
475 pptr->state( ingr::putState::on );
476
477 // Now deal with the many
478 for( auto s : m_presetPutName )
479 {
480 try
481 {
482 pptr = m_node->input( s );
483 }
484 catch( ... )
485 {
486 return;
487 }
488
489 if( s == m_curVal || m_alwaysOn.count( s ) == 1 )
490 {
491 pptr->enabled( true );
492 pptr->state( ingr::putState::on );
493 }
494 else
495 {
496 pptr->state( ingr::putState::off );
497
498 if( m_noAutoOn.count( s ) == 1 ) // if we turn it off, we disable it
499 {
500 pptr->enabled( false );
501 }
502 }
503 }
504 }
505 }
506 m_parentGraph->stateChange();
507 }
508
509 return;
510}
511
513{
514 if( m_node == nullptr || !m_parentGraph || !m_node->auxDataValid() )
515 {
516 return;
517 }
518
519 if( m_tracking ) // regardless of whether required, if tracking this is our state
520 {
521 m_curLabel = "tracking";
522 m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "tracking" );
523 m_parentGraph->valueExtra( m_node->name(), "state", "tracking" );
524 }
525 else if( m_trackingReq ) // we can only be "not tracking" if tracking is required
526 {
527 m_curLabel = "not tracking";
528 m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "not tracking" );
529 m_parentGraph->valueExtra( m_node->name(), "state", "not tracking" );
530 }
531 else if( m_presetPutName.size() == 1 ) // otherwise, if we have a single node it's off
532 {
533 m_curLabel = "off";
534 m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "off" );
535 m_parentGraph->valueExtra( m_node->name(), "state", "---" );
536 }
537 else
538 {
539 // We don't change labels if m_presetPutName.size() > 1
540 m_parentGraph->valueExtra( m_node->name(), "state", "---" );
541 }
542
543 // replace xigNode::togglePutsOff() so we can check for always-on
544
545 for( auto &&iput : m_node->inputs() )
546 {
547 if( m_alwaysOn.count( iput.second->name() ) > 0 )
548 {
549 continue;
550 }
551
552 // iput.second->enabled(false);
553 iput.second->state( ingr::putState::off );
554 }
555
556 for( auto &&oput : m_node->outputs() )
557 {
558 if( m_alwaysOn.count( oput.second->name() ) > 0 )
559 {
560 continue;
561 }
562 // oput.second->enabled(false);
563 oput.second->state( ingr::putState::off );
564
565 if( m_noAutoOn.count( oput.second->name() ) == 1 ) // if we turn it off, we disable it
566 {
567 oput.second->enabled( false );
568 }
569 }
570}
571
572inline void stdMotionNode::loadConfig( mx::app::appConfigurator &config )
573{
574 if( !m_parentGraph )
575 {
576 std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "parent graph is null" );
577 throw std::runtime_error( msg );
578 }
579
580 std::string type;
581 config.configUnused( type, mx::app::iniFile::makeKey( name(), "type" ) );
582
583 if( type != "stdMotion" )
584 {
585 std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "node type is not stdMotion" );
586 throw std::runtime_error( msg );
587 }
588
589 std::string dev = name();
590 config.configUnused( dev, mx::app::iniFile::makeKey( name(), "device" ) );
591
592 std::string prePrefix = "preset";
593 config.configUnused( prePrefix, mx::app::iniFile::makeKey( name(), "presetPrefix" ) );
594
595 std::string preDir = "output";
596 config.configUnused( preDir, mx::app::iniFile::makeKey( name(), "presetDir" ) );
597
598 if( preDir == "input" )
599 {
600 presetDir( ingr::ioDir::input );
601 }
602 else if( preDir == "output" )
603 {
604 presetDir( ingr::ioDir::output );
605 }
606 else
607 {
608 std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "invalid presetDir (must be input or output)" );
609 throw std::runtime_error( msg );
610 }
611
612 std::vector<std::string> prePutName( { "out" } );
613 config.configUnused( prePutName, mx::app::iniFile::makeKey( name(), "presetPutName" ) );
614 if( prePutName.size() == 0 )
615 {
616 std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "presetPutName can't be empty" );
617 throw std::runtime_error( msg );
618 }
619
620 std::vector<std::string> alwaysOn;
621 config.configUnused( alwaysOn, mx::app::iniFile::makeKey( name(), "alwaysOn" ) );
622 try
623 {
624 for( auto &ao : alwaysOn )
625 {
626 m_alwaysOn.insert( ao );
627 }
628 }
629 catch( const std::exception &e )
630 {
631 std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "exception from insert in m_alwaysOn" );
632 msg += ":";
633 msg += e.what();
634 throw std::runtime_error( msg );
635 }
636
637 std::vector<std::string> noAutoOn;
638 config.configUnused( noAutoOn, mx::app::iniFile::makeKey( name(), "noAutoOn" ) );
639 try
640 {
641 for( auto &ao : noAutoOn )
642 {
643 m_noAutoOn.insert( ao );
644 }
645 }
646 catch( const std::exception &e )
647 {
648 std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "exception from insert in m_noAutoOn" );
649 msg += ":";
650 msg += e.what();
651 throw std::runtime_error( msg );
652 }
653
654 std::string trackReqKey;
655 config.configUnused( trackReqKey, mx::app::iniFile::makeKey( name(), "trackingReqKey" ) );
656
657 std::string trackReqEl;
658 config.configUnused( trackReqEl, mx::app::iniFile::makeKey( name(), "trackingReqElement" ) );
659
660 // Check if both are set
661 if( ( trackReqKey == "" && trackReqEl != "" ) || ( trackReqKey != "" && trackReqEl == "" ) )
662 {
663 std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig",
664 "trackingReqKey and trackingReqElement must both be provided" );
665 throw std::runtime_error( msg );
666 }
667
668 std::string trackKey;
669 config.configUnused( trackKey, mx::app::iniFile::makeKey( name(), "trackerKey" ) );
670
671 std::string trackEl;
672 config.configUnused( trackEl, mx::app::iniFile::makeKey( name(), "trackerElement" ) );
673
674 // Check if both are set
675 if( ( trackKey == "" && trackEl != "" ) || ( trackKey != "" && trackEl == "" ) )
676 {
677 std::string msg =
678 XIGN_EXCEPTION( "stdMotionNode::loadConfig", "trackingKey and trackingElement must both be provided" );
679 throw std::runtime_error( msg );
680 }
681
682 // This will catch the case where one or the other pair was set, but not both
683 if( ( trackKey == "" && trackReqKey != "" ) || ( trackKey != "" && trackReqKey == "" ) )
684 {
685 std::string msg =
686 XIGN_EXCEPTION( "stdMotionNode::loadConfig", "trackingReqKey and trackerKey must both be provided" );
687 throw std::runtime_error( msg );
688 }
689
690 device( dev );
691 presetPrefix( prePrefix );
692 presetPutName( prePutName );
693 trackingReqKey( trackReqKey );
694 trackingReqElement( trackReqEl );
695 trackerKey( trackKey );
696 trackerElement( trackEl );
697}
698
699#endif // stdMotionNode_hpp
Implementation of an instGraph node interface for a MagAO-X Finite State Machine (FSM)
Definition fsmNode.hpp:69
const std::string & device() const
Get the device name.
Definition fsmNode.hpp:228
virtual int handleSetProperty(const pcf::IndiProperty &ipRecv)
INDI SetProperty callback.
Definition fsmNode.hpp:375
stateCodeT m_state
The numerical code of the current state.
Definition fsmNode.hpp:84
std::string m_device
The INDI device name. Defaults to the node name set on construction.
Definition fsmNode.hpp:74
std::string m_trackerKey
The INDI key (device.property) for the switch denoting that this stage is tracking.
virtual void togglePutsOn()
Change the state of all inputs and all outputs to on.
std::string m_trackingReqElement
The element of the INDI property denoted by m_trackingReqKey to follow.
std::set< std::string > m_noAutoOn
Contains the names of any puts which are not automatically turned on if they are off.
const std::string & device() const
Get the device name.
Definition fsmNode.hpp:228
const std::string & presetPrefix()
std::string m_curVal
The current value of the preset property. Corresponds to the element name of the selected preset.
const std::string & trackingReqElement()
const std::string & trackerElement()
bool m_trackingReq
Flag indicating if the stage should be (true) or should not be (false, default) tracking.
bool m_tracking
Flag indicating whether or not the stage is currently tracking (default false).
ingr::ioDir m_presetDir
This sets whether the multi-put selector is on the input or the output (default)
std::string m_presetKey
The INDI key (device.property) for the presets. This is, say, fwpupil.filterName. It is set automatic...
std::set< std::string > m_alwaysOn
Contains the names of any puts which are always on if any are on.
const std::string & trackingReqKey()
std::vector< std::string > m_presetPutName
std::string m_curLabel
The current value of the put label.
std::string m_trackingReqKey
The INDI key (device.property) for the switch denoting that this stage should be or should not be tra...
virtual int handleSetProperty(const pcf::IndiProperty &ipRecv)
INDI SetProperty callback.
void loadConfig(mx::app::appConfigurator &config)
std::string m_trackerElement
The element of the INDI property denoted by m_trackerKey to follow.
const std::string & curLabel()
Get the current label text.
stdMotionNode(const std::string &name, ingr::instGraphXML *parentGraph)
Only c'tor. Must be constructed with node name and a parent graph.
const std::vector< std::string > & presetPutName()
const std::string & trackerKey()
std::string m_presetPrefix
The prefix for preset naes. Usually either "preset" or "filter", to which "Name" is appended.
virtual void togglePutsOff()
Change the state of all inputs and all outputs to off.
const ingr::ioDir & presetDir()
void key(const std::string &nkey)
Add a key to the set.
Definition xigNode.hpp:116
ingr::instNode * m_node
The underlying instGraph node.
Definition xigNode.hpp:37
virtual void togglePutsOn()
Change the state of all inputs and all outputs to on.
Definition xigNode.hpp:126
int m_changes
Counter that can be incremented when changes are detected. Set to 0 when graph is updated.
Definition xigNode.hpp:39
ingr::instGraphXML * m_parentGraph
The parent instGraph that this node is a part of.
Definition xigNode.hpp:35
std::string name()
Get the name of this node.
Definition xigNode.hpp:106
The MagAO-X Instrument Graph fsmNode header file.
@ OPERATING
The device is operating, other than homing.
@ READY
The device is ready for operation, but is not operating.
#define XIGN_EXCEPTION(src, expl)
Definition xigNode.hpp:24