Line data Source code
1 : /** \file fsmNode.hpp
2 : * \brief The MagAO-X Instrument Graph fsmNode header file
3 : *
4 : * \ingroup instGraph_files
5 : */
6 :
7 : #ifndef fsmNode_hpp
8 : #define fsmNode_hpp
9 :
10 : #include "xigNode.hpp"
11 :
12 : enum class fsmNodeActionT
13 : {
14 : passive, /**< Only monitor and report FSM state, don't change puts*/
15 : threshOff, /**< If state is not in one of the specified states, turn puts off*/
16 : active, /**< If state is in one of the specified states, turn puts on. Turn them off otherwise.*/
17 : unknown /**< Unknown action, generally an error. */
18 : };
19 :
20 15 : std::string fsmNodeActionT2String( fsmNodeActionT action )
21 : {
22 15 : if( action == fsmNodeActionT::passive )
23 : {
24 30 : return "passive";
25 : }
26 0 : else if( action == fsmNodeActionT::threshOff )
27 : {
28 0 : return "threshOff";
29 : }
30 0 : else if( action == fsmNodeActionT::active )
31 : {
32 0 : return "active";
33 : }
34 : else
35 : {
36 0 : return "";
37 : }
38 : }
39 :
40 15 : fsmNodeActionT fsmNodeActionTFromString( const std::string &action )
41 : {
42 15 : if( action == "passive" )
43 : {
44 13 : return fsmNodeActionT::passive;
45 : }
46 2 : else if( action == "threshOff" )
47 : {
48 1 : return fsmNodeActionT::threshOff;
49 : }
50 1 : else if( action == "active" )
51 : {
52 1 : return fsmNodeActionT::active;
53 : }
54 : else
55 : {
56 0 : return fsmNodeActionT::unknown;
57 : }
58 : }
59 :
60 : /// Implementation of an instGraph node interface for a MagAO-X Finite State Machine (FSM)
61 : /** This class is interraces to a standard FSM. It tracks the FSM state INDI property
62 : * and keeps its internal state updated.
63 : *
64 : * Whether it impacts ioput status depends on the `action` specified.
65 : *
66 : */
67 :
68 : class fsmNode : public xigNode
69 : {
70 :
71 : typedef MagAOX::app::stateCodes::stateCodeT stateCodeT;
72 :
73 : protected:
74 : std::string m_device; ///< The INDI device name. Defaults to the node name set on construction.
75 : std::string m_fsmPropName{ "fsm" }; ///< The INDI property name for the FSM, normally "fsm".
76 : std::string m_fsmElName{ "state" }; ///< The INDI property element name for the FSM, normally "state".
77 :
78 : std::string m_fsmKey; ///< The unique INDI key, `<device>.<fsmPropName>`, for the FSM state INDI property.
79 :
80 : fsmNodeActionT m_fsmAction{ fsmNodeActionT::passive };
81 :
82 : std::vector<stateCodeT> m_targetStates;
83 :
84 : stateCodeT m_state{ -999 }; ///< The numerical code of the current state.
85 : std::string m_stateStr; ///< The string name of the current state.
86 :
87 : bool m_stateOnTarget{ false }; ///< Flag indicating if the current state matches any of the target states.
88 :
89 : public:
90 : /// Constructor.
91 : /**
92 : * Default c'tor is deleted in base classs. Must supply both node name and a parentGraph with a node with the same
93 : * name in it.
94 : */
95 : fsmNode( const std::string &name, /**< [in] the name of the node */
96 : ingr::instGraphXML *parentGraph /**< [in] the parent instGraph */
97 : );
98 :
99 : /// Set the device name
100 : /**
101 : * Derived classes may implement this to add extra logic. The device name defaults
102 : * to the node name on construction.
103 : */
104 : virtual void device( const std::string &dev /**< [in] the new device name */ );
105 :
106 : /// Get the device name
107 : /**
108 : * \return the current value of m_device
109 : */
110 : const std::string &device() const;
111 :
112 : /// Set the fsm property name
113 : /**
114 : * Derived classes may implement this to add extra logic. The fsm property name defaults
115 : * to "fsm"
116 : *
117 : * This can only be called before device is set
118 : */
119 : virtual void fsmPropName( const std::string &pn /**< [in] the new property name */ );
120 :
121 : /// Get the fsm property name
122 : /**
123 : * \return the current value of m_fsmPropName
124 : */
125 : const std::string &fsmPropName() const;
126 :
127 : /// Set the fsm element name
128 : /**
129 : * Derived classes may implement this to add extra logic. The fsm element name defaults
130 : * to "state"
131 : *
132 : * This can be called at any time
133 : */
134 : virtual void fsmElName( const std::string &en /**< [in] the new element name */ );
135 :
136 : /// Get the fsm element name
137 : /**
138 : * \return the current value of m_fsmElName
139 : */
140 : const std::string &fsmElName() const;
141 :
142 : /// Get the FSM unique key
143 : /**
144 : * \return the current value of m_fsmKey
145 : */
146 : const std::string &fsmKey() const;
147 :
148 : /// Get the action
149 : /**
150 : * \return the current value of m_fsmAction
151 : */
152 : fsmNodeActionT fsmAction() const;
153 :
154 : /// Set the action
155 : void fsmAction( fsmNodeActionT act );
156 :
157 : /// Get the target states
158 : /**
159 : * \return the current value of m_targetStates
160 : */
161 : const std::vector<stateCodeT> &targetStates() const;
162 :
163 : /// Load this specific node's settings from an application configuration
164 : /**
165 : * Verifies that the named node is an fsmNode.
166 : *
167 : * \throws std::runtime_error if m_parentGraph is nullptr or the config is not for an fsmNode.
168 : */
169 : void loadConfig( mx::app::appConfigurator &config /**< [in] the application configurator
170 : loaded with this node's options*/ );
171 :
172 : protected:
173 : /// Load this specific node's settings from an application configuration of a derived class
174 : /**
175 : * Does not cerifies that the named node is an fsmNode.
176 : *
177 : */
178 : void loadConfigDerived( mx::app::appConfigurator &config /**< [in] the application configurator
179 : loaded with this node's options*/ );
180 :
181 : public:
182 : /// INDI SetProperty callback
183 : virtual int handleSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the received INDI property to handle*/ );
184 :
185 : /// INDI SetProperty callback with indication if action was taken
186 : /** The possible actions are determined by m_fsmAction. If the action was taken then the caller
187 : * should return without further processing.
188 : *
189 : */
190 : virtual int handleSetProperty( bool &actionTaken, /** < [out] indicates if action taken (true). */
191 : const pcf::IndiProperty &ipRecv /**< [in] the received INDI property to handle*/ );
192 :
193 : public:
194 : virtual void updateGUI();
195 : };
196 :
197 173 : inline fsmNode::fsmNode( const std::string &name, ingr::instGraphXML *parentGraph ) : xigNode( name, parentGraph )
198 : {
199 34 : }
200 :
201 22 : inline void fsmNode::device( const std::string &dev )
202 : {
203 22 : if( m_device != "" && dev != m_device )
204 : {
205 1 : std::string msg = "fsmNode::device attempt to change device name from " + m_device + " to " + dev;
206 1 : msg += " in " + name();
207 1 : msg += " at ";
208 1 : msg += __FILE__;
209 1 : msg += " " + std::to_string( __LINE__ );
210 1 : throw std::runtime_error( msg );
211 1 : }
212 :
213 21 : if( dev == "" )
214 : {
215 0 : std::string msg = "fsmNode::device attempt to set empty device name in " + name();
216 0 : msg += " at ";
217 0 : msg += __FILE__;
218 0 : msg += " " + std::to_string( __LINE__ );
219 0 : throw std::runtime_error( msg );
220 0 : }
221 :
222 21 : m_device = dev;
223 21 : m_fsmKey = m_device + '.' + m_fsmPropName;
224 :
225 21 : key( m_fsmKey );
226 21 : }
227 :
228 5 : inline const std::string &fsmNode::device() const
229 : {
230 5 : return m_device;
231 : }
232 :
233 15 : inline void fsmNode::fsmPropName( const std::string &pn )
234 : {
235 15 : if( m_fsmPropName != "" && m_device != "" )
236 : {
237 0 : std::string msg = "fsmNode::fsmPropName attempt to change fsmPropName name from " + m_fsmPropName + " to " + pn;
238 0 : msg += " in " + name();
239 0 : msg += " at ";
240 0 : msg += __FILE__;
241 0 : msg += " " + std::to_string( __LINE__ );
242 0 : throw std::runtime_error( msg );
243 0 : }
244 :
245 15 : if( m_device != "" )
246 : {
247 0 : std::string msg = "fsmNode::fsmPropName attempt to set propName after device already set " + name();
248 0 : msg += " at ";
249 0 : msg += __FILE__;
250 0 : msg += " " + std::to_string( __LINE__ );
251 0 : throw std::runtime_error( msg );
252 0 : }
253 :
254 15 : if( pn == "" )
255 : {
256 0 : std::string msg = "fsmNode::fsmPropName attempt to set propName to empty " + name();
257 0 : msg += " at ";
258 0 : msg += __FILE__;
259 0 : msg += " " + std::to_string( __LINE__ );
260 0 : throw std::runtime_error( msg );
261 0 : }
262 :
263 15 : m_fsmPropName = pn;
264 15 : }
265 :
266 15 : inline const std::string &fsmNode::fsmPropName() const
267 : {
268 15 : return m_fsmPropName;
269 : }
270 :
271 15 : inline void fsmNode::fsmElName( const std::string &en )
272 : {
273 15 : if( en == "" )
274 : {
275 0 : std::string msg = "fsmNode::fsmElName attempt to set elName to empty " + name();
276 0 : msg += " at ";
277 0 : msg += __FILE__;
278 0 : msg += " " + std::to_string( __LINE__ );
279 0 : throw std::runtime_error( msg );
280 0 : }
281 :
282 15 : m_fsmElName = en;
283 15 : }
284 :
285 15 : inline const std::string &fsmNode::fsmElName() const
286 : {
287 15 : return m_fsmElName;
288 : }
289 :
290 3 : const std::string &fsmNode::fsmKey() const
291 : {
292 3 : return m_fsmKey;
293 : }
294 :
295 3 : fsmNodeActionT fsmNode::fsmAction() const
296 : {
297 3 : return m_fsmAction;
298 : }
299 :
300 0 : void fsmNode::fsmAction( fsmNodeActionT act )
301 : {
302 0 : m_fsmAction = act;
303 0 : }
304 :
305 6 : const std::vector<fsmNode::stateCodeT> &fsmNode::targetStates() const
306 : {
307 6 : return m_targetStates;
308 : }
309 :
310 4 : inline void fsmNode::loadConfig( mx::app::appConfigurator &config )
311 : {
312 4 : if( !m_parentGraph )
313 : {
314 0 : std::string msg = "fsmNode::loadConfig: parent graph is null";
315 0 : msg += " at ";
316 0 : msg += __FILE__;
317 0 : msg += " " + std::to_string( __LINE__ );
318 0 : throw std::runtime_error( msg );
319 0 : }
320 :
321 4 : std::string type;
322 4 : config.configUnused( type, mx::app::iniFile::makeKey( name(), "type" ) );
323 :
324 4 : if( type != "fsm" )
325 : {
326 0 : std::string msg = "fsmNode::loadConfig: node type is not fsmNode";
327 0 : msg += " at ";
328 0 : msg += __FILE__;
329 0 : msg += " " + std::to_string( __LINE__ );
330 0 : throw std::runtime_error( msg );
331 0 : }
332 :
333 4 : loadConfigDerived( config );
334 4 : }
335 :
336 15 : inline void fsmNode::loadConfigDerived( mx::app::appConfigurator &config )
337 : {
338 : //This must be first
339 15 : std::string propName = fsmPropName();
340 15 : config.configUnused( propName, mx::app::iniFile::makeKey( name(), "fsmPropName" ) );
341 15 : fsmPropName( propName );
342 :
343 15 : std::string dev = name();
344 15 : config.configUnused( dev, mx::app::iniFile::makeKey( name(), "device" ) );
345 15 : device( dev );
346 :
347 15 : std::string elName = fsmElName();
348 15 : config.configUnused( elName, mx::app::iniFile::makeKey( name(), "fsmElName" ) );
349 15 : fsmElName( elName );
350 :
351 15 : std::string action = fsmNodeActionT2String( m_fsmAction );
352 15 : config.configUnused( action, mx::app::iniFile::makeKey( name(), "fsmAction" ) );
353 15 : m_fsmAction = fsmNodeActionTFromString( action );
354 :
355 15 : if( m_fsmAction == fsmNodeActionT::unknown )
356 : {
357 0 : std::string msg = XIGN_EXCEPTION( "fsmNode::loadConfig", "fsmAction is unknown" );
358 0 : throw std::runtime_error( msg );
359 0 : }
360 :
361 15 : std::vector<std::string> targetStates;
362 15 : config.configUnused( targetStates, mx::app::iniFile::makeKey( name(), "targetStates" ) );
363 15 : m_targetStates.resize( targetStates.size() );
364 18 : for( size_t n = 0; n < targetStates.size(); ++n )
365 : {
366 3 : m_targetStates[n] = MagAOX::app::stateCodes::str2Code( targetStates[n] );
367 : }
368 :
369 15 : if( m_parentGraph && m_node )
370 : {
371 75 : m_parentGraph->valueExtra( m_node->name(), "state", "" );
372 : }
373 15 : }
374 :
375 14 : inline int fsmNode::handleSetProperty( const pcf::IndiProperty &ipRecv )
376 : {
377 : bool actionTaken;
378 28 : return handleSetProperty( actionTaken, ipRecv );
379 : }
380 :
381 22 : inline int fsmNode::handleSetProperty( bool &actionTaken, const pcf::IndiProperty &ipRecv )
382 : {
383 22 : if( ipRecv.createUniqueKey() != m_fsmKey )
384 : {
385 19 : actionTaken = false;
386 19 : return 0;
387 : }
388 :
389 3 : if( !ipRecv.find( m_fsmElName ) )
390 : {
391 0 : actionTaken = false;
392 0 : return 0;
393 : }
394 :
395 3 : m_stateStr = ipRecv[m_fsmElName].get<std::string>();
396 :
397 3 : MagAOX::app::stateCodes::stateCodeT state = MagAOX::app::stateCodes::str2CodeFast( m_stateStr );
398 :
399 3 : if( state != m_state )
400 : {
401 3 : ++m_changes;
402 : }
403 :
404 3 : m_state = state;
405 :
406 6 : m_parentGraph->valueExtra( m_node->name(), "fsmstate", m_stateStr );
407 :
408 3 : bool stateOnTarget = false;
409 :
410 3 : for( auto state : m_targetStates )
411 : {
412 0 : if( m_state == state )
413 : {
414 0 : stateOnTarget = true;
415 0 : break;
416 : }
417 : }
418 3 : m_stateOnTarget = stateOnTarget;
419 :
420 3 : if( m_fsmAction == fsmNodeActionT::threshOff )
421 : {
422 0 : if( m_stateOnTarget )
423 : {
424 0 : actionTaken = false;
425 0 : return 0;
426 : }
427 : else
428 : {
429 0 : togglePutsOff();
430 0 : actionTaken = true;
431 0 : return 0;
432 : }
433 : }
434 3 : else if( m_fsmAction == fsmNodeActionT::active )
435 : {
436 0 : if( m_stateOnTarget )
437 : {
438 0 : togglePutsOn();
439 0 : actionTaken = true;
440 0 : return 0;
441 : }
442 : else
443 : {
444 0 : togglePutsOff();
445 0 : actionTaken = true;
446 0 : return 0;
447 : }
448 : }
449 : else // m_fsmAction == fsmNodeActionT::passive
450 : {
451 3 : actionTaken = false;
452 3 : return 0;
453 : }
454 : }
455 :
456 0 : inline void fsmNode::updateGUI()
457 : {
458 0 : }
459 :
460 : #endif // fsmNode_hpp
|