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; //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  std::cerr << "from " << p.getName() << "." << els[n] << ": ";
189  std::cerr << e.what() << "\n";
190  }
191  catch(...)
192  {
193  std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
194  std::cerr << "from " << p.getName() << "." << els[n] << "\n";
195  }
196 }
197 
198 /// Update the value of the INDI element, but only if it has changed.
199 /** Only sends the set property message if the new value is different.
200  *
201  * \todo investigate how this handles floating point values and string conversions.
202  * \todo this needs a const char specialization to std::string
203  *
204  */
205 template<class indiDriverT>
206 void updateSwitchIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
207  const std::string & el, ///< [in] The element name
208  const pcf::IndiElement::SwitchStateType & newVal, ///< [in] the new value
209  indiDriverT * indiDriver, ///< [in] the MagAOX INDI driver to use
210  pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok
211  )
212 {
213  if( !indiDriver ) return;
214 
215  try
216  {
217  pcf::IndiElement::SwitchStateType oldVal = p[el].getSwitchState();
218 
219  pcf::IndiProperty::PropertyStateType oldState = p.getState();
220 
221  if(oldVal != newVal || oldState != newState)
222  {
223  p[el].setSwitchState(newVal);
224  p.setTimeStamp(pcf::TimeStamp());
225  p.setState (newState);
226  indiDriver->sendSetProperty (p);
227  }
228  }
229  catch(...)
230  {
231  std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
232  std::cerr << "from " << p.getName() << "." << el << "\n";
233  }
234 }
235 
236 
237 /// Update the values of a one-of-many INDI switch vector, but only if it has changed.
238 /** Only sends the set property message if the new settings are different.
239  *
240  *
241  */
242 template<class indiDriverT>
243 void updateSelectionSwitchIfChanged( pcf::IndiProperty & p, ///< [in/out] The property containing the element to possibly update
244  const std::string & el, ///< [in] The element name which is now on
245  indiDriverT * indiDriver, ///< [in] the MagAOX INDI driver to use
246  pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok
247  )
248 {
249  if( !indiDriver ) return;
250 
251  if(!p.find(el))
252  {
253  std::cerr << "INDI error at " << __FILE__ << " " << __LINE__ << "\n";
254  std::cerr << p.getName() << " does not have " << el << "\n";
255  return;
256  }
257 
258  try
259  {
260 
261  bool changed = false;
262  for(auto elit = p.getElements().begin(); elit != p.getElements().end(); ++elit)
263  {
264  if( elit->first == el )
265  {
266  if(elit->second.getSwitchState() != pcf::IndiElement::On)
267  {
268  p[elit->first].setSwitchState(pcf::IndiElement::On);
269  changed = true;
270  }
271  }
272  else
273  {
274  if(elit->second.getSwitchState() != pcf::IndiElement::Off)
275  {
276  p[elit->first].setSwitchState(pcf::IndiElement::Off);
277  changed = true;
278  }
279  }
280  }
281 
282  pcf::IndiProperty::PropertyStateType oldState = p.getState();
283 
284  if(changed || oldState != newState)
285  {
286  p.setState (newState);
287  p.setTimeStamp(pcf::TimeStamp());
288  indiDriver->sendSetProperty (p);
289  }
290 
291  }
292  catch(...)
293  {
294  std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
295  std::cerr << "from " << p.getName() << "." << el << "\n";
296  }
297 }
298 
299 /// Parse an INDI key into the device and property names
300 /** We often represent an INDI property as a unique key in the form
301  * `deviceName.propName`. This function parses such a key into its
302  * parts.
303  *
304  * \returns 0 on success
305  * \returns -1 if the provided key is not at least 3 characters long
306  * \returns -2 if no '.' is found
307  * \returns -3 if '.' is the first character
308  * \returns -4 if '.' is the last character
309  */
310 inline
311 int parseIndiKey( std::string & devName, ///< [out] the device name
312  std::string & propName, ///< [out] the property name
313  const std::string & key ///< [in] the key to parse
314  )
315 {
316  if(key.size() < 3)
317  {
318  return -1;
319  }
320 
321  size_t p = key.find('.');
322 
323  if(p == std::string::npos)
324  {
325  devName = "";
326  propName = "";
327  return -2;
328  }
329 
330  if(p == 0)
331  {
332  devName = "";
333  propName = "";
334  return -3;
335  }
336 
337  if(p == key.size()-1)
338  {
339  devName = "";
340  propName = "";
341  return -4;
342  }
343 
344  devName = key.substr(0, p);
345  propName = key.substr(p+1);
346 
347  return 0;
348 }
349 
350 } //namespace indi
351 } //namespace app
352 } //namespace MagAOX
353 
354 #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:243
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:311
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:206
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