API
 
Loading...
Searching...
No Matches
indiUtils.hpp
Go to the documentation of this file.
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
20namespace MagAOX
21{
22namespace app
23{
24namespace 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 */
37inline 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 prop.add( pcf::IndiElement( name, "" ) );
43
44 // Don't set "" just in case libcommon does something with defaults
45 if( label != "" )
46 {
47 prop[name].setLabel( label );
48 }
49
50 return 0;
51}
52
53/// Add a standard INDI Number element
54/**
55 * \returns 0 on success
56 * \returns -1 on error
57 */
58template <typename T>
59int 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 prop.add( pcf::IndiElement( name, 0 ) );
70 prop[name].setMin( min );
71 prop[name].setMax( max );
72 prop[name].setStep( step );
73 prop[name].setFormat( format );
74
75 // Don't set "" just in case libcommon does something with defaults
76 if( label != "" )
77 {
78 prop[name].setLabel( label );
79 }
80
81 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 */
91template <typename T, class indiDriverT>
92void 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 if( !indiDriver )
99 {
100 return;
101 }
102
103 try
104 {
105 // This is same code in IndiElement
106 std::stringstream ssValue;
107 ssValue.precision( 15 );
108 ssValue << std::boolalpha << newVal;
109
110 pcf::IndiProperty::PropertyStateType oldState = p.getState();
111
112 // Do comparison in string space, not raw value
113 if( p[el].getValue() != ssValue.str() || oldState != newState )
114 {
115 p[el].set( newVal );
116 p.setTimeStamp( pcf::TimeStamp() );
117 p.setState( newState );
118 indiDriver->sendSetProperty( p );
119 }
120 }
121 catch( std::exception &e )
122 {
123 std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
124 std::cerr << "from " << p.getName() << "." << el << ": ";
125 std::cerr << e.what() << "\n";
126 }
127 catch( ... )
128 {
129 std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
130 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 */
139template <typename T, class indiDriverT>
140void 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 if( !indiDriver )
147 return;
148
149 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 bool changed = false;
155 pcf::IndiProperty::PropertyStateType oldState = p.getState();
156
157 if( oldState != newState )
158 changed = true;
159
160 for( n = 0; n < els.size() && changed != true; ++n )
161 {
162 // This is same code in IndiElement
163 std::stringstream ssValue;
164 ssValue.precision( 15 );
165 ssValue << std::boolalpha << newVals[n];
166
167 // compare in string space
168 if( p[els[n]].getValue() != ssValue.str() )
169 changed = true;
170 }
171
172 // and if there are changes, we send an update
173 if( changed )
174 {
175 for( n = 0; n < els.size(); ++n )
176 {
177 p[els[n]].set( newVals[n] );
178 }
179 p.setTimeStamp( pcf::TimeStamp() );
180 p.setState( newState );
181 indiDriver->sendSetProperty( p );
182 }
183 }
184 catch( std::exception &e )
185 {
186 std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
187 if( n < els.size() )
188 {
189 std::cerr << "from " << p.getName() << "." << els[n] << ": ";
190 }
191 std::cerr << e.what() << "\n";
192 }
193 catch( ... )
194 {
195 std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
196 if( n < els.size() )
197 {
198 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 */
210template <typename T, typename elVecT, class indiDriverT>
211void 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 if( !indiDriver )
218 return;
219
220 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 bool changed = false;
226 pcf::IndiProperty::PropertyStateType oldState = p.getState();
227
228 if( oldState != newState )
229 changed = true;
230
231 for( n = 0; n < els.size() && changed != true; ++n )
232 {
233 // This is same code in IndiElement
234 std::stringstream ssValue;
235 ssValue.precision( 15 );
236 ssValue << std::boolalpha << newVals[n];
237
238 // compare in string space
239 if( p[els[n]].getValue() != ssValue.str() )
240 changed = true;
241 }
242
243 // and if there are changes, we send an update
244 if( changed )
245 {
246 for( n = 0; n < els.size(); ++n )
247 {
248 p[els[n]].set( newVals[n] );
249 }
250 p.setTimeStamp( pcf::TimeStamp() );
251 p.setState( newState );
252 indiDriver->sendSetProperty( p );
253 }
254 }
255 catch( std::exception &e )
256 {
257 std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
258 if( n < els.size() )
259 {
260 std::cerr << "from " << p.getName() << "." << els[n] << ": ";
261 }
262 std::cerr << e.what() << "\n";
263 }
264 catch( ... )
265 {
266 std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
267 if( n < els.size() )
268 {
269 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 */
283template <typename T, class indiDriverT>
284void 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 updatesIfChanged<T, std::vector<const char *>, indiDriverT>( p, els, newVals, indiDriver, newState );
291}
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 */
302template <typename T, class indiDriverT>
303void 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 */
319template <class indiDriverT>
320void 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 if( !indiDriver )
327 {
328 return;
329 }
330
331 try
332 {
333 pcf::IndiElement::SwitchStateType oldVal = p[el].getSwitchState();
334
335 pcf::IndiProperty::PropertyStateType oldState = p.getState();
336
337 if( oldVal != newVal || oldState != newState )
338 {
339 p[el].setSwitchState( newVal );
340 p.setTimeStamp( pcf::TimeStamp() );
341 p.setState( newState );
342 indiDriver->sendSetProperty( p );
343 }
344 }
345 catch( ... )
346 {
347 std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
348 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 */
357template <class indiDriverT>
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 if( !indiDriver )
365 return;
366
367 if( !p.find( el ) )
368 {
369 std::cerr << "INDI error at " << __FILE__ << " " << __LINE__ << "\n";
370 std::cerr << p.getName() << " does not have " << el << "\n";
371 return;
372 }
373
374 try
375 {
376
377 bool changed = false;
378 for( auto elit = p.getElements().begin(); elit != p.getElements().end(); ++elit )
379 {
380 if( elit->first == el )
381 {
382 if( elit->second.getSwitchState() != pcf::IndiElement::On )
383 {
384 p[elit->first].setSwitchState( pcf::IndiElement::On );
385 changed = true;
386 }
387 }
388 else
389 {
390 if( elit->second.getSwitchState() != pcf::IndiElement::Off )
391 {
392 p[elit->first].setSwitchState( pcf::IndiElement::Off );
393 changed = true;
394 }
395 }
396 }
397
398 pcf::IndiProperty::PropertyStateType oldState = p.getState();
399
400 if( changed || oldState != newState )
401 {
402 p.setState( newState );
403 p.setTimeStamp( pcf::TimeStamp() );
404 indiDriver->sendSetProperty( p );
405 }
406 }
407 catch( ... )
408 {
409 std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
410 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 */
425inline 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 if( key.size() < 3 )
431 {
432 return -1;
433 }
434
435 size_t p = key.find( '.' );
436
437 if( p == std::string::npos )
438 {
439 devName = "";
440 propName = "";
441 return -2;
442 }
443
444 if( p == 0 )
445 {
446 devName = "";
447 propName = "";
448 return -3;
449 }
450
451 if( p == key.size() - 1 )
452 {
453 devName = "";
454 propName = "";
455 return -4;
456 }
457
458 devName = key.substr( 0, p );
459 propName = key.substr( p + 1 );
460
461 return 0;
462}
463
464} // namespace indi
465} // namespace app
466} // namespace MagAOX
467
468#endif // app_magAOXIndiDriver_hpp
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition indiUtils.hpp:92
void updateSelectionSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the values of a one-of-many INDI switch vector, but only if it has changed.
int parseIndiKey(std::string &devName, std::string &propName, const std::string &key)
Parse an INDI key into the device and property names.
int addTextElement(pcf::IndiProperty &prop, const std::string &name, const std::string &label="")
Add a standard INDI Text element.
Definition indiUtils.hpp:37
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
int addNumberElement(pcf::IndiProperty &prop, const std::string &name, const T &min, const T &max, const T &step, const std::string &format, const std::string &label="")
Add a standard INDI Number element.
Definition indiUtils.hpp:59
void updatesIfChanged(pcf::IndiProperty &p, const elVecT &els, const std::vector< T > &newVals, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the elements of an INDI propery, but only if there has been a change in at least one.
Definition dm.hpp:19