Instrument Neutral Distributed Interface INDI  2.0.2
smartfocus.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2015 Camiel Severijns. All rights reserved.
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7  .
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12  .
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 *******************************************************************************/
18 
19 #include "smartfocus.h"
20 
21 #include "indicom.h"
22 
23 #include <cerrno>
24 #include <fcntl.h>
25 #include <memory>
26 #include <cstring>
27 #include <termios.h>
28 #include <unistd.h>
29 
30 namespace
31 {
32 static constexpr const SmartFocus::Position PositionInvalid { static_cast<SmartFocus::Position>(0xFFFF) };
33 // Interval to check the focuser state (in milliseconds)
34 static constexpr const int TimerInterval { 500 };
35 // in seconds
36 static constexpr const int ReadTimeOut { 1 };
37 
38 // SmartFocus command and response characters
39 static constexpr const char goto_position { 'g' };
40 static constexpr const char stop_focuser { 's' };
41 static constexpr const char read_id_register { 'b' };
42 static constexpr const char read_id_respons { 'j' };
43 static constexpr const char read_position { 'p' };
44 static constexpr const char read_flags { 't' };
45 static constexpr const char motion_complete { 'c' };
46 static constexpr const char motion_error { 'r' };
47 static constexpr const char motion_stopped { 's' };
48 } // namespace
49 
50 std::unique_ptr<SmartFocus> smartFocus(new SmartFocus());
51 
53 {
55 }
56 
58 {
60 
61  // No speed for SmartFocus
62  FocusSpeedN[0].min = 1;
63  FocusSpeedN[0].max = 1;
64  FocusSpeedN[0].value = 1;
66 
67  IUFillLight(&FlagsL[STATUS_SERIAL_FRAMING_ERROR], "SERIAL_FRAMING_ERROR", "Serial framing error", IPS_OK);
68  IUFillLight(&FlagsL[STATUS_SERIAL_OVERRUN_ERROR], "SERIAL_OVERRUN_ERROR", "Serial overrun error", IPS_OK);
69  IUFillLight(&FlagsL[STATUS_MOTOR_ENCODE_ERROR], "MOTOR_ENCODER_ERROR", "Motor/encoder error", IPS_OK);
70  IUFillLight(&FlagsL[STATUS_AT_ZERO_POSITION], "AT_ZERO_POSITION", "At zero position", IPS_OK);
71  IUFillLight(&FlagsL[STATUS_AT_MAX_POSITION], "AT_MAX_POSITION", "At max. position", IPS_OK);
72  IUFillLightVector(&FlagsLP, FlagsL, STATUS_NUM_FLAGS, getDeviceName(), "FLAGS", "Status Flags", MAIN_CONTROL_TAB,
73  IPS_IDLE);
74 
75  IUFillNumber(&MotionErrorN[0], "MOTION_ERROR", "Motion error", "%6.0f", -100., 100., 1., 0.);
76  IUFillNumberVector(&MotionErrorNP, MotionErrorN, 1, getDeviceName(), "MOTION_ERROR", "Motion error",
78 
79  FocusRelPosN[0].min = 0.;
80  FocusRelPosN[0].max = FocusMaxPosN[0].value; //MaxPositionN[0].value;
81  FocusRelPosN[0].value = 10;
82  FocusRelPosN[0].step = 1;
83 
84  FocusAbsPosN[0].min = 0.;
85  FocusAbsPosN[0].max = FocusMaxPosN[0].value; //MaxPositionN[0].value;
86  FocusAbsPosN[0].value = 0;
87  FocusAbsPosN[0].step = 1;
88 
89  setCurrentPollingPeriod(TimerInterval);
90 
91  return true;
92 }
93 
95 {
97 
98  if (isConnected())
99  {
100  defineProperty(&FlagsLP);
101  defineProperty(&MotionErrorNP);
102  SFgetState();
103  IDMessage(getDeviceName(), "SmartFocus focuser ready for use.");
104  }
105  else
106  {
107  deleteProperty(FlagsLP.name);
108  deleteProperty(MotionErrorNP.name);
109  }
110  return true;
111 }
112 
114 {
115  if (isSimulation())
116  return true;
117 
118  if (!SFacknowledge())
119  {
120  LOG_DEBUG("SmartFocus is not communicating.");
121  return false;
122  }
123 
124  LOG_DEBUG("SmartFocus is communicating.");
125  return true;
126 }
127 
129 {
130  return "SmartFocus";
131 }
132 
133 bool SmartFocus::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
134 {
135  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
136  {
137  if (strcmp(name, MotionErrorNP.name) == 0)
138  {
139  IUUpdateNumber(&MotionErrorNP, values, names, n);
140  MotionErrorNP.s = IPS_OK;
141  IDSetNumber(&MotionErrorNP, nullptr);
142  return true;
143  }
144  }
145  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
146 }
147 
148 //bool SmartFocus::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) {
149 // return INDI::Focuser::ISNewSwitch(dev,name,states,names,n);
150 //}
151 
153 {
154  bool result = true;
155  if (!isSimulation() && SFisMoving())
156  {
157  LOG_DEBUG("AbortFocuser: stopping motion");
158  result = send(&stop_focuser, sizeof(stop_focuser), "AbortFocuser");
159  // N.B.: The response to this stop command will be captured in the TimerHit method!
160  }
161  return result;
162 }
163 
164 IPState SmartFocus::MoveAbsFocuser(uint32_t targetPosition)
165 {
166  Position destination = static_cast<Position>(targetPosition);
167  IPState result = IPS_ALERT;
168  if (isSimulation())
169  {
170  position = destination;
171  state = Idle;
172  result = IPS_OK;
173  }
174  else
175  {
176  constexpr bool Correct_Positions = true;
177  if (Correct_Positions)
178  {
179  const int error = MotionErrorN[0].value; // The NGF-S overshoots motions by 3 steps.
180  if (destination > position) destination -= error, destination = std::max(position, destination);
181  if (destination < position) destination += error, destination = std::min(position, destination);
182  }
183  if (destination != position)
184  {
185  char command[3];
186  command[0] = goto_position;
187  command[1] = ((destination >> 8) & 0xFF);
188  command[2] = (destination & 0xFF);
189  LOGF_DEBUG("MoveAbsFocuser: destination= %d", destination);
190  tcflush(PortFD, TCIOFLUSH);
191  if (send(command, sizeof(command), "MoveAbsFocuser"))
192  {
193  char respons;
194  if (recv(&respons, sizeof(respons), "MoveAbsFocuser"))
195  {
196  LOGF_DEBUG("MoveAbsFocuser received echo: %c", respons);
197  if (respons != goto_position)
198  LOGF_ERROR("MoveAbsFocuser received unexpected respons: %c (0x02x)", respons,
199  respons);
200  else
201  {
202  state = MovingTo;
203  result = IPS_BUSY;
204  }
205  }
206  }
207  }
208  else
209  result = IPS_OK;
210  }
211  return result;
212 }
213 
215 {
216  return MoveAbsFocuser(position + (dir == FOCUS_INWARD ? -ticks : ticks));
217 }
218 
220 {
221  public:
222  NonBlockingIO(const char *_device, const int _fd) : device(_device), fd(_fd), flags(fcntl(_fd, F_GETFL, 0))
223  {
224  if (flags == -1)
225  DEBUGFDEVICE(device, INDI::Logger::DBG_ERROR, "NonBlockingIO::NonBlockingIO() fcntl get error: errno=%d",
226  errno);
227  else if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) == -1)
228  DEBUGFDEVICE(device, INDI::Logger::DBG_ERROR, "NonBlockingIO::NonBlockingIO() fcntl set error: errno=%d",
229  errno);
230  }
231 
233  {
234  if (flags != -1 && fcntl(fd, F_SETFL, flags) == -1)
236  "NonBlockinIO::~NonBlockingIO() fcntl set error: errno=%d", errno);
237  }
238 
239  private:
240  const char *device;
241  const int fd;
242  const int flags;
243 };
244 
246 {
247  // Wait for the end-of-motion character (c,r, or s) when the focuser is moving
248  // due to a request from this driver. Otherwise, retrieve the current position
249  // and state flags of the SmartFocus unit to keep the driver up-to-date with
250  // motion commands issued manually.
251  // TODO: What happens when the smartFocus unit is switched off?
252  if (!isConnected())
253  return;
254 
255  if (!isSimulation() && SFisMoving())
256  {
257  NonBlockingIO non_blocking(getDeviceName(), PortFD); // Automatically controls blocking IO by its scope
258  char respons;
259  if (read(PortFD, &respons, sizeof(respons)) == sizeof(respons))
260  {
261  LOGF_DEBUG("TimerHit() received character: %c (0x%02x)", respons, respons);
262  if (respons != motion_complete && respons != motion_error && respons != motion_stopped)
263  LOGF_ERROR("TimerHit() received unexpected character: %c (0x%02x)", respons,
264  respons);
265  state = Idle;
266  }
267  }
268  if (SFisIdle())
269  SFgetState();
270  timer_id = SetTimer(TimerInterval);
271 }
272 
274 {
275  Focuser::saveConfigItems(fp);
276  IUSaveConfigNumber(fp, &MotionErrorNP);
277  return true;
278 }
279 
280 bool SmartFocus::SFacknowledge()
281 {
282  bool success = false;
283  if (isSimulation())
284  success = true;
285  else
286  {
287  tcflush(PortFD, TCIOFLUSH);
288  if (send(&read_id_register, sizeof(read_id_register), "SFacknowledge"))
289  {
290  char respons[2];
291  if (recv(respons, sizeof(respons), "SFacknowledge", false))
292  {
293  LOGF_DEBUG("SFacknowledge received: %c%c", respons[0], respons[1]);
294  success = (respons[0] == read_id_register && respons[1] == read_id_respons);
295  if (!success)
296  LOGF_ERROR("SFacknowledge received unexpected respons: %c%c (0x02 0x02x)",
297  respons[0], respons[1], respons[0], respons[1]);
298  }
299  }
300  }
301  return success;
302 }
303 
304 SmartFocus::Position SmartFocus::SFgetPosition()
305 {
306  Position result = PositionInvalid;
307  if (isSimulation())
308  result = position;
309  else
310  {
311  tcflush(PortFD, TCIOFLUSH);
312  if (send(&read_position, sizeof(read_position), "SFgetPosition"))
313  {
314  char respons[3];
315  if (recv(respons, sizeof(respons), "SFgetPosition"))
316  {
317  if (respons[0] == read_position)
318  {
319  result = (((static_cast<Position>(respons[1]) << 8) & 0xFF00) |
320  (static_cast<Position>(respons[2]) & 0x00FF));
321  LOGF_DEBUG("SFgetPosition: position=%d", result);
322  }
323  else
324  LOGF_ERROR("SFgetPosition received unexpected respons: %c (0x02x)", respons[0],
325  respons[0]);
326  }
327  }
328  }
329  return result;
330 }
331 
332 SmartFocus::Flags SmartFocus::SFgetFlags()
333 {
334  Flags result = 0x00;
335  if (!isSimulation())
336  {
337  tcflush(PortFD, TCIOFLUSH);
338  if (send(&read_flags, sizeof(read_flags), "SFgetFlags"))
339  {
340  char respons[2];
341  if (recv(respons, sizeof(respons), "SFgetFlags"))
342  {
343  if (respons[0] == read_flags)
344  {
345  result = static_cast<Flags>(respons[1]);
346  LOGF_DEBUG("SFgetFlags: flags=0x%02x", result);
347  }
348  else
349  LOGF_ERROR("SFgetFlags received unexpected respons: %c (0x02x)", respons[0],
350  respons[0]);
351  }
352  }
353  }
354  return result;
355 }
356 
357 void SmartFocus::SFgetState()
358 {
359  const Flags flags = SFgetFlags();
360 
361  FlagsL[STATUS_SERIAL_FRAMING_ERROR].s = (flags & SerFramingError ? IPS_ALERT : IPS_OK);
362  FlagsL[STATUS_SERIAL_OVERRUN_ERROR].s = (flags & SerOverrunError ? IPS_ALERT : IPS_OK);
363  FlagsL[STATUS_MOTOR_ENCODE_ERROR].s = (flags & MotorEncoderError ? IPS_ALERT : IPS_OK);
364  FlagsL[STATUS_AT_ZERO_POSITION].s = (flags & AtZeroPosition ? IPS_ALERT : IPS_OK);
365  FlagsL[STATUS_AT_MAX_POSITION].s = (flags & AtMaxPosition ? IPS_ALERT : IPS_OK);
366  IDSetLight(&FlagsLP, nullptr);
367 
368  if ((position = SFgetPosition()) == PositionInvalid)
369  {
371  IDSetNumber(&FocusAbsPosNP, "Error while reading SmartFocus position");
372  }
373  else
374  {
375  FocusAbsPosN[0].value = position;
377  IDSetNumber(&FocusAbsPosNP, nullptr);
378  }
379 }
380 
381 bool SmartFocus::send(const char *command, const size_t nbytes, const char *from, const bool log_error)
382 {
383  int nbytes_written = 0;
384  const int rc = tty_write(PortFD, command, nbytes, &nbytes_written);
385  const bool success = (rc == TTY_OK && nbytes_written == (int)nbytes);
386 
387  if (!success && log_error)
388  {
389  char errstr[MAXRBUF];
390  tty_error_msg(rc, errstr, MAXRBUF);
391  LOGF_ERROR("%s: %s (%d of %d bytes written).", from, errstr, nbytes_written, nbytes);
392  }
393  return success;
394 }
395 
396 bool SmartFocus::recv(char *respons, const size_t nbytes, const char *from, const bool log_error)
397 {
398  int nbytes_read = 0;
399  const int rc = tty_read(PortFD, respons, nbytes, ReadTimeOut, &nbytes_read);
400  const bool success = (rc == TTY_OK && nbytes_read == (int)nbytes);
401 
402  if (!success && log_error)
403  {
404  char errstr[MAXRBUF];
405  tty_error_msg(rc, errstr, MAXRBUF);
406  LOGF_ERROR("%s: %s (%d of %d bytes read).", errstr, from, nbytes_read, nbytes);
407  }
408  return success;
409 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setCurrentPollingPeriod(uint32_t msec)
setCurrentPollingPeriod Change the current polling period to call TimerHit() function in the driver.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INumberVectorProperty FocusSpeedNP
INumberVectorProperty FocusAbsPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
NonBlockingIO(const char *_device, const int _fd)
Definition: smartfocus.cpp:222
virtual bool Handshake() override
perform handshake with device to check communication
Definition: smartfocus.cpp:113
const char * getDefaultName() override
Definition: smartfocus.cpp:128
virtual IPState MoveAbsFocuser(uint32_t targetPosition) override
MoveFocuser the focuser to an absolute position.
Definition: smartfocus.cpp:164
bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: smartfocus.cpp:57
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: smartfocus.cpp:152
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: smartfocus.cpp:214
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: smartfocus.cpp:273
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: smartfocus.cpp:245
unsigned short Position
Definition: smartfocus.h:43
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: smartfocus.cpp:94
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: smartfocus.cpp:133
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
double max(void)
int errno
double min(void)
@ 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
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IUFillLight(ILight *lp, const char *name, const char *label, IPState s)
Assign attributes for a light property. The light's auxiliary elements will be set to NULL.
Definition: indidevapi.c:169
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
void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name, const char *label, const char *group, IPState s)
Assign attributes for a light vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:255
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
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 IDSetLight(const ILightVectorProperty *lvp, const char *fmt,...)
Definition: indidriver.c:1251
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDMessage(const char *dev, const char *fmt,...)
Definition: indidriver.c:960
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 LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
#define MAXRBUF
Definition: indiserver.cpp:102
std::unique_ptr< SmartFocus > smartFocus(new SmartFocus())
char name[MAXINDINAME]
Definition: indiapi.h:421
char name[MAXINDINAME]
Definition: indiapi.h:323