Line data Source code
1 : /** \file indiPropNode.hpp
2 : * \brief The MagAO-X Instrument Graph indiPropNode header file
3 : *
4 : * \ingroup instGraph_files
5 : */
6 :
7 : #ifndef indiPropNode_hpp
8 : #define indiPropNode_hpp
9 :
10 : #include "fsmNode.hpp"
11 :
12 : /// An instGraph node which tracks a specific INDI property and element of that property.
13 : /** When the element matches the target value all puts are turned on. All puts are off
14 : * otherwise.
15 : */
16 : class indiPropNode : public fsmNode
17 : {
18 :
19 : protected:
20 : std::string m_propKey; ///< unique key, device.name, of the property tp track
21 : std::string m_propEl; ///< the element of the property to track
22 :
23 : std::string m_propValStr; ///< the target value of the element. This is always set.
24 :
25 : double m_propValNum{ std::numeric_limits<double>::lowest() }; /**< the numeric target value, set from
26 : m_propValStr if the property is a number.*/
27 :
28 : /** The switch target value, set from m_propValStr if the property is a switch. In this case
29 : * m_propValStr can have values `On` or `Off`. The comparison is made insensitive to case (ON and off
30 : * are valid).
31 : */
32 : pcf::IndiElement::SwitchStateType m_propValSw{ pcf::IndiElement::SwitchStateType::UnknownSwitchState };
33 :
34 : /// The property type. Discovered introspectively on first call to \ref handleSetProperty.
35 : pcf::IndiProperty::Type m_type{ pcf::IndiProperty::Unknown };
36 :
37 : double m_tol{ 1e-7 }; ///< The tolerance for floating point comparison. Default is 1e-7.
38 :
39 : bool m_state{ false }; ///< The current state of the comparison.
40 :
41 : bool m_first{ true }; ///< Flag indicating if it's the first call to \ref handleSetProperty
42 :
43 : std::string m_onStr {"ON"};
44 : std::string m_offStr {"OFF"};
45 :
46 : public:
47 : /// Only c'tor. Must be constructed with node name and a parent graph.
48 : indiPropNode( const std::string &name, /** [in] the name of this node*/
49 : ingr::instGraphXML *parentGraph /** [in] the graph which this node belongs to*/
50 : );
51 :
52 : /// Set the unique key of the INDI property to track
53 : void propKey( const std::string &pk /** [in] */ );
54 :
55 : /// Get the unique key of the INDI property to track
56 : /**
57 : * \returns the value of m_propKey
58 : */
59 : const std::string &propKey() const;
60 :
61 : /// Set the element of the INDI property to track
62 : void propEl( const std::string &pe /** [in] */ );
63 :
64 : /// Get the element of the INDI property to track
65 : /**
66 : * \returns the value of m_propEl
67 : */
68 : const std::string &propEl() const;
69 :
70 : /// Set the target value of the INDI element.
71 : /** Always set in its string form and converted as needed
72 : */
73 : void propValStr( const std::string &pv /** [in] */ );
74 :
75 : /// Get the target value of the INDI element.
76 : /**
77 : * \returns the value of m_propValStr
78 : */
79 : const std::string &propValStr() const;
80 :
81 : /// Get the target value of the INDI element if it's a number.
82 : /**
83 : * \returns the value of m_propValNum
84 : */
85 : const double &propValNum() const;
86 :
87 : /// Get the target value of the INDI element if it's a switch.
88 : /**
89 : * \returns the value of m_propValSw
90 : */
91 : const pcf::IndiElement::SwitchStateType &propValSw();
92 :
93 : /// Get the type of the INDI property being tracked
94 : /**
95 : * \returns the value of m_type
96 : */
97 : const pcf::IndiProperty::Type &type() const;
98 :
99 : /// Get the tolerance used for numeric comparison
100 : /**
101 : * \returns the value of m_tol
102 : */
103 : const double &tol() const;
104 :
105 : /// Get the current value of the comparison
106 : /**
107 : * \returns the value of m_state
108 : */
109 : const bool &state() const;
110 :
111 : protected:
112 : /// On first call to handleSetProperty we find the property type and convert the target value
113 : virtual int firstSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the received INDI property*/ );
114 :
115 : public:
116 : /// INDI SetProperty callback
117 : virtual int handleSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the received INDI property to handle*/ );
118 :
119 : /// Toggle all puts on
120 : virtual void toggleOn();
121 :
122 : /// Toggle all puts off
123 : virtual void toggleOff();
124 :
125 : /// Configure this node form an appConfigurator.
126 : void loadConfig( mx::app::appConfigurator &config /**< [in] the loaded configuration */ );
127 : };
128 :
129 50 : indiPropNode::indiPropNode( const std::string &name, ingr::instGraphXML *parentGraph ) : fsmNode( name, parentGraph )
130 : {
131 12 : if( m_parentGraph )
132 : {
133 60 : m_parentGraph->valueExtra( m_node->name(), "fsmstate", "" );
134 : }
135 12 : }
136 :
137 8 : inline void indiPropNode::propKey( const std::string &pk )
138 : {
139 8 : m_propKey = pk;
140 :
141 8 : key( m_propKey );
142 8 : }
143 :
144 2 : const std::string &indiPropNode::propKey() const
145 : {
146 2 : return m_propKey;
147 : }
148 :
149 : inline void indiPropNode::propEl( const std::string &pe )
150 : {
151 : m_propEl = pe;
152 : }
153 :
154 2 : const std::string &indiPropNode::propEl() const
155 : {
156 2 : return m_propEl;
157 : }
158 :
159 : inline void indiPropNode::propValStr( const std::string &pv )
160 : {
161 : m_propValStr = pv;
162 : }
163 :
164 2 : const std::string &indiPropNode::propValStr() const
165 : {
166 2 : return m_propValStr;
167 : }
168 :
169 3 : const double &indiPropNode::propValNum() const
170 : {
171 3 : return m_propValNum;
172 : }
173 :
174 3 : const pcf::IndiElement::SwitchStateType &indiPropNode::propValSw()
175 : {
176 3 : return m_propValSw;
177 : }
178 :
179 5 : const pcf::IndiProperty::Type &indiPropNode::type() const
180 : {
181 5 : return m_type;
182 : }
183 :
184 2 : const double &indiPropNode::tol() const
185 : {
186 2 : return m_tol;
187 : }
188 :
189 8 : const bool &indiPropNode::state() const
190 : {
191 8 : return m_state;
192 : }
193 :
194 5 : inline int indiPropNode::firstSetProperty( const pcf::IndiProperty &ipRecv )
195 : {
196 : // On first call we figure what type it is and convert the value
197 :
198 5 : if( ipRecv.getType() == pcf::IndiProperty::Type::Number )
199 : {
200 2 : m_type = pcf::IndiProperty::Type::Number;
201 : try
202 : {
203 2 : m_propValNum = std::stod( m_propValStr );
204 : }
205 1 : catch( const std::exception &e )
206 : {
207 5 : std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "exception caught" );
208 1 : msg += ": ";
209 1 : msg += e.what();
210 :
211 1 : throw std::runtime_error( msg );
212 2 : }
213 : }
214 3 : else if( ipRecv.getType() == pcf::IndiProperty::Type::Switch )
215 : {
216 2 : m_type = pcf::IndiProperty::Type::Switch;
217 : try
218 : {
219 2 : std::string ustr = m_propValStr;
220 2 : std::transform( m_propValStr.begin(), m_propValStr.end(), ustr.begin(), ::toupper );
221 :
222 2 : if( ustr == "ON" )
223 : {
224 1 : m_propValSw = pcf::IndiElement::SwitchStateType::On;
225 : }
226 1 : else if( ustr == "OFF" )
227 : {
228 0 : m_propValSw = pcf::IndiElement::SwitchStateType::Off;
229 : }
230 : else
231 : {
232 5 : std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "invalid switch state" );
233 1 : throw std::invalid_argument( msg );
234 1 : }
235 2 : }
236 1 : catch( const std::exception &e )
237 : {
238 5 : std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "exception caught" );
239 1 : msg += ": ";
240 1 : msg += e.what();
241 :
242 1 : throw std::runtime_error( msg );
243 2 : }
244 : }
245 1 : else if( ipRecv.getType() == pcf::IndiProperty::Type::Text )
246 : {
247 1 : m_type = pcf::IndiProperty::Type::Text;
248 : }
249 : else
250 : {
251 0 : std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "INDI property of type not implemented" );
252 0 : throw std::runtime_error( msg );
253 0 : }
254 :
255 3 : return 0;
256 : }
257 :
258 8 : inline int indiPropNode::handleSetProperty( const pcf::IndiProperty &ipRecv )
259 : {
260 8 : bool actionTaken = false;
261 :
262 8 : int rv = fsmNode::handleSetProperty( actionTaken, ipRecv );
263 8 : if( rv < 0 )
264 : {
265 0 : std::cerr << "Error from fsmNode::handleSetProperty\n";
266 0 : return rv;
267 : }
268 :
269 8 : if( actionTaken )
270 : {
271 0 : return 0;
272 : }
273 :
274 8 : if( ipRecv.createUniqueKey() != m_propKey )
275 : {
276 0 : return 0;
277 : }
278 :
279 8 : if( !ipRecv.find( m_propEl ) )
280 : {
281 0 : std::cerr << "!ipRecv.find( m_propEl )\n";
282 0 : return -1;
283 : }
284 :
285 8 : if( m_first )
286 : {
287 : try
288 : {
289 5 : int rv = firstSetProperty( ipRecv );
290 3 : if(rv < 0)
291 : {
292 0 : std::cerr << "Error from firstSetProperty\n";
293 0 : return rv;
294 : }
295 : }
296 2 : catch( const std::exception &e )
297 : {
298 10 : std::string msg = XIGN_EXCEPTION( "indiPropNode::handleSetProperty", "exception caught" );
299 2 : msg += ": ";
300 2 : msg += e.what();
301 :
302 2 : throw std::runtime_error( msg );
303 4 : }
304 : }
305 :
306 6 : bool on = false;
307 :
308 6 : if( m_type == pcf::IndiProperty::Type::Number )
309 : {
310 2 : if( fabs( ipRecv[m_propEl].get<double>() - m_propValNum ) <= m_tol )
311 : {
312 1 : on = true;
313 : }
314 : }
315 4 : else if( m_type == pcf::IndiProperty::Type::Switch )
316 : {
317 2 : if( ipRecv[m_propEl].getSwitchState() == m_propValSw )
318 : {
319 1 : on = true;
320 : }
321 : }
322 2 : else if( m_type == pcf::IndiProperty::Type::Text )
323 : {
324 2 : if( ipRecv[m_propEl].get() == m_propValStr )
325 : {
326 1 : on = true;
327 : }
328 : }
329 : else
330 : {
331 0 : std::string msg = XIGN_EXCEPTION( "indiPropNode::handleSetProperty", "type not implemented" );
332 0 : throw std::runtime_error( msg );
333 0 : }
334 :
335 6 : if( m_first )
336 : {
337 : // trigger the change on the first run.
338 3 : if( on )
339 : {
340 3 : m_state = false;
341 : }
342 : else
343 : {
344 0 : m_state = true;
345 : }
346 :
347 : // made this it's own branch so we don't do this every time:
348 3 : m_first = false;
349 : }
350 :
351 6 : if( on != m_state )
352 : {
353 6 : ++m_changes;
354 6 : m_state = on;
355 6 : if( on )
356 : {
357 3 : toggleOn();
358 6 : m_parentGraph->valueExtra( m_node->name(), "state", m_onStr );
359 3 : return 0;
360 : }
361 : else
362 : {
363 3 : toggleOff();
364 6 : m_parentGraph->valueExtra( m_node->name(), "state", m_offStr );
365 3 : return 0;
366 : }
367 : }
368 :
369 0 : return 0;
370 : }
371 :
372 3 : inline void indiPropNode::toggleOn()
373 : {
374 3 : togglePutsOn();
375 3 : }
376 :
377 3 : inline void indiPropNode::toggleOff()
378 : {
379 3 : togglePutsOff();
380 3 : }
381 :
382 12 : inline void indiPropNode::loadConfig( mx::app::appConfigurator &config )
383 : {
384 12 : if( !m_parentGraph )
385 : {
386 0 : std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "parent graph is null" );
387 0 : throw std::runtime_error( msg );
388 0 : }
389 :
390 12 : std::string type;
391 12 : config.configUnused( type, mx::app::iniFile::makeKey( name(), "type" ) );
392 :
393 12 : if( type != "indiProp" )
394 : {
395 5 : std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "node type is not indiProp" );
396 1 : throw std::runtime_error( msg );
397 1 : }
398 :
399 11 : fsmNode::loadConfigDerived( config );
400 :
401 11 : std::string pk;
402 11 : config.configUnused( pk, mx::app::iniFile::makeKey( name(), "propKey" ) );
403 :
404 11 : if( pk == "" )
405 : {
406 5 : std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "propKey can not be empty" );
407 1 : throw std::runtime_error( msg );
408 1 : }
409 :
410 10 : std::string pe;
411 10 : config.configUnused( pe, mx::app::iniFile::makeKey( name(), "propEl" ) );
412 :
413 10 : if( pe == "" )
414 : {
415 5 : std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "propEl can not be empty" );
416 1 : throw std::runtime_error( msg );
417 1 : }
418 :
419 9 : std::string pv;
420 9 : config.configUnused( pv, mx::app::iniFile::makeKey( name(), "propVal" ) );
421 :
422 9 : if( pv == "" )
423 : {
424 5 : std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "propVal can not be empty" );
425 1 : throw std::runtime_error( msg );
426 1 : }
427 :
428 8 : config.configUnused( m_tol, mx::app::iniFile::makeKey( name(), "tol" ) );
429 :
430 : // Add propEl and propVal
431 8 : propKey( pk );
432 8 : m_propEl = pe;
433 8 : m_propValStr = pv;
434 :
435 16 : config.configUnused( m_onStr, mx::app::iniFile::makeKey( name(), "onStr" ) );
436 8 : config.configUnused( m_offStr, mx::app::iniFile::makeKey( name(), "offStr" ) );
437 18 : }
438 :
439 : #endif // indiPropNode_hpp
|