Line data Source code
1 : /** \file indiUtils.hpp
2 : * \brief MagAO-X INDI Utilities
3 : * \author Jared R. Males (jaredmales@gmail.com)
4 : *
5 : * History:
6 : * - 2019-01-03 created by JRM
7 : *
8 : * \ingroup app_files
9 : */
10 :
11 : #ifndef app_indiUtils_hpp
12 : #define app_indiUtils_hpp
13 :
14 : #include <iostream>
15 : #include <limits>
16 :
17 : #include "../../INDI/libcommon/IndiProperty.hpp"
18 : #include "../../INDI/libcommon/IndiElement.hpp"
19 :
20 : namespace MagAOX
21 : {
22 : namespace app
23 : {
24 : namespace indi
25 : {
26 :
27 : #define INDI_IDLE ( pcf::IndiProperty::Idle )
28 : #define INDI_OK ( pcf::IndiProperty::Ok )
29 : #define INDI_BUSY ( pcf::IndiProperty::Busy )
30 : #define INDI_ALERT ( pcf::IndiProperty::Alert )
31 :
32 : /// Add a standard INDI Text element
33 : /**
34 : * \returns 0 on success
35 : * \returns -1 on error
36 : */
37 0 : inline int addTextElement( pcf::IndiProperty &prop, ///< [out] the property to which to add the elemtn
38 : const std::string &name, ///< [in] the name of the element
39 : const std::string &label = "" ///< [in] [optional] the GUI label suggestion for this property
40 : )
41 : {
42 0 : prop.add( pcf::IndiElement( name, "" ) );
43 :
44 : // Don't set "" just in case libcommon does something with defaults
45 0 : if( label != "" )
46 : {
47 0 : prop[name].setLabel( label );
48 : }
49 :
50 0 : return 0;
51 : }
52 :
53 : /// Add a standard INDI Number element
54 : /**
55 : * \returns 0 on success
56 : * \returns -1 on error
57 : */
58 : template <typename T>
59 54 : int addNumberElement( pcf::IndiProperty &prop, ///< [out] the property to which to add the elemtn
60 : const std::string &name, ///< [in] the name of the element
61 : const T &min, ///< [in] the minimum value for the element
62 : const T &max, ///< [in] the minimum value for the element
63 : const T &step, ///< [in] the step size of the lement
64 : const std::string &format, ///< [in] the _ value for the elements, applied to both target and
65 : ///< current. Set to "" to use the MagAO-X standard for type.
66 : const std::string &label = "" ///< [in] [optional] the GUI label suggestion for this property
67 : )
68 : {
69 54 : prop.add( pcf::IndiElement( name, 0 ) );
70 54 : prop[name].setMin( min );
71 54 : prop[name].setMax( max );
72 54 : prop[name].setStep( step );
73 54 : prop[name].setFormat( format );
74 :
75 : // Don't set "" just in case libcommon does something with defaults
76 54 : if( label != "" )
77 : {
78 54 : prop[name].setLabel( label );
79 : }
80 :
81 54 : return 0;
82 : }
83 :
84 : /// Update the value of the INDI element, but only if it has changed.
85 : /** Only sends the set property message if the new value is different.
86 : * For properties with more than one element that may have changed, you should use the vector version below.
87 : *
88 : * \todo this needs a const char specialization to std::string
89 : *
90 : */
91 : template <typename T, class indiDriverT>
92 106 : void updateIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
93 : const std::string &el, ///< [in] The element name
94 : const T &newVal, ///< [in] the new value
95 : indiDriverT *indiDriver, ///< [in] the MagAOX INDI driver to use
96 : pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
97 : {
98 106 : if( !indiDriver )
99 : {
100 90 : return;
101 : }
102 :
103 : try
104 : {
105 : // This is same code in IndiElement
106 16 : std::stringstream ssValue;
107 16 : ssValue.precision( 15 );
108 16 : ssValue << std::boolalpha << newVal;
109 :
110 16 : pcf::IndiProperty::PropertyStateType oldState = p.getState();
111 :
112 : // Do comparison in string space, not raw value
113 16 : if( p[el].getValue() != ssValue.str() || oldState != newState )
114 : {
115 9 : p[el].set( newVal );
116 9 : p.setTimeStamp( pcf::TimeStamp() );
117 9 : p.setState( newState );
118 9 : indiDriver->sendSetProperty( p );
119 : }
120 16 : }
121 0 : catch( std::exception &e )
122 : {
123 0 : std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
124 0 : std::cerr << "from " << p.getName() << "." << el << ": ";
125 0 : std::cerr << e.what() << "\n";
126 : }
127 0 : catch( ... )
128 : {
129 0 : std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
130 0 : std::cerr << "from " << p.getName() << "." << el << "\n";
131 : }
132 : }
133 :
134 : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
135 : /** Only sends the set property message if at least one of the new values is different, or if the state has changed.
136 : *
137 : *
138 : */
139 : template <typename T, class indiDriverT>
140 0 : void updateIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
141 : const std::vector<std::string> &els, ///< [in] The element names
142 : const std::vector<T> &newVals, ///< [in] the new values
143 : indiDriverT *indiDriver, ///< [in] the MagAOX INDI driver to use
144 : pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
145 : {
146 0 : if( !indiDriver )
147 0 : return;
148 :
149 0 : size_t n = 0; // loop index outside so we can use it for error reporting.
150 :
151 : try
152 : {
153 : // First we look for any changes
154 0 : bool changed = false;
155 0 : pcf::IndiProperty::PropertyStateType oldState = p.getState();
156 :
157 0 : if( oldState != newState )
158 0 : changed = true;
159 :
160 0 : for( n = 0; n < els.size() && changed != true; ++n )
161 : {
162 : // This is same code in IndiElement
163 0 : std::stringstream ssValue;
164 0 : ssValue.precision( 15 );
165 0 : ssValue << std::boolalpha << newVals[n];
166 :
167 : // compare in string space
168 0 : if( p[els[n]].getValue() != ssValue.str() )
169 0 : changed = true;
170 : }
171 :
172 : // and if there are changes, we send an update
173 0 : if( changed )
174 : {
175 0 : for( n = 0; n < els.size(); ++n )
176 : {
177 0 : p[els[n]].set( newVals[n] );
178 : }
179 0 : p.setTimeStamp( pcf::TimeStamp() );
180 0 : p.setState( newState );
181 0 : indiDriver->sendSetProperty( p );
182 : }
183 : }
184 0 : catch( std::exception &e )
185 : {
186 0 : std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
187 0 : if( n < els.size() )
188 : {
189 0 : std::cerr << "from " << p.getName() << "." << els[n] << ": ";
190 : }
191 0 : std::cerr << e.what() << "\n";
192 : }
193 0 : catch( ... )
194 : {
195 0 : std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
196 0 : if( n < els.size() )
197 : {
198 0 : std::cerr << "from " << p.getName() << "." << els[n] << "\n";
199 : }
200 : }
201 : }
202 :
203 : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
204 : /** Only sends the set property message if at least one of the new values is different, or if the state has changed.
205 : *
206 : * \tparam T the type of the values
207 : * \tparam elVecT the type of the element names, should be either std::string or const char *
208 : * \tparam indiDriverT the type of the INDI driver class
209 : */
210 : template <typename T, typename elVecT, class indiDriverT>
211 0 : void updatesIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
212 : const elVecT &els, ///< [in] The element names
213 : const std::vector<T> &newVals, ///< [in] the new values
214 : indiDriverT *indiDriver, ///< [in] the MagAOX INDI driver to use
215 : pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
216 : {
217 0 : if( !indiDriver )
218 0 : return;
219 :
220 0 : size_t n = 0; // loop index outside so we can use it for error reporting.
221 :
222 : try
223 : {
224 : // First we look for any changes
225 0 : bool changed = false;
226 0 : pcf::IndiProperty::PropertyStateType oldState = p.getState();
227 :
228 0 : if( oldState != newState )
229 0 : changed = true;
230 :
231 0 : for( n = 0; n < els.size() && changed != true; ++n )
232 : {
233 : // This is same code in IndiElement
234 0 : std::stringstream ssValue;
235 0 : ssValue.precision( 15 );
236 0 : ssValue << std::boolalpha << newVals[n];
237 :
238 : // compare in string space
239 0 : if( p[els[n]].getValue() != ssValue.str() )
240 0 : changed = true;
241 : }
242 :
243 : // and if there are changes, we send an update
244 0 : if( changed )
245 : {
246 0 : for( n = 0; n < els.size(); ++n )
247 : {
248 0 : p[els[n]].set( newVals[n] );
249 : }
250 0 : p.setTimeStamp( pcf::TimeStamp() );
251 0 : p.setState( newState );
252 0 : indiDriver->sendSetProperty( p );
253 : }
254 : }
255 0 : catch( std::exception &e )
256 : {
257 0 : std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
258 0 : if( n < els.size() )
259 : {
260 0 : std::cerr << "from " << p.getName() << "." << els[n] << ": ";
261 : }
262 0 : std::cerr << e.what() << "\n";
263 : }
264 0 : catch( ... )
265 : {
266 0 : std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
267 0 : if( n < els.size() )
268 : {
269 0 : std::cerr << "from " << p.getName() << "." << els[n] << "\n";
270 : }
271 : }
272 : }
273 :
274 : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
275 : /** Specialization for const char * element names.
276 : *
277 : * Only sends the set property message if at least one of the new values is different, or if the state has changed.
278 : *
279 : * \tparam T the type of the values
280 : * \tparam indiDriverT the type of the INDI driver class
281 : *
282 : */
283 : template <typename T, class indiDriverT>
284 0 : void updatesIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
285 : const std::vector<const char *> &els, ///< [in] The element names
286 : const std::vector<T> &newVals, ///< [in] the new values
287 : indiDriverT *indiDriver, ///< [in] the MagAOX INDI driver to use
288 : pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
289 : {
290 0 : updatesIfChanged<T, std::vector<const char *>, indiDriverT>( p, els, newVals, indiDriver, newState );
291 0 : }
292 :
293 : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
294 : /** Specialization for std::string element names.
295 : *
296 : * Only sends the set property message if at least one of the new values is different, or if the state has changed.
297 : *
298 : * \tparam T the type of the values
299 : * \tparam indiDriverT the type of the INDI driver class
300 : *
301 : */
302 : template <typename T, class indiDriverT>
303 : void updatesIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
304 : const std::vector<std::string> &els, ///< [in] The element names
305 : const std::vector<T> &newVals, ///< [in] the new values
306 : indiDriverT *indiDriver, ///< [in] the MagAOX INDI driver to use
307 : pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
308 : {
309 : updatesIfChanged<T, std::vector<std::string>, indiDriverT>( p, els, newVals, indiDriver, newState );
310 : }
311 :
312 : /// Update the value of the INDI element, but only if it has changed.
313 : /** Only sends the set property message if the new value is different.
314 : *
315 : * \todo investigate how this handles floating point values and string conversions.
316 : * \todo this needs a const char specialization to std::string
317 : *
318 : */
319 : template <class indiDriverT>
320 10 : void updateSwitchIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
321 : const std::string &el, ///< [in] The element name
322 : const pcf::IndiElement::SwitchStateType &newVal, ///< [in] the new value
323 : indiDriverT *indiDriver, ///< [in] the MagAOX INDI driver to use
324 : pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
325 : {
326 10 : if( !indiDriver )
327 : {
328 10 : return;
329 : }
330 :
331 : try
332 : {
333 0 : pcf::IndiElement::SwitchStateType oldVal = p[el].getSwitchState();
334 :
335 0 : pcf::IndiProperty::PropertyStateType oldState = p.getState();
336 :
337 0 : if( oldVal != newVal || oldState != newState )
338 : {
339 0 : p[el].setSwitchState( newVal );
340 0 : p.setTimeStamp( pcf::TimeStamp() );
341 0 : p.setState( newState );
342 0 : indiDriver->sendSetProperty( p );
343 : }
344 : }
345 0 : catch( ... )
346 : {
347 0 : std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
348 0 : std::cerr << "from " << p.getName() << "." << el << "\n";
349 : }
350 : }
351 :
352 : /// Update the values of a one-of-many INDI switch vector, but only if it has changed.
353 : /** Only sends the set property message if the new settings are different.
354 : *
355 : *
356 : */
357 : template <class indiDriverT>
358 0 : void updateSelectionSwitchIfChanged(
359 : pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
360 : const std::string &el, ///< [in] The element name which is now on
361 : indiDriverT *indiDriver, ///< [in] the MagAOX INDI driver to use
362 : pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
363 : {
364 0 : if( !indiDriver )
365 0 : return;
366 :
367 0 : if( !p.find( el ) )
368 : {
369 0 : std::cerr << "INDI error at " << __FILE__ << " " << __LINE__ << "\n";
370 0 : std::cerr << p.getName() << " does not have " << el << "\n";
371 0 : return;
372 : }
373 :
374 : try
375 : {
376 :
377 0 : bool changed = false;
378 0 : for( auto elit = p.getElements().begin(); elit != p.getElements().end(); ++elit )
379 : {
380 0 : if( elit->first == el )
381 : {
382 0 : if( elit->second.getSwitchState() != pcf::IndiElement::On )
383 : {
384 0 : p[elit->first].setSwitchState( pcf::IndiElement::On );
385 0 : changed = true;
386 : }
387 : }
388 : else
389 : {
390 0 : if( elit->second.getSwitchState() != pcf::IndiElement::Off )
391 : {
392 0 : p[elit->first].setSwitchState( pcf::IndiElement::Off );
393 0 : changed = true;
394 : }
395 : }
396 : }
397 :
398 0 : pcf::IndiProperty::PropertyStateType oldState = p.getState();
399 :
400 0 : if( changed || oldState != newState )
401 : {
402 0 : p.setState( newState );
403 0 : p.setTimeStamp( pcf::TimeStamp() );
404 0 : indiDriver->sendSetProperty( p );
405 : }
406 : }
407 0 : catch( ... )
408 : {
409 0 : std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
410 0 : std::cerr << "from " << p.getName() << "." << el << "\n";
411 : }
412 : }
413 :
414 : /// Parse an INDI key into the device and property names
415 : /** We often represent an INDI property as a unique key in the form
416 : * `deviceName.propName`. This function parses such a key into its
417 : * parts.
418 : *
419 : * \returns 0 on success
420 : * \returns -1 if the provided key is not at least 3 characters long
421 : * \returns -2 if no '.' is found
422 : * \returns -3 if '.' is the first character
423 : * \returns -4 if '.' is the last character
424 : */
425 6 : inline int parseIndiKey( std::string &devName, ///< [out] the device name
426 : std::string &propName, ///< [out] the property name
427 : const std::string &key ///< [in] the key to parse
428 : )
429 : {
430 6 : if( key.size() < 3 )
431 : {
432 2 : return -1;
433 : }
434 :
435 4 : size_t p = key.find( '.' );
436 :
437 4 : if( p == std::string::npos )
438 : {
439 1 : devName = "";
440 1 : propName = "";
441 1 : return -2;
442 : }
443 :
444 3 : if( p == 0 )
445 : {
446 1 : devName = "";
447 1 : propName = "";
448 1 : return -3;
449 : }
450 :
451 2 : if( p == key.size() - 1 )
452 : {
453 1 : devName = "";
454 1 : propName = "";
455 1 : return -4;
456 : }
457 :
458 1 : devName = key.substr( 0, p );
459 1 : propName = key.substr( p + 1 );
460 :
461 1 : return 0;
462 : }
463 :
464 : } // namespace indi
465 : } // namespace app
466 : } // namespace MagAOX
467 :
468 : #endif // app_magAOXIndiDriver_hpp
|