Instrument Neutral Distributed Interface INDI  2.0.2
indifocuserinterface.cpp
Go to the documentation of this file.
1 /*
2  Focuser Interface
3  Copyright (C) 2011 Jasem Mutlaq (mutlaqja@ikarustech.com)
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 #include "indifocuserinterface.h"
22 
23 #include "indilogger.h"
24 
25 #include <cstring>
26 #include <cmath>
27 
28 namespace INDI
29 {
30 
31 FocuserInterface::FocuserInterface(DefaultDevice * defaultDevice) : m_defaultDevice(defaultDevice)
32 {
33 }
34 
35 void FocuserInterface::initProperties(const char * groupName)
36 {
37  IUFillNumber(&FocusSpeedN[0], "FOCUS_SPEED_VALUE", "Focus Speed", "%3.0f", 0.0, 255.0, 1.0, 255.0);
38  IUFillNumberVector(&FocusSpeedNP, FocusSpeedN, 1, m_defaultDevice->getDeviceName(), "FOCUS_SPEED", "Speed", groupName,
39  IP_RW, 60, IPS_OK);
40 
41  IUFillNumber(&FocusTimerN[0], "FOCUS_TIMER_VALUE", "Focus Timer (ms)", "%4.0f", 0.0, 5000.0, 50.0, 1000.0);
42  IUFillNumberVector(&FocusTimerNP, FocusTimerN, 1, m_defaultDevice->getDeviceName(), "FOCUS_TIMER", "Timer", groupName,
43  IP_RW, 60, IPS_OK);
44  lastTimerValue = 1000.0;
45 
46  IUFillSwitch(&FocusMotionS[0], "FOCUS_INWARD", "Focus In", ISS_ON);
47  IUFillSwitch(&FocusMotionS[1], "FOCUS_OUTWARD", "Focus Out", ISS_OFF);
48  IUFillSwitchVector(&FocusMotionSP, FocusMotionS, 2, m_defaultDevice->getDeviceName(), "FOCUS_MOTION", "Direction",
49  groupName, IP_RW,
50  ISR_1OFMANY, 60, IPS_OK);
51 
52  // Absolute Position
53  IUFillNumber(&FocusAbsPosN[0], "FOCUS_ABSOLUTE_POSITION", "Steps", "%.f", 0.0, 100000.0, 1000.0, 0);
55  "Absolute Position",
56  groupName, IP_RW, 60, IPS_OK);
57 
58  // Relative Position
59  IUFillNumber(&FocusRelPosN[0], "FOCUS_RELATIVE_POSITION", "Steps", "%.f", 0.0, 100000.0, 1000.0, 0);
61  "Relative Position",
62  groupName, IP_RW, 60, IPS_OK);
63 
64  // Sync
65  IUFillNumber(&FocusSyncN[0], "FOCUS_SYNC_VALUE", "Steps", "%.f", 0.0, 100000.0, 1000.0, 0);
67  groupName, IP_RW, 60, IPS_OK);
68 
69  // Maximum Position
70  IUFillNumber(&FocusMaxPosN[0], "FOCUS_MAX_VALUE", "Steps", "%.f", 1e3, 1e6, 1e4, 1e5);
71  IUFillNumberVector(&FocusMaxPosNP, FocusMaxPosN, 1, m_defaultDevice->getDeviceName(), "FOCUS_MAX", "Max. Position",
72  groupName, IP_RW, 60, IPS_OK);
73 
74  // Abort
75  IUFillSwitch(&FocusAbortS[0], "ABORT", "Abort", ISS_OFF);
76  IUFillSwitchVector(&FocusAbortSP, FocusAbortS, 1, m_defaultDevice->getDeviceName(), "FOCUS_ABORT_MOTION", "Abort Motion",
77  groupName, IP_RW,
78  ISR_ATMOST1, 60, IPS_IDLE);
79 
80  // Revese
81  IUFillSwitch(&FocusReverseS[DefaultDevice::INDI_ENABLED], "INDI_ENABLED", "Enabled", ISS_OFF);
82  IUFillSwitch(&FocusReverseS[DefaultDevice::INDI_DISABLED], "INDI_DISABLED", "Disabled", ISS_ON);
84  "Reverse Motion", groupName, IP_RW,
85  ISR_1OFMANY, 60, IPS_IDLE);
86 
87  // Backlash Compensation
88  IUFillSwitch(&FocusBacklashS[DefaultDevice::INDI_ENABLED], "INDI_ENABLED", "Enabled", ISS_OFF);
89  IUFillSwitch(&FocusBacklashS[DefaultDevice::INDI_DISABLED], "INDI_DISABLED", "Disabled", ISS_ON);
91  "Backlash", groupName, IP_RW,
92  ISR_1OFMANY, 60, IPS_IDLE);
93 
94  // Backlash Compensation Value
95  IUFillNumber(&FocusBacklashN[0], "FOCUS_BACKLASH_VALUE", "Steps", "%.f", 0, 1e6, 100, 0);
97  "Backlash",
98  groupName, IP_RW, 60, IPS_OK);
99 }
100 
102 {
104  {
105  // Now we add our focusser specific stuff
107 
108  if (HasVariableSpeed())
109  {
111 
112  // We only define Focus Timer if we can not absolute move
113  if (CanAbsMove() == false)
115  }
116  if (CanRelMove())
118  if (CanAbsMove())
119  {
122  }
123  if (CanAbort())
125  if (CanSync())
127  if (CanReverse())
129  if (HasBacklash())
130  {
133  }
134  }
135  else
136  {
138  if (HasVariableSpeed())
139  {
141 
142  if (CanAbsMove() == false)
144  }
145  if (CanRelMove())
147  if (CanAbsMove())
148  {
151  }
152  if (CanAbort())
154  if (CanSync())
156  if (CanReverse())
158  if (HasBacklash())
159  {
162  }
163  }
164 
165  return true;
166 }
167 
168 bool FocuserInterface::processNumber(const char * dev, const char * name, double values[], char * names[], int n)
169 {
170  // Move focuser based on requested timeout
171  if (!strcmp(name, FocusTimerNP.name))
172  {
173  FocusDirection dir;
174  int speed;
175  int t;
176 
177  // first we get all the numbers just sent to us
178  IUUpdateNumber(&FocusTimerNP, values, names, n);
179 
180  // Now lets find what we need for this move
181  speed = FocusSpeedN[0].value;
182 
183  if (FocusMotionS[0].s == ISS_ON)
184  dir = FOCUS_INWARD;
185  else
186  dir = FOCUS_OUTWARD;
187 
188  t = FocusTimerN[0].value;
189  lastTimerValue = t;
190 
191  FocusTimerNP.s = MoveFocuser(dir, speed, t);
192  IDSetNumber(&FocusTimerNP, nullptr);
193  return true;
194  }
195 
196  // Set variable focus speed
197  if (!strcmp(name, FocusSpeedNP.name))
198  {
200  int current_speed = FocusSpeedN[0].value;
201  IUUpdateNumber(&FocusSpeedNP, values, names, n);
202 
203  if (SetFocuserSpeed(FocusSpeedN[0].value) == false)
204  {
205  FocusSpeedN[0].value = current_speed;
208  }
209 
210  // Update client display
211  IDSetNumber(&FocusSpeedNP, nullptr);
212  return true;
213  }
214 
215  // Update Maximum Position allowed
216  if (!strcmp(name, FocusMaxPosNP.name))
217  {
218  uint32_t maxTravel = rint(values[0]);
219  if (SetFocuserMaxPosition(maxTravel))
220  {
221  IUUpdateNumber(&FocusMaxPosNP, values, names, n);
222 
223  FocusAbsPosN[0].max = FocusMaxPosN[0].value;
224  FocusSyncN[0].max = FocusMaxPosN[0].value;
225 
226  FocusAbsPosN[0].step = FocusMaxPosN[0].value / 50.0;
227  FocusSyncN[0].step = FocusMaxPosN[0].value / 50.0;
228 
229  FocusAbsPosN[0].min = 0;
230  FocusSyncN[0].min = 0;
231 
232  FocusRelPosN[0].max = FocusMaxPosN[0].value / 2;
233  FocusRelPosN[0].step = FocusMaxPosN[0].value / 100.0;
234  FocusRelPosN[0].min = 0;
235 
239 
241 
243  }
244  else
246 
247  IDSetNumber(&FocusMaxPosNP, nullptr);
248  return true;
249  }
250 
251  // Sync
252  if (!strcmp(name, FocusSyncNP.name))
253  {
254  if (SyncFocuser(rint(values[0])))
255  {
256  FocusSyncN[0].value = rint(values[0]);
257  FocusAbsPosN[0].value = rint(values[0]);
258  FocusSyncNP.s = IPS_OK;
259  IDSetNumber(&FocusSyncNP, nullptr);
260  IDSetNumber(&FocusAbsPosNP, nullptr);
261  }
262  else
263  {
265  IDSetNumber(&FocusSyncNP, nullptr);
266  }
267  return true;
268  }
269 
270  // Set backlash value
271  if (!strcmp(name, FocusBacklashNP.name))
272  {
274  {
276 
277  // Only warn if there is non-zero backlash value.
278  if (values[0] > 0)
279  DEBUGDEVICE(dev, Logger::DBG_WARNING, "Focuser backlash must be enabled first.");
280  }
281  else
282  {
283  uint32_t steps = static_cast<uint32_t>(values[0]);
284  if (SetFocuserBacklash(steps))
285  {
286  FocusBacklashN[0].value = values[0];
289  }
290  else
292  }
293  IDSetNumber(&FocusBacklashNP, nullptr);
294  return true;
295  }
296 
297  // Update Absolute Focuser Position
298  if (!strcmp(name, FocusAbsPosNP.name))
299  {
300  int newPos = rint(values[0]);
301 
302  if (newPos < FocusAbsPosN[0].min)
303  {
305  IDSetNumber(&FocusAbsPosNP, nullptr);
306  DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus minimum position is %g",
307  FocusAbsPosN[0].min);
308  return false;
309  }
310  else if (newPos > FocusAbsPosN[0].max)
311  {
313  IDSetNumber(&FocusAbsPosNP, nullptr);
314  DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus maximum position is %g",
315  FocusAbsPosN[0].max);
316  return false;
317  }
318 
319  IPState ret;
320 
321  if ((ret = MoveAbsFocuser(newPos)) == IPS_OK)
322  {
324  IUUpdateNumber(&FocusAbsPosNP, values, names, n);
325  DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser moved to position %d", newPos);
326  IDSetNumber(&FocusAbsPosNP, nullptr);
327  return true;
328  }
329  else if (ret == IPS_BUSY)
330  {
332  DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser is moving to position %d", newPos);
333  IDSetNumber(&FocusAbsPosNP, nullptr);
334  return true;
335  }
336 
338  DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position.");
339  IDSetNumber(&FocusAbsPosNP, nullptr);
340  return false;
341  }
342 
343  // Update Relative focuser steps. This moves the focuser CW/CCW by this number of steps.
344  if (!strcmp(name, FocusRelPosNP.name))
345  {
346  int newPos = rint(values[0]);
347 
348  if (newPos <= 0)
349  {
350  DEBUGDEVICE(dev, Logger::DBG_ERROR, "Relative ticks value must be greater than zero.");
352  IDSetNumber(&FocusRelPosNP, nullptr);
353  return false;
354  }
355 
356  IPState ret;
357 
358  if (CanAbsMove())
359  {
360  if (FocusMotionS[0].s == ISS_ON)
361  {
362  if (FocusAbsPosN[0].value - newPos < FocusAbsPosN[0].min)
363  {
365  IDSetNumber(&FocusRelPosNP, nullptr);
367  "Requested position out of bound. Focus minimum position is %g", FocusAbsPosN[0].min);
368  return false;
369  }
370  }
371  else
372  {
373  if (FocusAbsPosN[0].value + newPos > FocusAbsPosN[0].max)
374  {
376  IDSetNumber(&FocusRelPosNP, nullptr);
378  "Requested position out of bound. Focus maximum position is %g", FocusAbsPosN[0].max);
379  return false;
380  }
381  }
382  }
383 
384  if ((ret = MoveRelFocuser((FocusMotionS[0].s == ISS_ON ? FOCUS_INWARD : FOCUS_OUTWARD), newPos)) == IPS_OK)
385  {
388  IUUpdateNumber(&FocusRelPosNP, values, names, n);
389  IDSetNumber(&FocusRelPosNP, "Focuser moved %d steps %s", newPos,
390  FocusMotionS[0].s == ISS_ON ? "inward" : "outward");
391  IDSetNumber(&FocusAbsPosNP, nullptr);
392  return true;
393  }
394  else if (ret == IPS_BUSY)
395  {
396  IUUpdateNumber(&FocusRelPosNP, values, names, n);
399  IDSetNumber(&FocusAbsPosNP, "Focuser is moving %d steps %s...", newPos,
400  FocusMotionS[0].s == ISS_ON ? "inward" : "outward");
401  IDSetNumber(&FocusAbsPosNP, nullptr);
402  return true;
403  }
404 
406  DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position.");
407  IDSetNumber(&FocusRelPosNP, nullptr);
408  return false;
409  }
410 
411  return false;
412 }
413 
414 bool FocuserInterface::processSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
415 {
416  INDI_UNUSED(dev);
417  // This one is for focus motion
418  if (!strcmp(name, FocusMotionSP.name))
419  {
420  // Record last direction and state.
422  IPState prevState = FocusMotionSP.s;
423 
424  IUUpdateSwitch(&FocusMotionSP, states, names, n);
425 
427 
428  if (CanRelMove() || CanAbsMove() || HasVariableSpeed())
429  {
431  }
432  // If we are dealing with a simple dumb DC focuser, we move in a specific direction in an open-loop fashion until stopped.
433  else
434  {
435  // If we are reversing direction let's issue abort first.
436  if (prevDirection != targetDirection && prevState == IPS_BUSY)
437  AbortFocuser();
438 
439  FocusMotionSP.s = MoveFocuser(targetDirection, 0, 0);
440  }
441 
442  IDSetSwitch(&FocusMotionSP, nullptr);
443 
444  return true;
445  }
446 
447  // Backlash compensation
448  else if (!strcmp(name, FocusBacklashSP.name))
449  {
450  int prevIndex = IUFindOnSwitchIndex(&FocusBacklashSP);
451  IUUpdateSwitch(&FocusBacklashSP, states, names, n);
452 
454  {
455  IUUpdateSwitch(&FocusBacklashSP, states, names, n);
458  }
459  else
460  {
462  FocusBacklashS[prevIndex].s = ISS_ON;
464  }
465 
466  IDSetSwitch(&FocusBacklashSP, nullptr);
467  return true;
468  }
469 
470  // Abort Focuser
471  else if (!strcmp(name, FocusAbortSP.name))
472  {
474 
475  if (AbortFocuser())
476  {
478  if (CanAbsMove() && FocusAbsPosNP.s != IPS_IDLE)
479  {
481  IDSetNumber(&FocusAbsPosNP, nullptr);
482  }
483  if (CanRelMove() && FocusRelPosNP.s != IPS_IDLE)
484  {
486  IDSetNumber(&FocusRelPosNP, nullptr);
487  }
488  }
489  else
491 
492  IDSetSwitch(&FocusAbortSP, nullptr);
493  return true;
494  }
495 
496  // Reverse Motion
497  else if (!strcmp(name, FocusReverseSP.name))
498  {
499  int prevIndex = IUFindOnSwitchIndex(&FocusReverseSP);
500  IUUpdateSwitch(&FocusReverseSP, states, names, n);
501 
503  {
506  }
507  else
508  {
510  FocusReverseS[prevIndex].s = ISS_ON;
512  }
513 
514  IDSetSwitch(&FocusReverseSP, nullptr);
515  return true;
516  }
517 
518  return false;
519 }
520 
521 IPState FocuserInterface::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
522 {
523  INDI_UNUSED(dir);
524  INDI_UNUSED(speed);
525  INDI_UNUSED(duration);
526  // Must be implemented by child class
527  return IPS_ALERT;
528 }
529 
531 {
532  INDI_UNUSED(dir);
533  INDI_UNUSED(ticks);
534  // Must be implemented by child class
535  return IPS_ALERT;
536 }
537 
539 {
540  INDI_UNUSED(ticks);
541  // Must be implemented by child class
542  return IPS_ALERT;
543 }
544 
546 {
547  // This should be a virtual function, because the low level hardware class
548  // must override this
549  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support abort motion.");
550  return false;
551 }
552 
554 {
555  INDI_UNUSED(enabled);
556  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support reverse motion.");
557  return false;
558 }
559 
560 bool FocuserInterface::SyncFocuser(uint32_t ticks)
561 {
562  INDI_UNUSED(ticks);
563  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support syncing.");
564  return false;
565 }
566 
568 {
569  INDI_UNUSED(speed);
570  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support variable speed.");
571  return false;
572 }
573 
575 {
576  INDI_UNUSED(ticks);
577  return true;
578 }
579 
581 {
582  INDI_UNUSED(steps);
583  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR, "Focuser does not support backlash compensation.");
584  return false;
585 }
586 
588 {
589  // If disabled, set the focuser backlash to zero.
590  if (enabled)
591  return SetFocuserBacklash(static_cast<int32_t>(FocusBacklashN[0].value));
592  else
593  return SetFocuserBacklash(0);
594 }
595 
597 {
598  if (CanAbsMove())
600  if (CanReverse())
602  if (HasBacklash())
603  {
606  }
607 
608  return true;
609 }
610 
611 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
ISwitchVectorProperty FocusMotionSP
INumberVectorProperty FocusSpeedNP
INumberVectorProperty FocusSyncNP
INumberVectorProperty FocusBacklashNP
virtual bool SetFocuserBacklash(int32_t steps)
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool SetFocuserBacklashEnabled(bool enabled)
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
virtual bool ReverseFocuser(bool enabled)
ReverseFocuser Reverse focuser motion direction.
ISwitchVectorProperty FocusBacklashSP
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks)
MoveFocuser the focuser to an relative position.
INumberVectorProperty FocusTimerNP
ISwitchVectorProperty FocusReverseSP
void initProperties(const char *groupName)
Initilize focuser properties. It is recommended to call this function within initProperties() of your...
virtual bool SetFocuserMaxPosition(uint32_t ticks)
SetFocuserMaxPosition Set Focuser Maximum position limit in the hardware.
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
virtual bool SetFocuserSpeed(int speed)
SetFocuserSpeed Set Focuser speed.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
virtual bool SyncFocuser(uint32_t ticks)
SyncFocuser Set current position to ticks without moving the focuser.
FocuserInterface(DefaultDevice *defaultDevice)
INumberVectorProperty FocusMaxPosNP
virtual bool AbortFocuser()
AbortFocuser all focus motion.
ISwitchVectorProperty FocusAbortSP
virtual IPState MoveAbsFocuser(uint32_t targetTicks)
MoveFocuser the focuser to an absolute position.
double max(void)
double min(void)
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_ATMOST1
Definition: indiapi.h:174
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1296
#define DEBUGDEVICE(device, priority, msg)
Definition: indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
Namespace to encapsulate INDI client, drivers, and mediator classes.
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371