Instrument Neutral Distributed Interface INDI  2.0.2
telescope_script.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2016 CloudMakers, s. r. o.. 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 "telescope_script.h"
20 
21 #include <cstring>
22 #include <memory>
23 
24 #include <unistd.h>
25 #include <sys/wait.h>
26 #include <errno.h>
27 
28 #define MAXARGS 20
29 
30 typedef enum
31 {
46 
47 static std::unique_ptr<ScopeScript> scope_script(new ScopeScript());
48 
50 {
52 }
53 
55 {
56  return (const char *)"Telescope Scripting Gateway";
57 }
58 
60 {
62 
63 #if defined(__APPLE__)
64  IUFillText(&ScriptsT[0], "FOLDER", "Folder", "/usr/local/share/indi/scripts");
65 #else
66  IUFillText(&ScriptsT[0], "FOLDER", "Folder", "/usr/share/indi/scripts");
67 #endif
68  IUFillText(&ScriptsT[SCRIPT_CONNECT], "SCRIPT_CONNECT", "Connect script", "connect.py");
69  IUFillText(&ScriptsT[SCRIPT_DISCONNECT], "SCRIPT_DISCONNECT", "Disconnect script", "disconnect.py");
70  IUFillText(&ScriptsT[SCRIPT_STATUS], "SCRIPT_STATUS", "Get status script", "status.py");
71  IUFillText(&ScriptsT[SCRIPT_GOTO], "SCRIPT_GOTO", "Goto script", "goto.py");
72  IUFillText(&ScriptsT[SCRIPT_SYNC], "SCRIPT_SYNC", "Sync script", "sync.py");
73  IUFillText(&ScriptsT[SCRIPT_PARK], "SCRIPT_PARK", "Park script", "park.py");
74  IUFillText(&ScriptsT[SCRIPT_UNPARK], "SCRIPT_UNPARK", "Unpark script", "unpark.py");
75  IUFillText(&ScriptsT[SCRIPT_MOVE_NORTH], "SCRIPT_MOVE_NORTH", "Move north script", "move_north.py");
76  IUFillText(&ScriptsT[SCRIPT_MOVE_EAST], "SCRIPT_MOVE_EAST", "Move east script", "move_east.py");
77  IUFillText(&ScriptsT[SCRIPT_MOVE_SOUTH], "SCRIPT_MOVE_SOUTH", "Move south script", "move_south.py");
78  IUFillText(&ScriptsT[SCRIPT_MOVE_WEST], "SCRIPT_MOVE_WEST", "Move west script", "move_west.py");
79  IUFillText(&ScriptsT[SCRIPT_ABORT], "SCRIPT_ABORT", "Abort motion script", "abort.py");
80  IUFillTextVector(&ScriptsTP, ScriptsT, SCRIPT_COUNT, getDefaultName(), "SCRIPTS", "Scripts", OPTIONS_TAB, IP_RW, 60,
81  IPS_IDLE);
82 
85  return true;
86 }
87 
89 {
91  IUSaveConfigText(fp, &ScriptsTP);
92  return true;
93 }
94 
95 void ScopeScript::ISGetProperties(const char *dev)
96 {
98  defineProperty(&ScriptsTP);
99 }
100 
101 bool ScopeScript::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
102 {
103  if (strcmp(dev, getDeviceName()) == 0 && strcmp(name, ScriptsTP.name) == 0)
104  {
105  IUUpdateText(&ScriptsTP, texts, names, n);
106  IDSetText(&ScriptsTP, nullptr);
107  return true;
108  }
109  return Telescope::ISNewText(dev, name, texts, names, n);
110 }
111 
112 bool ScopeScript::RunScript(int script, ...)
113 {
114  char tmp[256];
115  strncpy(tmp, ScriptsT[script].text, sizeof(tmp));
116 
117  char **args = (char **)malloc(MAXARGS * sizeof(char *));
118  int arg = 1;
119  char *p = tmp;
120  args[0] = p;
121  while (arg < MAXARGS)
122  {
123  char *pp = strstr(p, " ");
124  if (pp == nullptr)
125  break;
126  *pp++ = 0;
127  args[arg++] = pp;
128  p = pp;
129  }
130  va_list ap;
131  va_start(ap, script);
132  while (arg < MAXARGS)
133  {
134  char *pp = va_arg(ap, char *);
135  args[arg++] = pp;
136  if (pp == nullptr)
137  break;
138  }
139  va_end(ap);
140  char path[1024];
141  snprintf(path, sizeof(path), "%s/%s", ScriptsT[0].text, tmp);
142 
143  if (access(path, F_OK | X_OK) != 0)
144  {
145  LOGF_ERROR("Cannot use script [%s], %s", path, strerror(errno));
146  return false;
147  }
148  if (isDebug())
149  {
150  char dbg[8 * 1024];
151  snprintf(dbg, sizeof(dbg), "execvp('%s'", path);
152  for (int i = 0; args[i]; i++)
153  {
154  strcat(dbg, ", '");
155  strcat(dbg, args[i]);
156  strcat(dbg, "'");
157  }
158  strcat(dbg, ", nullptr)");
159  LOG_DEBUG(dbg);
160  }
161 
162  int pid = fork();
163  if (pid == -1)
164  {
165  LOG_ERROR("Fork failed");
166  return false;
167  }
168  else if (pid == 0)
169  {
170  execvp(path, args);
171  LOG_ERROR("Failed to execute script");
172  exit(1);
173  }
174  else
175  {
176  int status;
177  waitpid(pid, &status, 0);
178  LOGF_DEBUG("Script %s returned %d", ScriptsT[script].text, status);
179  return status == 0;
180  }
181 }
182 
184 {
185  return true;
186 }
187 
189 {
190  if (isConnected())
191  return true;
192 
193  bool status = RunScript(SCRIPT_CONNECT, nullptr);
194  if (status)
195  {
196  LOG_INFO("Successfully connected");
197  ReadScopeStatus();
199  }
200  else
201  {
202  LOG_ERROR("Failed to connect");
203  }
204  return status;
205 }
206 
208 {
209  bool status = RunScript(SCRIPT_DISCONNECT, nullptr);
210  if (status)
211  {
212  LOG_INFO("Successfully disconnected");
213  }
214  else
215  {
216  LOG_WARN("Failed to disconnect");
217  }
218  return status;
219 }
220 
222 {
223  if (!isConnected())
224  return false;
225  char tmpfile[] = "/tmp/indi_telescope_script_status_XXXXXX";
226  int fd = mkstemp(tmpfile);
227  if (fd == -1)
228  {
229  LOGF_ERROR("Temp file %s creation for status script failed, %s", tmpfile, strerror(errno));
230  unlink(tmpfile);
231  return false;
232  }
233  close(fd);
234  bool status = RunScript(SCRIPT_STATUS, tmpfile, nullptr);
235  if (status)
236  {
237  int parked = 0;
238  float ra = 0, dec = 0;
239  FILE *file = fopen(tmpfile, "r");
240 
241  int rc = fscanf(file, "%d %f %f", &parked, &ra, &dec);
242  INDI_UNUSED(rc);
243  fclose(file);
244  unlink(tmpfile);
245  if (parked != 0)
246  {
247  if (!isParked())
248  {
249  SetParked(true);
250  LOG_INFO("Park successfully executed");
251  }
252  }
253  else
254  {
255  if (isParked())
256  {
257  SetParked(false);
258  LOG_INFO("Unpark successfully executed");
259  }
260  }
261  NewRaDec(ra, dec);
262  }
263  else
264  {
265  LOG_ERROR("Failed to read status");
266  }
267  return status;
268 }
269 
270 bool ScopeScript::Goto(double ra, double dec)
271 {
272  char _ra[16], _dec[16];
273  snprintf(_ra, 16, "%f", ra);
274  snprintf(_dec, 16, "%f", dec);
275  bool status = RunScript(SCRIPT_GOTO, _ra, _dec, nullptr);
276  if (status)
277  {
278  LOG_INFO("Goto successfully executed");
280  }
281  else
282  {
283  LOG_ERROR("Goto failed");
284  }
285  return status;
286 }
287 
288 bool ScopeScript::Sync(double ra, double dec)
289 {
290  char _ra[16], _dec[16];
291  snprintf(_ra, 16, "%f", ra);
292  snprintf(_dec, 16, "%f", dec);
293  bool status = RunScript(SCRIPT_SYNC, _ra, _dec, nullptr);
294  if (status)
295  {
296  LOG_INFO("Sync successfully executed");
297  }
298  else
299  {
300  LOG_ERROR("Failed to sync");
301  }
302  return status;
303 }
304 
306 {
307  bool status = RunScript(SCRIPT_PARK, nullptr);
308  if (!status)
309  {
310  LOG_ERROR("Failed to park");
311  }
312  return status;
313 }
314 
316 {
317  bool status = RunScript(SCRIPT_UNPARK, nullptr);
318  if (!status)
319  {
320  LOG_ERROR("Failed to unpark");
321  }
322  return status;
323 }
324 
326 {
327  char _rate[] = { (char)('0' + IUFindOnSwitchIndex(&SlewRateSP)), 0 };
328  bool status = RunScript(command == MOTION_STOP ? SCRIPT_ABORT :
330  _rate, nullptr, nullptr);
331  return status;
332 }
333 
335 {
336  char _rate[] = { (char)('0' + IUFindOnSwitchIndex(&SlewRateSP)), 0 };
337  bool status =
338  RunScript(command == MOTION_STOP ? SCRIPT_ABORT : dir == DIRECTION_WEST ? SCRIPT_MOVE_WEST : SCRIPT_MOVE_EAST,
339  _rate, nullptr, nullptr);
340  return status;
341 }
342 
344 {
345  bool status = RunScript(SCRIPT_ABORT, nullptr);
346  if (status)
347  {
348  LOG_INFO("Successfully aborted");
349  }
350  else
351  {
352  LOG_ERROR("Failed to abort");
353  }
354  return status;
355 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
uint16_t getDriverInterface() const
void addDebugControl()
Add Debug control to the driver.
TelescopeStatus TrackState
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
bool isParked()
isParked is mount currently parked?
ISwitchVectorProperty SlewRateSP
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual const char * getDefaultName() override
void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool Park() override
Park the telescope to its home position.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool Disconnect() override
Disconnect from device.
void ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Update the value of an existing text vector property.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
int errno
double ra
double dec
@ IP_RW
Definition: indiapi.h:186
@ IPS_IDLE
Definition: indiapi.h:161
INDI_DIR_WE
Definition: indibasetypes.h:55
@ DIRECTION_WEST
Definition: indibasetypes.h:56
INDI_DIR_NS
Definition: indibasetypes.h:48
@ DIRECTION_NORTH
Definition: indibasetypes.h:49
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidevapi.c:20
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOG_WARN(txt)
Definition: indilogger.h:73
#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 LOG_INFO(txt)
Definition: indilogger.h:74
int fd
Definition: intelliscope.c:43
char name[MAXINDINAME]
Definition: indiapi.h:250
#define MAXARGS
@ SCRIPT_DISCONNECT
@ SCRIPT_MOVE_EAST
@ SCRIPT_MOVE_WEST
@ SCRIPT_PARK
@ SCRIPT_STATUS
@ SCRIPT_CONNECT
@ SCRIPT_COUNT
@ SCRIPT_SYNC
@ SCRIPT_UNPARK
@ SCRIPT_ABORT
@ SCRIPT_MOVE_SOUTH
@ SCRIPT_GOTO
@ SCRIPT_MOVE_NORTH