API
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 
20 
21 namespace MagAOX
22 {
23 namespace app
24 {
25 namespace indi
26 {
27 
28 #define INDI_IDLE (pcf::IndiProperty::Idle)
29 #define INDI_OK (pcf::IndiProperty::Ok)
30 #define INDI_BUSY (pcf::IndiProperty::Busy)
31 #define INDI_ALERT (pcf::IndiProperty::Alert)
32 
33 
34 /// Add a standard INDI Text element
35 /**
36  * \returns 0 on success
37  * \returns -1 on error
38  */
39 inline
40 int addTextElement( pcf::IndiProperty & prop, ///< [out] the property to which to add the elemtn
41  const std::string & name, ///< [in] the name of the element
42  const std::string & label = "" ///< [in] [optional] the GUI label suggestion for this property
43  )
44 {
45  prop.add(pcf::IndiElement(name, 0));
46 
47  //Don't set "" just in case libcommon does something with defaults
48  if(label != "")
49  {
50  prop[name].setLabel(label);
51  }
52 
53  return 0;
54 }
55 
56 
57 /// Add a standard INDI Number element
58 /**
59  * \returns 0 on success
60  * \returns -1 on error
61  */
62 template<typename T>
63 int addNumberElement( pcf::IndiProperty & prop, ///< [out] the property to which to add the elemtn
64  const std::string & name, ///< [in] the name of the element
65  const T & min, ///< [in] the minimum value for the element
66  const T & max, ///< [in] the minimum value for the element
67  const T & step, ///< [in] the step size of the lement
68  const std::string & format, ///< [in] the _ value for the elements, applied to both target and current. Set to "" to use the MagAO-X standard for type.
69  const std::string & label = "" ///< [in] [optional] the GUI label suggestion for this property
70  )
71 {
72  prop.add(pcf::IndiElement(name, 0));
73  prop[name].setMin(min);
74  prop[name].setMax(max);
75  prop[name].setStep(step);
76  prop[name].setFormat(format);
77 
78  //Don't set "" just in case libcommon does something with defaults
79  if(label != "")
80  {
81  prop[name].setLabel(label);
82  }
83 
84  return 0;
85 }
86 
87 /// Update the value of the INDI element, but only if it has changed.
88 /** Only sends the set property message if the new value is different.
89  * For properties with more than one element that may have changed, you should use the vector version below.
90  *
91  * \todo this needs a const char specialization to std::string
92  *
93  */
94 template<typename T, class indiDriverT>
95 void updateIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
96  const std::string & el, ///< [in] The element name
97  const T & newVal, ///< [in] the new value
98  indiDriverT * indiDriver, ///< [in] the MagAOX INDI driver to use
99  pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok
100  )
101 {
102  if( !indiDriver ) return;
103 
104  try
105  {
106  //This is same code in IndiElement
107  std::stringstream ssValue;
108  ssValue.precision( 15 );
109  ssValue << std::boolalpha << newVal;
110 
111  pcf::IndiProperty::PropertyStateType oldState = p.getState();
112 
113  //Do comparison in string space, not raw value
114  if(p[el].getValue() != ssValue.str()|| oldState != newState)
115  {
116  p[el].set(newVal);
117  p.setTimeStamp(pcf::TimeStamp());
118  p.setState (newState);
119  indiDriver->sendSetProperty (p);
120  }
121  }
122  catch(std::exception & e)
123  {
124  std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
125  std::cerr << "from " << p.getName() << "." << el << ": ";
126  std::cerr << e.what() << "\n";
127  }
128  catch(...)
129  {
130  std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
131  std::cerr << "from " << p.getName() << "." << el << "\n";
132  }
133 
134 }
135 
136 /// Update the elements of an INDI propery, but only if there has been a change in at least one.
137 /** Only sends the set property message if at least one of the new values is different, or if the state has changed.
138  *
139  * \todo this needs a const char specialization to std::string
140  *
141  */
142 template<typename T, class indiDriverT>
143 void updateIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
144  const std::vector<std::string> & els, ///< [in] The element names
145  const std::vector<T> & newVals, ///< [in] the new values
146  indiDriverT * indiDriver, ///< [in] the MagAOX INDI driver to use
147  pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok
148  )
149 {
150  if( !indiDriver ) return;
151 
152  size_t n = 0; //loop index outside so we can use it for error reporting.
153 
154  try
155  {
156  //First we look for any changes
157  bool changed = false;
158  pcf::IndiProperty::PropertyStateType oldState = p.getState();
159 
160  if(oldState != newState) changed = true;
161 
162  for(n=0; n< els.size() && changed != true; ++n)
163  {
164  //This is same code in IndiElement
165  std::stringstream ssValue;
166  ssValue.precision( 15 );
167  ssValue << std::boolalpha << newVals[n];
168 
169  //compare in string space
170  if(p[els[n]].getValue() != ssValue.str()) changed = true;
171  }
172 
173  //and if there are changes, we send an update
174  if(changed)
175  {
176  for(n=0; n< els.size(); ++n)
177  {
178  p[els[n]].set(newVals[n]);
179  }
180  p.setTimeStamp(pcf::TimeStamp());
181  p.setState (newState);
182  indiDriver->sendSetProperty (p);
183  }
184  }
185  catch(std::exception & e)
186  {
187  std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
188  if(n < els.size())
189  {
190  std::cerr << "from " << p.getName() << "." << els[n] << ": ";
191  }
192  std::cerr << e.what() << "\n";
193  }
194  catch(...)
195  {
196  std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
197  if(n < els.size())
198  {
199  std::cerr << "from " << p.getName() << "." << els[n] << "\n";
200  }
201  }
202 }
203 
204 /// Update the value of the INDI element, but only if it has changed.
205 /** Only sends the set property message if the new value is different.
206  *
207  * \todo investigate how this handles floating point values and string conversions.
208  * \todo this needs a const char specialization to std::string
209  *
210  */
211 template<class indiDriverT>
212 void updateSwitchIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
213  const std::string & el, ///< [in] The element name
214  const pcf::IndiElement::SwitchStateType & newVal, ///< [in] the new value
215  indiDriverT * indiDriver, ///< [in] the MagAOX INDI driver to use
216  pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok
217  )
218 {
219  if( !indiDriver ) return;
220 
221  try
222  {
223  pcf::IndiElement::SwitchStateType oldVal = p[el].getSwitchState();
224 
225  pcf::IndiProperty::PropertyStateType oldState = p.getState();
226 
227  if(oldVal != newVal || oldState != newState)
228  {
229  p[el].setSwitchState(newVal);
230  p.setTimeStamp(pcf::TimeStamp());
231  p.setState (newState);
232  indiDriver->sendSetProperty (p);
233  }
234  }
235  catch(...)
236  {
237  std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
238  std::cerr << "from " << p.getName() << "." << el << "\n";
239  }
240 }
241 
242 
243 /// Update the values of a one-of-many INDI switch vector, but only if it has changed.
244 /** Only sends the set property message if the new settings are different.
245  *
246  *
247  */
248 template<class indiDriverT>
249 void updateSelectionSwitchIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
250  const std::string & el, ///< [in] The element name which is now on
251  indiDriverT * indiDriver, ///< [in] the MagAOX INDI driver to use
252  pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok
253  )
254 {
255  if( !indiDriver ) return;
256 
257  if(!p.find(el))
258  {
259  std::cerr << "INDI error at " << __FILE__ << " " << __LINE__ << "\n";
260  std::cerr << p.getName() << " does not have " << el << "\n";
261  return;
262  }
263 
264  try
265  {
266 
267  bool changed = false;
268  for(auto elit = p.getElements().begin(); elit != p.getElements().end(); ++elit)
269  {
270  if( elit->first == el )
271  {
272  if(elit->second.getSwitchState() != pcf::IndiElement::On)
273  {
274  p[elit->first].setSwitchState(pcf::IndiElement::On);
275  changed = true;
276  }
277  }
278  else
279  {
280  if(elit->second.getSwitchState() != pcf::IndiElement::Off)
281  {
282  p[elit->first].setSwitchState(pcf::IndiElement::Off);
283  changed = true;
284  }
285  }
286  }
287 
288  pcf::IndiProperty::PropertyStateType oldState = p.getState();
289 
290  if(changed || oldState != newState)
291  {
292  p.setState (newState);
293  p.setTimeStamp(pcf::TimeStamp());
294  indiDriver->sendSetProperty (p);
295  }
296 
297  }
298  catch(...)
299  {
300  std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
301  std::cerr << "from " << p.getName() << "." << el << "\n";
302  }
303 }
304 
305 /// Parse an INDI key into the device and property names
306 /** We often represent an INDI property as a unique key in the form
307  * `deviceName.propName`. This function parses such a key into its
308  * parts.
309  *
310  * \returns 0 on success
311  * \returns -1 if the provided key is not at least 3 characters long
312  * \returns -2 if no '.' is found
313  * \returns -3 if '.' is the first character
314  * \returns -4 if '.' is the last character
315  */
316 inline
317 int parseIndiKey( std::string & devName, ///< [out] the device name
318  std::string & propName, ///< [out] the property name
319  const std::string & key ///< [in] the key to parse
320  )
321 {
322  if(key.size() < 3)
323  {
324  return -1;
325  }
326 
327  size_t p = key.find('.');
328 
329  if(p == std::string::npos)
330  {
331  devName = "";
332  propName = "";
333  return -2;
334  }
335 
336  if(p == 0)
337  {
338  devName = "";
339  propName = "";
340  return -3;
341  }
342 
343  if(p == key.size()-1)
344  {
345  devName = "";
346  propName = "";
347  return -4;
348  }
349 
350  devName = key.substr(0, p);
351  propName = key.substr(p+1);
352 
353  return 0;
354 }
355 
356 } //namespace indi
357 } //namespace app
358 } //namespace MagAOX
359 
360 #endif //app_magAOXIndiDriver_hpp
std::ostream & cerr()
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:95
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.
Definition: indiUtils.hpp:249
int parseIndiKey(std::string &devName, std::string &propName, const std::string &key)
Parse an INDI key into the device and property names.
Definition: indiUtils.hpp:317
int addTextElement(pcf::IndiProperty &prop, const std::string &name, const std::string &label="")
Add a standard INDI Text element.
Definition: indiUtils.hpp:40
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.
Definition: indiUtils.hpp:212
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:63
Definition: dm.hpp:24