Instrument Neutral Distributed Interface INDI 2.2.5
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
30namespace
31{
32static constexpr const SmartFocus::Position PositionInvalid { static_cast<SmartFocus::Position>(0xFFFF) };
33// Interval to check the focuser state (in milliseconds)
34static constexpr const int TimerInterval { 500 };
35// in seconds
36static constexpr const int ReadTimeOut { 1 };
37
38// SmartFocus command and response characters
39static constexpr const char goto_position { 'g' };
40static constexpr const char stop_focuser { 's' };
41static constexpr const char read_id_register { 'b' };
42static constexpr const char read_id_respons { 'j' };
43static constexpr const char read_position { 'p' };
44static constexpr const char read_flags { 't' };
45static constexpr const char motion_complete { 'c' };
46static constexpr const char motion_error { 'r' };
47static constexpr const char motion_stopped { 's' };
48} // namespace
49
50std::unique_ptr<SmartFocus> smartFocus(new SmartFocus());
51
56
58{
60
61 // No speed for SmartFocus
62 FocusSpeedNP[0].setMin(1);
63 FocusSpeedNP[0].setMax(1);
64 FocusSpeedNP[0].setValue(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 FocusRelPosNP[0].setMin(0.);
80 FocusRelPosNP[0].setMax(FocusMaxPosNP[0].getValue()); //MaxPositionN[0].value);
81 FocusRelPosNP[0].setValue(10);
82 FocusRelPosNP[0].setStep(1);
83
84 FocusAbsPosNP[0].setMin(0.);
85 FocusAbsPosNP[0].setMax(FocusMaxPosNP[0].getValue()); //MaxPositionN[0].value);
86 FocusAbsPosNP[0].setValue(0);
87 FocusAbsPosNP[0].setStep(1);
88 setCurrentPollingPeriod(TimerInterval);
89 return true;
90}
91
93{
95
96 if (isConnected())
97 {
98 defineProperty(&FlagsLP);
99 defineProperty(&MotionErrorNP);
100 SFgetState();
101 IDMessage(getDeviceName(), "SmartFocus focuser ready for use.");
102 }
103 else
104 {
105 deleteProperty(FlagsLP.name);
106 deleteProperty(MotionErrorNP.name);
107 }
108 return true;
109}
110
112{
113 if (isSimulation())
114 return true;
115
116 if (!SFacknowledge())
117 {
118 LOG_DEBUG("SmartFocus is not communicating.");
119 return false;
120 }
121
122 LOG_DEBUG("SmartFocus is communicating.");
123 return true;
124}
125
127{
128 return "SmartFocus";
129}
130
131bool SmartFocus::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
132{
133 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
134 {
135 if (strcmp(name, MotionErrorNP.name) == 0)
136 {
137 IUUpdateNumber(&MotionErrorNP, values, names, n);
138 MotionErrorNP.s = IPS_OK;
139 IDSetNumber(&MotionErrorNP, nullptr);
140 return true;
141 }
142 }
143 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
144}
145
146//bool SmartFocus::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) {
147// return INDI::Focuser::ISNewSwitch(dev,name,states,names,n);
148//}
149
151{
152 bool result = true;
153 if (!isSimulation() && SFisMoving())
154 {
155 LOG_DEBUG("AbortFocuser: stopping motion");
156 result = send(&stop_focuser, sizeof(stop_focuser), "AbortFocuser");
157 // N.B.: The response to this stop command will be captured in the TimerHit method!
158 }
159 return result;
160}
161
162IPState SmartFocus::MoveAbsFocuser(uint32_t targetPosition)
163{
164 Position destination = static_cast<Position>(targetPosition);
165 IPState result = IPS_ALERT;
166 if (isSimulation())
167 {
168 position = destination;
169 state = Idle;
170 result = IPS_OK;
171 }
172 else
173 {
174 constexpr bool Correct_Positions = true;
175 if (Correct_Positions)
176 {
177 const int error = MotionErrorN[0].value; // The NGF-S overshoots motions by 3 steps.
178 if (destination > position) destination -= error, destination = std::max(position, destination);
179 if (destination < position) destination += error, destination = std::min(position, destination);
180 }
181 if (destination != position)
182 {
183 char command[3];
184 command[0] = goto_position;
185 command[1] = ((destination >> 8) & 0xFF);
186 command[2] = (destination & 0xFF);
187 LOGF_DEBUG("MoveAbsFocuser: destination= %d", destination);
188 tcflush(PortFD, TCIOFLUSH);
189 if (send(command, sizeof(command), "MoveAbsFocuser"))
190 {
191 char respons;
192 if (recv(&respons, sizeof(respons), "MoveAbsFocuser"))
193 {
194 LOGF_DEBUG("MoveAbsFocuser received echo: %c", respons);
195 if (respons != goto_position)
196 LOGF_ERROR("MoveAbsFocuser received unexpected respons: %c (0x02x)", respons,
197 respons);
198 else
199 {
200 state = MovingTo;
201 result = IPS_BUSY;
202 }
203 }
204 }
205 }
206 else
207 result = IPS_OK;
208 }
209 return result;
210}
211
213{
214 return MoveAbsFocuser(position + (dir == FOCUS_INWARD ? -ticks : ticks));
215}
216
218{
219 public:
220 NonBlockingIO(const char *_device, const int _fd) : device(_device), fd(_fd), flags(fcntl(_fd, F_GETFL, 0))
221 {
222 if (flags == -1)
223 DEBUGFDEVICE(device, INDI::Logger::DBG_ERROR, "NonBlockingIO::NonBlockingIO() fcntl get error: errno=%d",
224 errno);
225 else if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) == -1)
226 DEBUGFDEVICE(device, INDI::Logger::DBG_ERROR, "NonBlockingIO::NonBlockingIO() fcntl set error: errno=%d",
227 errno);
228 }
229
231 {
232 if (flags != -1 && fcntl(fd, F_SETFL, flags) == -1)
234 "NonBlockinIO::~NonBlockingIO() fcntl set error: errno=%d", errno);
235 }
236
237 private:
238 const char *device;
239 const int fd;
240 const int flags;
241};
242
244{
245 // Wait for the end-of-motion character (c,r, or s) when the focuser is moving
246 // due to a request from this driver. Otherwise, retrieve the current position
247 // and state flags of the SmartFocus unit to keep the driver up-to-date with
248 // motion commands issued manually.
249 // TODO: What happens when the smartFocus unit is switched off?
250 if (!isConnected())
251 return;
252
253 if (!isSimulation() && SFisMoving())
254 {
255 NonBlockingIO non_blocking(getDeviceName(), PortFD); // Automatically controls blocking IO by its scope
256 char respons;
257 if (read(PortFD, &respons, sizeof(respons)) == sizeof(respons))
258 {
259 LOGF_DEBUG("TimerHit() received character: %c (0x%02x)", respons, respons);
260 if (respons != motion_complete && respons != motion_error && respons != motion_stopped)
261 LOGF_ERROR("TimerHit() received unexpected character: %c (0x%02x)", respons,
262 respons);
263 state = Idle;
264 }
265 }
266 if (SFisIdle())
267 SFgetState();
268 timer_id = SetTimer(TimerInterval);
269}
270
272{
273 Focuser::saveConfigItems(fp);
274 IUSaveConfigNumber(fp, &MotionErrorNP);
275 return true;
276}
277
278bool SmartFocus::SFacknowledge()
279{
280 bool success = false;
281 if (isSimulation())
282 success = true;
283 else
284 {
285 tcflush(PortFD, TCIOFLUSH);
286 if (send(&read_id_register, sizeof(read_id_register), "SFacknowledge"))
287 {
288 char respons[2];
289 if (recv(respons, sizeof(respons), "SFacknowledge", false))
290 {
291 LOGF_DEBUG("SFacknowledge received: %c%c", respons[0], respons[1]);
292 success = (respons[0] == read_id_register && respons[1] == read_id_respons);
293 if (!success)
294 LOGF_ERROR("SFacknowledge received unexpected respons: %c%c (0x02 0x02x)",
295 respons[0], respons[1], respons[0], respons[1]);
296 }
297 }
298 }
299 return success;
300}
301
302SmartFocus::Position SmartFocus::SFgetPosition()
303{
304 Position result = PositionInvalid;
305 if (isSimulation())
306 result = position;
307 else
308 {
309 tcflush(PortFD, TCIOFLUSH);
310 if (send(&read_position, sizeof(read_position), "SFgetPosition"))
311 {
312 char respons[3];
313 if (recv(respons, sizeof(respons), "SFgetPosition"))
314 {
315 if (respons[0] == read_position)
316 {
317 result = (((static_cast<Position>(respons[1]) << 8) & 0xFF00) |
318 (static_cast<Position>(respons[2]) & 0x00FF));
319 LOGF_DEBUG("SFgetPosition: position=%d", result);
320 }
321 else
322 LOGF_ERROR("SFgetPosition received unexpected respons: %c (0x02x)", respons[0],
323 respons[0]);
324 }
325 }
326 }
327 return result;
328}
329
330SmartFocus::Flags SmartFocus::SFgetFlags()
331{
332 Flags result = 0x00;
333 if (!isSimulation())
334 {
335 tcflush(PortFD, TCIOFLUSH);
336 if (send(&read_flags, sizeof(read_flags), "SFgetFlags"))
337 {
338 char respons[2];
339 if (recv(respons, sizeof(respons), "SFgetFlags"))
340 {
341 if (respons[0] == read_flags)
342 {
343 result = static_cast<Flags>(respons[1]);
344 LOGF_DEBUG("SFgetFlags: flags=0x%02x", result);
345 }
346 else
347 LOGF_ERROR("SFgetFlags received unexpected respons: %c (0x02x)", respons[0],
348 respons[0]);
349 }
350 }
351 }
352 return result;
353}
354
355void SmartFocus::SFgetState()
356{
357 const Flags flags = SFgetFlags();
358
359 FlagsL[STATUS_SERIAL_FRAMING_ERROR].s = (flags & SerFramingError ? IPS_ALERT : IPS_OK);
360 FlagsL[STATUS_SERIAL_OVERRUN_ERROR].s = (flags & SerOverrunError ? IPS_ALERT : IPS_OK);
361 FlagsL[STATUS_MOTOR_ENCODE_ERROR].s = (flags & MotorEncoderError ? IPS_ALERT : IPS_OK);
362 FlagsL[STATUS_AT_ZERO_POSITION].s = (flags & AtZeroPosition ? IPS_ALERT : IPS_OK);
363 FlagsL[STATUS_AT_MAX_POSITION].s = (flags & AtMaxPosition ? IPS_ALERT : IPS_OK);
364 IDSetLight(&FlagsLP, nullptr);
365
366 if ((position = SFgetPosition()) == PositionInvalid)
367 {
369 LOG_ERROR("Error while reading SmartFocus position");
370 }
371 else
372 {
373 FocusAbsPosNP[0].setValue(position);
376 }
377}
378
379bool SmartFocus::send(const char *command, const size_t nbytes, const char *from, const bool log_error)
380{
381 int nbytes_written = 0;
382 const int rc = tty_write(PortFD, command, nbytes, &nbytes_written);
383 const bool success = (rc == TTY_OK && nbytes_written == (int)nbytes);
384
385 if (!success && log_error)
386 {
387 char errstr[MAXRBUF];
388 tty_error_msg(rc, errstr, MAXRBUF);
389 LOGF_ERROR("%s: %s (%d of %d bytes written).", from, errstr, nbytes_written, nbytes);
390 }
391 return success;
392}
393
394bool SmartFocus::recv(char *respons, const size_t nbytes, const char *from, const bool log_error)
395{
396 int nbytes_read = 0;
397 const int rc = tty_read(PortFD, respons, nbytes, ReadTimeOut, &nbytes_read);
398 const bool success = (rc == TTY_OK && nbytes_read == (int)nbytes);
399
400 if (!success && log_error)
401 {
402 char errstr[MAXRBUF];
403 tty_error_msg(rc, errstr, MAXRBUF);
404 LOGF_ERROR("%s: %s (%d of %d bytes read).", errstr, from, nbytes_read, nbytes);
405 }
406 return success;
407}
bool isConnected() const
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.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusSpeedNP
INDI::PropertyNumber FocusMaxPosNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
NonBlockingIO(const char *_device, const int _fd)
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
const char * getDefaultName() override
virtual IPState MoveAbsFocuser(uint32_t targetPosition) override
MoveFocuser the focuser to an absolute position.
bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool saveConfigItems(FILE *fp) override
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
unsigned short Position
Definition smartfocus.h:43
bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
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,...
int errno
@ IP_RW
Definition indiapi.h:184
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
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:387
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:490
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:473
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
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:398
void IDSetLight(const ILightVectorProperty *lvp, const char *fmt,...)
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
void IDMessage(const char *dev, const char *fmt,...)
Definition indidriver.c:983
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61
#define getDeviceName
std::unique_ptr< SmartFocus > smartFocus(new SmartFocus())
char name[MAXINDINAME]
Definition indiapi.h:419
char name[MAXINDINAME]
Definition indiapi.h:321