Line data Source code
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 : */
24 : class 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.
58 : std::string m_trackingReqElement;
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 :
130 1 : inline stdMotionNode::stdMotionNode( const std::string &name, ingr::instGraphXML *parentGraph )
131 3 : : fsmNode( name, parentGraph )
132 : {
133 1 : }
134 :
135 1 : inline void stdMotionNode::device( const std::string &dev )
136 : {
137 : // This will enforce the one-time only rule
138 1 : fsmNode::device( dev );
139 :
140 : // If presetPrefix is set, then we can make the key
141 1 : if( m_presetPrefix != "" )
142 : {
143 0 : m_presetKey = m_device + "." + m_presetPrefix + "Name";
144 0 : key( m_presetKey );
145 : }
146 1 : }
147 :
148 1 : inline void stdMotionNode::presetPrefix( const std::string &pp )
149 : {
150 : // Set it one time only
151 1 : if( m_presetPrefix != "" && pp != m_presetPrefix )
152 : {
153 : std::string msg =
154 0 : "stdMotionNode::presetPrefix: attempt to change preset prefix from " + m_presetPrefix + " to " + pp;
155 0 : msg += " at ";
156 0 : msg += __FILE__;
157 0 : msg += " " + std::to_string( __LINE__ );
158 0 : throw std::runtime_error( msg );
159 0 : }
160 :
161 1 : m_presetPrefix = pp;
162 :
163 : // If device has been set then we can create the key
164 1 : if( m_device != "" )
165 : {
166 1 : m_presetKey = m_device + "." + m_presetPrefix + "Name";
167 1 : key( m_presetKey );
168 : }
169 1 : }
170 :
171 : inline const std::string &stdMotionNode::presetPrefix()
172 : {
173 : return m_presetPrefix;
174 : }
175 :
176 : inline const std::string &stdMotionNode::curLabel()
177 : {
178 : return m_curLabel;
179 : }
180 :
181 1 : inline void stdMotionNode::presetPutName( const std::vector<std::string> &ppp )
182 : {
183 1 : m_presetPutName = ppp;
184 1 : }
185 :
186 : inline const std::vector<std::string> &stdMotionNode::presetPutName()
187 : {
188 : return m_presetPutName;
189 : }
190 :
191 1 : inline void stdMotionNode::presetDir( const ingr::ioDir &dir )
192 : {
193 1 : m_presetDir = dir;
194 1 : }
195 :
196 : inline const ingr::ioDir &stdMotionNode::presetDir()
197 : {
198 : return m_presetDir;
199 : }
200 :
201 1 : inline void stdMotionNode::trackingReqKey( const std::string &tk )
202 : {
203 1 : m_trackingReqKey = tk;
204 :
205 1 : if( m_trackingReqKey != "" )
206 : {
207 0 : key( m_trackingReqKey );
208 : }
209 1 : }
210 :
211 : inline const std::string &stdMotionNode::trackingReqKey()
212 : {
213 : return m_trackingReqKey;
214 : }
215 :
216 1 : inline void stdMotionNode::trackingReqElement( const std::string &te )
217 : {
218 1 : m_trackingReqElement = te;
219 1 : }
220 :
221 : inline const std::string &stdMotionNode::trackingReqElement()
222 : {
223 : return m_trackingReqElement;
224 : }
225 :
226 1 : inline void stdMotionNode::trackerKey( const std::string &tk )
227 : {
228 1 : m_trackerKey = tk;
229 :
230 1 : if( m_trackerKey != "" )
231 : {
232 0 : key( m_trackerKey );
233 : }
234 1 : }
235 :
236 : inline const std::string &stdMotionNode::trackerKey()
237 : {
238 : return m_trackerKey;
239 : }
240 :
241 1 : inline void stdMotionNode::trackerElement( const std::string &te )
242 : {
243 1 : m_trackerElement = te;
244 1 : }
245 :
246 : inline const std::string &stdMotionNode::trackerElement()
247 : {
248 : return m_trackerElement;
249 : }
250 :
251 0 : inline int stdMotionNode::handleSetProperty( const pcf::IndiProperty &ipRecv )
252 : {
253 0 : int rv = fsmNode::handleSetProperty( ipRecv );
254 :
255 0 : if( rv < 0 )
256 : {
257 0 : return rv;
258 : }
259 :
260 0 : if( ipRecv.createUniqueKey() == m_trackingReqKey )
261 : {
262 0 : if( ipRecv.find( m_trackingReqElement ) )
263 : {
264 0 : if( ipRecv[m_trackingReqElement].getSwitchState() == pcf::IndiElement::On )
265 : {
266 0 : if( !m_trackingReq )
267 : {
268 0 : ++m_changes;
269 : }
270 :
271 0 : m_trackingReq = true;
272 : }
273 : else
274 : {
275 0 : if( m_trackingReq )
276 : {
277 0 : ++m_changes;
278 : }
279 0 : m_trackingReq = false;
280 : }
281 : }
282 : }
283 0 : else if( ipRecv.createUniqueKey() == m_trackerKey )
284 : {
285 0 : if( ipRecv.find( m_trackerElement ) )
286 : {
287 0 : if( ipRecv[m_trackerElement].getSwitchState() == pcf::IndiElement::On )
288 : {
289 0 : if( !m_tracking )
290 : {
291 0 : ++m_changes;
292 : }
293 :
294 0 : m_tracking = true;
295 : }
296 : else
297 : {
298 0 : if( m_tracking )
299 : {
300 0 : ++m_changes;
301 : }
302 0 : m_tracking = false;
303 : }
304 : }
305 : }
306 0 : else if( ipRecv.createUniqueKey() == m_presetKey )
307 : {
308 0 : if( m_node != nullptr )
309 : {
310 0 : bool nothingIsOn = true;
311 0 : for( auto &&it : ipRecv.getElements() )
312 : {
313 0 : if( it.second.getSwitchState() == pcf::IndiElement::On )
314 : {
315 0 : if( m_curVal != it.second.getName() && !m_tracking ) // we only update if not tracking
316 : {
317 0 : ++m_changes;
318 : }
319 :
320 0 : m_curVal = it.second.getName();
321 0 : nothingIsOn = false;
322 : }
323 : }
324 :
325 0 : if( nothingIsOn )
326 : {
327 0 : if( m_curVal != "" && !m_tracking ) // we only update if not tracking
328 : {
329 0 : ++m_changes;
330 : }
331 0 : m_curVal = "";
332 : }
333 : }
334 : }
335 :
336 0 : if( m_changes > 0 )
337 : {
338 0 : m_changes = 0;
339 :
340 0 : if( m_trackingReq )
341 : {
342 0 : if( m_tracking &&
343 0 : ( m_state == MagAOX::app::stateCodes::READY || m_state == MagAOX::app::stateCodes::OPERATING ) )
344 : {
345 0 : togglePutsOn();
346 : }
347 : else
348 : { // Either we aren't tracking or we aren't READY || OPERATING
349 0 : togglePutsOff();
350 : }
351 : }
352 : else
353 : {
354 0 : if( m_state != MagAOX::app::stateCodes::READY || m_tracking || m_curVal == "none" || m_curVal == "" )
355 : {
356 0 : togglePutsOff();
357 : }
358 : else
359 : {
360 0 : togglePutsOn();
361 : }
362 : }
363 : }
364 :
365 0 : return 0;
366 : }
367 :
368 0 : inline void stdMotionNode::togglePutsOn()
369 : {
370 0 : if( m_node == nullptr || !m_parentGraph || !m_node->auxDataValid() )
371 : {
372 0 : return;
373 : }
374 :
375 0 : if( m_trackingReq )
376 : {
377 0 : if( m_tracking )
378 : {
379 0 : m_curLabel = "tracking";
380 0 : m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "tracking" );
381 0 : m_parentGraph->valueExtra( m_node->name(), "state", "tracking" );
382 0 : xigNode::togglePutsOn();
383 : }
384 : else
385 : {
386 0 : m_curLabel = "not tracking";
387 0 : m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "not tracking" );
388 0 : m_parentGraph->valueExtra( m_node->name(), "state", "not tracking" );
389 0 : m_parentGraph->stateChange();
390 : }
391 : }
392 0 : else if( m_state == MagAOX::app::stateCodes::READY )
393 : {
394 0 : m_curLabel = m_curVal;
395 :
396 0 : m_parentGraph->valueExtra( m_node->name(), "state", m_curLabel );
397 :
398 0 : if( m_presetPutName.size() == 1 ) // There's only one put, it's just on or off with a value
399 : {
400 0 : m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, m_curVal );
401 0 : xigNode::togglePutsOn();
402 : }
403 : else // There is more than one put, and which one is on is selected by the value of the switch
404 : {
405 0 : 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 0 : pptr = m_node->inputs().begin()->second;
414 : }
415 0 : catch( ... )
416 : {
417 0 : return;
418 0 : }
419 :
420 : // the single node is always on if any are on
421 0 : pptr->enabled( true );
422 0 : pptr->state( ingr::putState::on );
423 :
424 0 : ingr::putState inst = pptr->state();
425 0 : if( inst != ingr::putState::on )
426 : {
427 0 : inst = ingr::putState::waiting;
428 : }
429 :
430 : // Now deal with the many
431 0 : for( auto s : m_presetPutName )
432 : {
433 : try
434 : {
435 0 : pptr = m_node->output( s );
436 : }
437 0 : catch( ... )
438 : {
439 0 : return;
440 0 : }
441 :
442 0 : if( s == m_curVal || m_alwaysOn.count( s ) == 1 )
443 : {
444 0 : pptr->enabled( true );
445 0 : pptr->state( inst );
446 : }
447 : else
448 : {
449 0 : pptr->state( ingr::putState::off );
450 :
451 0 : if( m_noAutoOn.count( s ) == 1 ) // if we turn it off, we disable it
452 : {
453 0 : pptr->enabled( false );
454 : }
455 : }
456 0 : }
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 0 : pptr = m_node->outputs().begin()->second;
467 : }
468 0 : catch( ... )
469 : {
470 0 : return;
471 0 : }
472 :
473 : // the single node is always on if any are on
474 0 : pptr->enabled( true );
475 0 : pptr->state( ingr::putState::on );
476 :
477 : // Now deal with the many
478 0 : for( auto s : m_presetPutName )
479 : {
480 : try
481 : {
482 0 : pptr = m_node->input( s );
483 : }
484 0 : catch( ... )
485 : {
486 0 : return;
487 0 : }
488 :
489 0 : if( s == m_curVal || m_alwaysOn.count( s ) == 1 )
490 : {
491 0 : pptr->enabled( true );
492 0 : pptr->state( ingr::putState::on );
493 : }
494 : else
495 : {
496 0 : pptr->state( ingr::putState::off );
497 :
498 0 : if( m_noAutoOn.count( s ) == 1 ) // if we turn it off, we disable it
499 : {
500 0 : pptr->enabled( false );
501 : }
502 : }
503 0 : }
504 : }
505 : }
506 0 : m_parentGraph->stateChange();
507 : }
508 :
509 0 : return;
510 : }
511 :
512 0 : inline void stdMotionNode::togglePutsOff()
513 : {
514 0 : if( m_node == nullptr || !m_parentGraph || !m_node->auxDataValid() )
515 : {
516 0 : return;
517 : }
518 :
519 0 : if( m_tracking ) // regardless of whether required, if tracking this is our state
520 : {
521 0 : m_curLabel = "tracking";
522 0 : m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "tracking" );
523 0 : m_parentGraph->valueExtra( m_node->name(), "state", "tracking" );
524 : }
525 0 : else if( m_trackingReq ) // we can only be "not tracking" if tracking is required
526 : {
527 0 : m_curLabel = "not tracking";
528 0 : m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "not tracking" );
529 0 : m_parentGraph->valueExtra( m_node->name(), "state", "not tracking" );
530 : }
531 0 : else if( m_presetPutName.size() == 1 ) // otherwise, if we have a single node it's off
532 : {
533 0 : m_curLabel = "off";
534 0 : m_parentGraph->valuePut( name(), m_presetPutName[0], m_presetDir, "off" );
535 0 : m_parentGraph->valueExtra( m_node->name(), "state", "---" );
536 : }
537 : else
538 : {
539 : // We don't change labels if m_presetPutName.size() > 1
540 0 : m_parentGraph->valueExtra( m_node->name(), "state", "---" );
541 : }
542 :
543 : // replace xigNode::togglePutsOff() so we can check for always-on
544 :
545 0 : for( auto &&iput : m_node->inputs() )
546 : {
547 0 : if( m_alwaysOn.count( iput.second->name() ) > 0 )
548 : {
549 0 : continue;
550 : }
551 :
552 : // iput.second->enabled(false);
553 0 : iput.second->state( ingr::putState::off );
554 : }
555 :
556 0 : for( auto &&oput : m_node->outputs() )
557 : {
558 0 : if( m_alwaysOn.count( oput.second->name() ) > 0 )
559 : {
560 0 : continue;
561 : }
562 : // oput.second->enabled(false);
563 0 : oput.second->state( ingr::putState::off );
564 :
565 0 : if( m_noAutoOn.count( oput.second->name() ) == 1 ) // if we turn it off, we disable it
566 : {
567 0 : oput.second->enabled( false );
568 : }
569 : }
570 : }
571 :
572 1 : inline void stdMotionNode::loadConfig( mx::app::appConfigurator &config )
573 : {
574 1 : if( !m_parentGraph )
575 : {
576 0 : std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "parent graph is null" );
577 0 : throw std::runtime_error( msg );
578 0 : }
579 :
580 1 : std::string type;
581 1 : config.configUnused( type, mx::app::iniFile::makeKey( name(), "type" ) );
582 :
583 1 : if( type != "stdMotion" )
584 : {
585 0 : std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "node type is not stdMotion" );
586 0 : throw std::runtime_error( msg );
587 0 : }
588 :
589 1 : std::string dev = name();
590 2 : config.configUnused( dev, mx::app::iniFile::makeKey( name(), "device" ) );
591 :
592 2 : std::string prePrefix = "preset";
593 2 : config.configUnused( prePrefix, mx::app::iniFile::makeKey( name(), "presetPrefix" ) );
594 :
595 2 : std::string preDir = "output";
596 1 : config.configUnused( preDir, mx::app::iniFile::makeKey( name(), "presetDir" ) );
597 :
598 1 : if( preDir == "input" )
599 : {
600 0 : presetDir( ingr::ioDir::input );
601 : }
602 1 : else if( preDir == "output" )
603 : {
604 1 : presetDir( ingr::ioDir::output );
605 : }
606 : else
607 : {
608 0 : std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "invalid presetDir (must be input or output)" );
609 0 : throw std::runtime_error( msg );
610 0 : }
611 :
612 3 : std::vector<std::string> prePutName( { "out" } );
613 1 : config.configUnused( prePutName, mx::app::iniFile::makeKey( name(), "presetPutName" ) );
614 1 : if( prePutName.size() == 0 )
615 : {
616 0 : std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "presetPutName can't be empty" );
617 0 : throw std::runtime_error( msg );
618 0 : }
619 :
620 1 : std::vector<std::string> alwaysOn;
621 1 : config.configUnused( alwaysOn, mx::app::iniFile::makeKey( name(), "alwaysOn" ) );
622 : try
623 : {
624 1 : for( auto &ao : alwaysOn )
625 : {
626 0 : m_alwaysOn.insert( ao );
627 : }
628 : }
629 0 : catch( const std::exception &e )
630 : {
631 0 : std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "exception from insert in m_alwaysOn" );
632 0 : msg += ":";
633 0 : msg += e.what();
634 0 : throw std::runtime_error( msg );
635 0 : }
636 :
637 1 : std::vector<std::string> noAutoOn;
638 1 : config.configUnused( noAutoOn, mx::app::iniFile::makeKey( name(), "noAutoOn" ) );
639 : try
640 : {
641 1 : for( auto &ao : noAutoOn )
642 : {
643 0 : m_noAutoOn.insert( ao );
644 : }
645 : }
646 0 : catch( const std::exception &e )
647 : {
648 0 : std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig", "exception from insert in m_noAutoOn" );
649 0 : msg += ":";
650 0 : msg += e.what();
651 0 : throw std::runtime_error( msg );
652 0 : }
653 :
654 1 : std::string trackReqKey;
655 1 : config.configUnused( trackReqKey, mx::app::iniFile::makeKey( name(), "trackingReqKey" ) );
656 :
657 1 : std::string trackReqEl;
658 1 : config.configUnused( trackReqEl, mx::app::iniFile::makeKey( name(), "trackingReqElement" ) );
659 :
660 : // Check if both are set
661 1 : if( ( trackReqKey == "" && trackReqEl != "" ) || ( trackReqKey != "" && trackReqEl == "" ) )
662 : {
663 0 : std::string msg = XIGN_EXCEPTION( "stdMotionNode::loadConfig",
664 : "trackingReqKey and trackingReqElement must both be provided" );
665 0 : throw std::runtime_error( msg );
666 0 : }
667 :
668 1 : std::string trackKey;
669 1 : config.configUnused( trackKey, mx::app::iniFile::makeKey( name(), "trackerKey" ) );
670 :
671 1 : std::string trackEl;
672 1 : config.configUnused( trackEl, mx::app::iniFile::makeKey( name(), "trackerElement" ) );
673 :
674 : // Check if both are set
675 1 : if( ( trackKey == "" && trackEl != "" ) || ( trackKey != "" && trackEl == "" ) )
676 : {
677 : std::string msg =
678 0 : XIGN_EXCEPTION( "stdMotionNode::loadConfig", "trackingKey and trackingElement must both be provided" );
679 0 : throw std::runtime_error( msg );
680 0 : }
681 :
682 : // This will catch the case where one or the other pair was set, but not both
683 1 : if( ( trackKey == "" && trackReqKey != "" ) || ( trackKey != "" && trackReqKey == "" ) )
684 : {
685 : std::string msg =
686 0 : XIGN_EXCEPTION( "stdMotionNode::loadConfig", "trackingReqKey and trackerKey must both be provided" );
687 0 : throw std::runtime_error( msg );
688 0 : }
689 :
690 1 : device( dev );
691 1 : presetPrefix( prePrefix );
692 1 : presetPutName( prePutName );
693 1 : trackingReqKey( trackReqKey );
694 1 : trackingReqElement( trackReqEl );
695 1 : trackerKey( trackKey );
696 1 : trackerElement( trackEl );
697 4 : }
698 :
699 : #endif // stdMotionNode_hpp
|