Instrument Neutral Distributed Interface INDI  2.0.2
trutech_wheel.c
Go to the documentation of this file.
1 #if 0
2  True Technology Filter Wheel
3  Copyright (C) 2006 Jasem Mutlaq (mutlaqja AT ikarustech DOT 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 #endif
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <cstring>
24 #include <stdarg.h>
25 #include <cmath>
26 #include <unistd.h>
27 #include <ctime>
28 #include <fcntl.h>
29 #include <cerrno>
30 #include <sys/stat.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 
35 #include "indidevapi.h"
36 #include "eventloop.h"
37 #include "indicom.h"
38 
39 void ISInit(void);
40 void getBasicData(void);
41 void ISPoll(void *);
42 void handleExposure(void *);
43 void connectFilter(void);
44 int manageDefaults(char errmsg[]);
49 int isFilterConnected(void);
50 
51 static int targetFilter;
52 static int fd;
53 const unsigned char COMM_PRE = 0x01;
54 const unsigned char COMM_INIT = 0xA5;
55 const unsigned char COMM_FILL = 0x20;
56 
57 #define mydev "TruTech Wheel"
58 #define MAIN_GROUP "Main Control"
59 #define currentFilter FilterPositionN[0].value
60 #define POLLMS 3000
61 #define DEFAULT_FILTER_COUNT 5
62 #define MAX_FILTER_COUNT 10
63 #define ERRMSG_SIZE 1024
64 
65 #define CMD_SIZE 5
66 #define CMD_JUNK 64
67 #define CMD_RESP 15
68 
69 #define FILTER_TIMEOUT 15 /* 15 Seconds before timeout */
70 #define FIRST_FILTER 1
71 
72 #define DEBUG_ON 0
73 #define SIMULATION_ON 0
74 
75 /*INDI controls */
76 
77 /* Connect/Disconnect */
78 static ISwitch PowerS[] = { { "CONNECT", "Connect", ISS_OFF, 0, 0 }, { "DISCONNECT", "Disconnect", ISS_ON, 0, 0 } };
79 static ISwitchVectorProperty PowerSP = { mydev, "CONNECTION", "Connection", MAIN_GROUP, IP_RW, ISR_1OFMANY,
80  60, IPS_IDLE, PowerS, NARRAY(PowerS), "", 0 };
81 
82 /* Connection Port */
83 static IText PortT[] = { { "PORT", "Port", 0, 0, 0, 0 } };
84 static ITextVectorProperty PortTP = { mydev, "DEVICE_PORT", "Ports", MAIN_GROUP, IP_RW, 0,
85  IPS_IDLE, PortT, NARRAY(PortT), "", 0 };
86 
87 /* Home/Learn Swtich */
88 static ISwitch HomeS[] = { { "Find", "", ISS_OFF, 0, 0 } };
89 static ISwitchVectorProperty HomeSP = { mydev, "HOME", "", MAIN_GROUP, IP_RW, ISR_1OFMANY,
90  60, IPS_IDLE, HomeS, NARRAY(HomeS), "", 0 };
91 
92 /* Filter Count */
93 static INumber FilterCountN[] = { { "Count", "", "%2.0f", 0, MAX_FILTER_COUNT, 1, DEFAULT_FILTER_COUNT, 0, 0, 0 } };
94 static INumberVectorProperty FilterCountNP = { mydev, "Filter Count", "", MAIN_GROUP, IP_RW, 0, IPS_IDLE,
95  FilterCountN, NARRAY(FilterCountN), "", 0 };
96 
97 /* Filter Position */
98 static INumber FilterPositionN[] = { { "FILTER_SLOT_VALUE", "Active Filter", "%2.0f", 1, DEFAULT_FILTER_COUNT, 1, 1, 0,
99  0, 0 } };
100 static INumberVectorProperty FilterPositionNP = {
101  mydev, "FILTER_SLOT", "Filter", MAIN_GROUP, IP_RW, 0, IPS_IDLE, FilterPositionN, NARRAY(FilterPositionN), "", 0
102 };
103 
104 /* send client definitions of all properties */
105 void ISInit()
106 {
107  static int isInit = 0;
108 
109  if (isInit)
110  return;
111 
112  targetFilter = -1;
113  fd = -1;
114 
115  isInit = 1;
116 }
117 
118 void ISGetProperties(const char *dev)
119 {
120  ISInit();
121 
122  if (dev != nullptr && strcmp(mydev, dev))
123  return;
124 
125  /* Main Control */
126  IDDefSwitch(&PowerSP, NULL);
127  IDDefText(&PortTP, NULL);
128  IDDefNumber(&FilterCountNP, NULL);
129  IDDefSwitch(&HomeSP, NULL);
130  IDDefNumber(&FilterPositionNP, NULL);
131 }
132 
133 void ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[],
134  char *names[], int n)
135 {
136  INDI_UNUSED(dev);
137  INDI_UNUSED(name);
138  INDI_UNUSED(sizes);
139  INDI_UNUSED(blobsizes);
140  INDI_UNUSED(blobs);
141  INDI_UNUSED(formats);
142  INDI_UNUSED(names);
143  INDI_UNUSED(n);
144 }
146 {
147  INDI_UNUSED(root);
148 }
149 
150 void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
151 {
152  /* ignore if not ours */
153  if (dev != nullptr && strcmp(dev, mydev))
154  return;
155 
156  ISInit();
157 
158  /* Connection */
159  if (!strcmp(name, PowerSP.name))
160  {
161  IUUpdateSwitch(&PowerSP, states, names, n);
162  connectFilter();
163  return;
164  }
165 
166  /* Home Search */
167  if (!strcmp(name, HomeSP.name))
168  {
169  int err;
170  int nbytes = 0;
171  unsigned char type = 0x03;
172  unsigned char chksum = COMM_INIT + type + COMM_FILL;
173  unsigned char filter_command[CMD_SIZE];
174  //snprintf(filter_command, CMD_SIZE, "%c%c%c%c%c", COMM_PRE, COMM_INIT, type, COMM_FILL, chksum);
175  snprintf(filter_command, CMD_SIZE, "%c%c%c%c", COMM_INIT, type, COMM_FILL, chksum);
176 
177  if (checkPowerS(&HomeSP))
178  return;
179 
180  if (!SIMULATION_ON)
181  err = tty_write(fd, filter_command, CMD_SIZE, &nbytes);
182 
183  if (DEBUG_ON)
184  {
185  IDLog("Home Search Command (int): #%d#%d#%d#%d#\n", COMM_INIT, type, COMM_FILL, chksum);
186  IDLog("Home Search Command (char): #%c#%c#%c#%c#\n", COMM_INIT, type, COMM_FILL, chksum);
187  }
188 
189  /* Send Home Command */
190  if (err != TTY_OK)
191  {
192  char error_message[ERRMSG_SIZE];
193  tty_error_msg(err, error_message, ERRMSG_SIZE);
194 
195  HomeSP.s = IPS_ALERT;
196  IDSetSwitch(&HomeSP, "Sending command Home to filter failed. %s", error_message);
197  IDLog("Sending command Home to filter failed. %s\n", error_message);
198 
199  return;
200  }
201 
202  FilterPositionN[0].value = 1;
203  FilterPositionNP.s = IPS_OK;
204  HomeSP.s = IPS_OK;
205  IDSetSwitch(&HomeSP, "Filter set to HOME.");
206  IDSetNumber(&FilterPositionNP, NULL);
207  }
208 }
209 
210 void ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
211 {
212  ISInit();
213 
214  /* ignore if not ours */
215  if (dev != nullptr && strcmp(mydev, dev))
216  return;
217 
218  if (!strcmp(name, PortTP.name))
219  {
220  if (IUUpdateText(&PortTP, texts, names, n))
221  return;
222 
223  PortTP.s = IPS_OK;
224  IDSetText(&PortTP, NULL);
225  }
226 }
227 
228 void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
229 {
230  long err;
231  INumber *np;
232 
233  /* ignore if not ours */
234  if (dev != nullptr && strcmp(dev, mydev))
235  return;
236 
237  ISInit();
238 
239  if (!strcmp(FilterPositionNP.name, name))
240  {
241  if (!isFilterConnected())
242  {
243  IDMessage(mydev, "Filter is not connected.");
244  FilterPositionNP.s = IPS_IDLE;
245  IDSetNumber(&FilterPositionNP, NULL);
246  return;
247  }
248 
249  np = IUFindNumber(&FilterPositionNP, names[0]);
250  if (np == &FilterPositionN[0])
251  {
252  targetFilter = values[0];
253  int nbytes = 0;
254  unsigned char type = 0x01;
255  unsigned char chksum = COMM_INIT + type + (unsigned char)targetFilter;
256  /*char filter_command[5] = { COMM_PRE, COMM_INIT, type, targetFilter, chksum };*/
257  unsigned char filter_command[CMD_SIZE];
258 
259  if (targetFilter < FilterPositionN[0].min || targetFilter > FilterPositionN[0].max)
260  {
261  FilterPositionNP.s = IPS_ALERT;
262  IDSetNumber(&FilterPositionNP, "Error: valid range of filter is from %d to %d",
263  (int)FilterPositionN[0].min, (int)FilterPositionN[0].max);
264  return;
265  }
266 
267  IUUpdateNumber(&FilterPositionNP, values, names, n);
268 
269  //snprintf(filter_command, CMD_SIZE, "%c%c%c%c%c", COMM_PRE, COMM_INIT, type, targetFilter, chksum);
270  snprintf(filter_command, CMD_SIZE, "%c%c%c%c", COMM_INIT, type, targetFilter, chksum);
271 
272  if (DEBUG_ON)
273  {
274  IDLog("Filter Position Command (int): #%d#%d#%d#%d#\n", COMM_INIT, type, targetFilter, chksum);
275  IDLog("Filter Position Command (char): #%c#%c#%c#%c#\n", COMM_INIT, type, targetFilter, chksum);
276  }
277 
278  if (!SIMULATION_ON)
279  err = tty_write(fd, filter_command, CMD_SIZE, &nbytes);
280 
281  FilterPositionNP.s = IPS_OK;
282  IDSetNumber(&FilterPositionNP, "Setting current filter to slot %d", targetFilter);
283  }
284  else
285  {
286  FilterPositionNP.s = IPS_IDLE;
287  IDSetNumber(&FilterPositionNP, NULL);
288  }
289 
290  return;
291  }
292 
293  if (!strcmp(FilterCountNP.name, name))
294  {
295  if (!isFilterConnected())
296  {
297  IDMessage(mydev, "Filter is not connected.");
298  FilterCountNP.s = IPS_IDLE;
299  IDSetNumber(&FilterCountNP, NULL);
300  return;
301  }
302 
303  np = IUFindNumber(&FilterCountNP, names[0]);
304  if (np == &FilterCountN[0])
305  {
306  if (IUUpdateNumber(&FilterCountNP, values, names, n) < 0)
307  return;
308 
309  FilterPositionN[0].min = 1;
310  FilterPositionN[0].max = (int)FilterCountN[0].value;
311  IUUpdateMinMax(&FilterPositionNP);
312 
313  FilterCountNP.s = IPS_OK;
314  IDSetNumber(&FilterCountNP, "Updated number of available filters to %d", ((int)FilterCountN[0].value));
315  }
316  else
317  {
318  FilterCountNP.s = IPS_IDLE;
319  IDSetNumber(&FilterCountNP, NULL);
320  }
321 
322  return;
323  }
324 }
325 
327 {
328  int i = 0;
329  for (i = 0; i < sp->nsp; i++)
330  {
331  if (sp->sp[i].s == ISS_ON)
332  return i;
333  }
334 
335  return -1;
336 }
337 
339 {
340  if (PowerSP.s != IPS_OK)
341  {
342  if (!strcmp(sp->label, ""))
343  IDMessage(mydev, "Cannot change property %s while the wheel is offline.", sp->name);
344  else
345  IDMessage(mydev, "Cannot change property %s while the wheel is offline.", sp->label);
346 
347  sp->s = IPS_IDLE;
348  IDSetSwitch(sp, NULL);
349  return -1;
350  }
351 
352  return 0;
353 }
354 
356 {
357  if (PowerSP.s != IPS_OK)
358  {
359  if (!strcmp(np->label, ""))
360  IDMessage(mydev, "Cannot change property %s while the wheel is offline.", np->name);
361  else
362  IDMessage(mydev, "Cannot change property %s while the wheel is offline.", np->label);
363 
364  np->s = IPS_IDLE;
365  IDSetNumber(np, NULL);
366  return -1;
367  }
368 
369  return 0;
370 }
371 
373 {
374  if (PowerSP.s != IPS_OK)
375  {
376  if (!strcmp(tp->label, ""))
377  IDMessage(mydev, "Cannot change property %s while the wheel is offline.", tp->name);
378  else
379  IDMessage(mydev, "Cannot change property %s while the wheel is offline.", tp->label);
380 
381  tp->s = IPS_IDLE;
382  IDSetText(tp, NULL);
383  return -1;
384  }
385 
386  return 0;
387 }
388 
390 {
391  int err;
392  char errmsg[ERRMSG_SIZE];
393 
394  switch (PowerS[0].s)
395  {
396  case ISS_ON:
397  if (SIMULATION_ON)
398  {
399  PowerSP.s = IPS_OK;
400  IDSetSwitch(&PowerSP, "Simulated Filter Wheel is online.");
401  return;
402  }
403 
404  if ((err = tty_connect(PortT[0].text, 9600, 8, 0, 1, &fd)) != TTY_OK)
405  {
406  PowerSP.s = IPS_ALERT;
407  IDSetSwitch(
408  &PowerSP,
409  "Error: cannot connect to %s. Make sure the filter is connected and you have access to the port.",
410  PortT[0].text);
411  tty_error_msg(err, errmsg, ERRMSG_SIZE);
412  IDLog("Error: %s\n", errmsg);
413  return;
414  }
415 
416  PowerSP.s = IPS_OK;
417  IDSetSwitch(&PowerSP, "Filter Wheel is online. True Technology filter wheel suffers from several bugs. "
418  "Please refer to http://indi.sf.net/profiles/trutech.html for more details.");
419  break;
420 
421  case ISS_OFF:
422  if (SIMULATION_ON)
423  {
424  PowerSP.s = IPS_OK;
425  IDSetSwitch(&PowerSP, "Simulated Filter Wheel is offline.");
426  return;
427  }
428 
429  if ((err = tty_disconnect(fd)) != TTY_OK)
430  {
431  PowerSP.s = IPS_ALERT;
432  IDSetSwitch(&PowerSP, "Error: cannot disconnect.");
433  tty_error_msg(err, errmsg, ERRMSG_SIZE);
434  IDLog("Error: %s\n", errmsg);
435  return;
436  }
437 
438  PowerSP.s = IPS_IDLE;
439  IDSetSwitch(&PowerSP, "Filter Wheel is offline.");
440  break;
441  }
442 }
443 
444 /* isFilterConnected: return 1 if we have a connection, 0 otherwise */
446 {
447  return ((PowerS[0].s == ISS_ON) ? 1 : 0);
448 }
Public interface to INDI's eventloop mechanism.
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
#define NARRAY(a)
Handy macro to find the number of elements in array a[]. Must be used with actual array,...
Definition: indiapi.h:500
@ IP_RW
Definition: indiapi.h:186
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
Establishes a tty connection to a terminal device.
Definition: indicom.c:946
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_disconnect(int fd)
Closes a tty connection and flushes the bus.
Definition: indicom.c:1148
void IDLog(const char *fmt,...)
Definition: indicom.c:316
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
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indidevapi.c:66
Interface to the reference INDI C API device implementation on the Device Driver side.
#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 IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
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
void IDDefNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1104
void IDDefText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1081
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
void IDDefSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1127
int fd
Definition: intelliscope.c:43
ISwitchVectorProperty PowerSP
Definition: intelliscope.c:46
ITextVectorProperty PortTP
Definition: magellan1.cpp:65
__le16 type
Definition: pwc-ioctl.h:0
One number descriptor.
One switch descriptor.
One text descriptor.
Number vector property descriptor.
Definition: indiapi.h:319
char name[MAXINDINAME]
Definition: indiapi.h:323
char label[MAXINDILABEL]
Definition: indiapi.h:325
Switch vector property descriptor.
Definition: indiapi.h:367
char name[MAXINDINAME]
Definition: indiapi.h:371
char label[MAXINDILABEL]
Definition: indiapi.h:373
Text vector property descriptor.
Definition: indiapi.h:246
char label[MAXINDILABEL]
Definition: indiapi.h:252
char name[MAXINDINAME]
Definition: indiapi.h:250
const unsigned char COMM_FILL
Definition: trutech_wheel.c:55
#define SIMULATION_ON
Definition: trutech_wheel.c:73
#define ERRMSG_SIZE
Definition: trutech_wheel.c:63
int manageDefaults(char errmsg[])
Definition: fli_wheel.c:364
#define DEFAULT_FILTER_COUNT
Definition: trutech_wheel.c:61
void ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Update data of an existing blob vector property.
void connectFilter(void)
#define CMD_SIZE
Definition: trutech_wheel.c:65
void ISInit(void)
void handleExposure(void *)
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
void ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Update the value of an existing text vector property.
int checkPowerS(ISwitchVectorProperty *sp)
void ISGetProperties(const char *dev)
Get Device Properties.
#define DEBUG_ON
Definition: trutech_wheel.c:72
#define MAX_FILTER_COUNT
Definition: trutech_wheel.c:62
int checkPowerN(INumberVectorProperty *np)
#define mydev
Definition: trutech_wheel.c:57
const unsigned char COMM_PRE
Definition: trutech_wheel.c:53
int getOnSwitch(ISwitchVectorProperty *sp)
void ISPoll(void *)
Definition: fli_wheel.c:393
#define MAIN_GROUP
Definition: trutech_wheel.c:58
void getBasicData(void)
Definition: fli_wheel.c:308
void ISSnoopDevice(XMLEle *root)
Function defined by Drivers that is called when another Driver it is snooping (by having previously c...
void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
const unsigned char COMM_INIT
Definition: trutech_wheel.c:54
int isFilterConnected(void)
int checkPowerT(ITextVectorProperty *tp)