Instrument Neutral Distributed Interface INDI  2.0.2
dome_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 "dome_script.h"
20 
21 #include "indicom.h"
22 
23 #include <cmath>
24 #include <memory>
25 #include <cstring>
26 #include <unistd.h>
27 #include <sys/wait.h>
28 #include <errno.h>
29 
30 #define MAXARGS 20
31 
32 typedef enum
33 {
48 
49 static std::unique_ptr<DomeScript> scope_script(new DomeScript());
50 
52 {
54 }
55 
57 {
58  return (const char *)"Dome Scripting Gateway";
59 }
60 
62 {
65 #if defined(__APPLE__)
66  IUFillText(&ScriptsT[SCRIPT_FOLDER], "SCRIPT_FOLDER", "Folder", "/usr/local/share/indi/scripts");
67 #else
68  IUFillText(&ScriptsT[SCRIPT_FOLDER], "SCRIPT_FOLDER", "Folder", "/usr/share/indi/scripts");
69 #endif
70  IUFillText(&ScriptsT[SCRIPT_CONNECT], "SCRIPT_CONNECT", "Connect script", "connect.py");
71  IUFillText(&ScriptsT[SCRIPT_DISCONNECT], "SCRIPT_DISCONNECT", "Disconnect script", "disconnect.py");
72  IUFillText(&ScriptsT[SCRIPT_STATUS], "SCRIPT_STATUS", "Get status script", "status.py");
73  IUFillText(&ScriptsT[SCRIPT_OPEN], "SCRIPT_OPEN", "Open shutter script", "open.py");
74  IUFillText(&ScriptsT[SCRIPT_CLOSE], "SCRIPT_CLOSE", "Close shutter script", "close.py");
75  IUFillText(&ScriptsT[SCRIPT_PARK], "SCRIPT_PARK", "Park script", "park.py");
76  IUFillText(&ScriptsT[SCRIPT_UNPARK], "SCRIPT_UNPARK", "Unpark script", "unpark.py");
77  IUFillText(&ScriptsT[SCRIPT_GOTO], "SCRIPT_GOTO", "Goto script", "goto.py");
78  IUFillText(&ScriptsT[SCRIPT_MOVE_CW], "SCRIPT_MOVE_CW", "Move clockwise script", "move_cw.py");
79  IUFillText(&ScriptsT[SCRIPT_MOVE_CCW], "SCRIPT_MOVE_CCW", "Move counter clockwise script", "move_ccw.py");
80  IUFillText(&ScriptsT[SCRIPT_ABORT], "SCRIPT_ABORT", "Abort motion script", "abort.py");
81  IUFillTextVector(&ScriptsTP, ScriptsT, SCRIPT_COUNT, getDefaultName(), "SCRIPTS", "Scripts", OPTIONS_TAB, IP_RW, 60,
82  IPS_IDLE);
83 
85  return true;
86 }
87 
89 {
91  IUSaveConfigText(fp, &ScriptsTP);
92  return true;
93 }
94 
95 void DomeScript::ISGetProperties(const char *dev)
96 {
98  defineProperty(&ScriptsTP);
99  loadConfig(true, "SCRIPTS");
100 }
101 
102 bool DomeScript::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
103 {
104  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
105  {
106  if (strcmp(name, ScriptsTP.name) == 0)
107  {
108  IUUpdateText(&ScriptsTP, texts, names, n);
109  ScriptsTP.s = IPS_OK;
110  IDSetText(&ScriptsTP, nullptr);
111  return true;
112  }
113  }
114  return Dome::ISNewText(dev, name, texts, names, n);
115 }
116 
117 bool DomeScript::RunScript(int script, ...)
118 {
119  char tmp[256];
120  strncpy(tmp, ScriptsT[script].text, sizeof(tmp));
121 
122  char **args = (char **)malloc(MAXARGS * sizeof(char *));
123  int arg = 1;
124  char *p = tmp;
125  args[0] = p;
126  while (arg < MAXARGS)
127  {
128  char *pp = strstr(p, " ");
129  if (pp == nullptr)
130  break;
131  *pp++ = 0;
132  args[arg++] = pp;
133  p = pp;
134  }
135  va_list ap;
136  va_start(ap, script);
137  while (arg < MAXARGS)
138  {
139  char *pp = va_arg(ap, char *);
140  args[arg++] = pp;
141  if (pp == nullptr)
142  break;
143  }
144  va_end(ap);
145  char path[1024];
146  snprintf(path, sizeof(path), "%s/%s", ScriptsT[0].text, tmp);
147 
148  if (access(path, F_OK | X_OK) != 0)
149  {
150  LOGF_ERROR("Cannot use script [%s], %s", path, strerror(errno));
151  return false;
152  }
153  if (isDebug())
154  {
155  char dbg[8 * 1024];
156  snprintf(dbg, sizeof(dbg), "execvp('%s'", path);
157  for (int i = 0; args[i]; i++)
158  {
159  strcat(dbg, ", '");
160  strcat(dbg, args[i]);
161  strcat(dbg, "'");
162  }
163  strcat(dbg, ", nullptr)");
164  LOG_DEBUG(dbg);
165  }
166 
167  int pid = fork();
168  if (pid == -1)
169  {
170  LOG_ERROR("Fork failed");
171  return false;
172  }
173  else if (pid == 0)
174  {
175  execvp(path, args);
176  LOG_ERROR("Failed to execute script");
177  exit(1);
178  }
179  else
180  {
181  int status;
182  waitpid(pid, &status, 0);
183  LOGF_DEBUG("Script %s returned %d", ScriptsT[script].text, status);
184  return status == 0;
185  }
186 }
187 
189 {
191  if (isConnected())
192  {
193  if (InitPark())
194  {
196  }
197  else
198  {
199  SetAxis1Park(0);
201  }
202  TimerHit();
203  }
204  return true;
205 }
206 
208 {
209  if (!isConnected())
210  return;
211  char tmpfile[] = "/tmp/indi_dome_script_status_XXXXXX";
212  int fd = mkstemp(tmpfile);
213  if (fd == -1)
214  {
215  LOGF_ERROR("Temp file %s creation for status script failed, %s", tmpfile, strerror(errno));
216  unlink(tmpfile);
217  return;
218  }
219  close(fd);
220  bool status = RunScript(SCRIPT_STATUS, tmpfile, nullptr);
221  if (status)
222  {
223  int parked = 0, shutter = 0;
224  float az = 0;
225  FILE *file = fopen(tmpfile, "r");
226  int rc = fscanf(file, "%d %d %f", &parked, &shutter, &az);
227  INDI_UNUSED(rc);
228  fclose(file);
229  unlink(tmpfile);
230  DomeAbsPosN[0].value = az = round(range360(az) * 10) / 10;
231  if (parked != 0)
232  {
234  {
235  SetParked(true);
236  TargetAz = az;
237  LOG_INFO("Park successfully executed");
238  }
239  }
240  else
241  {
243  {
244  SetParked(false);
245  TargetAz = az;
246  LOG_INFO("Unpark successfully executed");
247  }
248  }
249  if (std::round(az * 10) != std::round(TargetAz * 10))
250  {
251  LOGF_INFO("Moving %g -> %g %d", std::round(az * 10) / 10, std::round(TargetAz * 10) / 10, getDomeState());
252  IDSetNumber(&DomeAbsPosNP, nullptr);
253  }
254  else if (getDomeState() == DOME_MOVING)
255  {
257  IDSetNumber(&DomeAbsPosNP, nullptr);
258  }
260  {
261  if (shutter == 0)
262  {
265  IDSetSwitch(&DomeShutterSP, nullptr);
266  LOG_INFO("Shutter was successfully closed");
267  }
268  }
269  else
270  {
271  if (shutter == 1)
272  {
275  IDSetSwitch(&DomeShutterSP, nullptr);
276  LOG_INFO("Shutter was successfully opened");
277  }
278  }
279  }
280  else
281  {
282  LOG_ERROR("Failed to read status");
283  }
285  if (!isParked() && TimeSinceUpdate++ > 4)
286  {
287  TimeSinceUpdate = 0;
289  }
290 }
291 
293 {
294  if (isConnected())
295  return true;
296 
297  bool status = RunScript(SCRIPT_CONNECT, nullptr);
298  if (status)
299  {
300  LOG_INFO("Successfully connected");
301  }
302  else
303  {
304  LOG_WARN("Failed to connect");
305  }
306  return status;
307 }
308 
310 {
311  bool status = RunScript(SCRIPT_DISCONNECT, nullptr);
312  if (status)
313  {
314  LOG_INFO("Successfully disconnected");
315  }
316  else
317  {
318  LOG_WARN("Failed to disconnect");
319  }
320  return status;
321 }
322 
324 {
325  bool status = RunScript(SCRIPT_PARK, nullptr);
326  if (status)
327  {
328  return IPS_BUSY;
329  }
330  LOG_ERROR("Failed to park");
331  return IPS_ALERT;
332 }
333 
335 {
336  bool status = RunScript(SCRIPT_UNPARK, nullptr);
337  if (status)
338  {
339  return IPS_BUSY;
340  }
341  LOG_ERROR("Failed to unpark");
342  return IPS_ALERT;
343 }
344 
346 {
347  if (RunScript(operation == SHUTTER_OPEN ? SCRIPT_OPEN : SCRIPT_CLOSE, nullptr))
348  {
349  return IPS_BUSY;
350  }
351  LOGF_ERROR("Failed to %s shutter", operation == SHUTTER_OPEN ? "open" : "close");
352  return IPS_ALERT;
353 }
354 
356 {
357  char _az[16];
358  snprintf(_az, 16, "%f", round(az * 10) / 10);
359  bool status = RunScript(SCRIPT_GOTO, _az, nullptr);
360  if (status)
361  {
362  TargetAz = az;
363  return IPS_BUSY;
364  }
365  LOG_ERROR("Failed to MoveAbs");
366  return IPS_ALERT;
367 }
368 
370 {
371  if (operation == MOTION_START)
372  {
373  if (RunScript(dir == DOME_CW ? SCRIPT_MOVE_CW : SCRIPT_MOVE_CCW, nullptr))
374  {
376  TargetAz = -1;
377  }
378  else
379  {
381  }
382  }
383  else
384  {
385  if (RunScript(SCRIPT_ABORT, nullptr))
386  {
388  }
389  else
390  {
392  }
393  }
394  IDSetNumber(&DomeAbsPosNP, nullptr);
395  return ((operation == MOTION_START) ? IPS_BUSY : IPS_OK);
396 }
397 
399 {
400  bool status = RunScript(SCRIPT_ABORT, nullptr);
401  if (status)
402  {
403  LOG_INFO("Successfully aborted");
404  }
405  else
406  {
407  LOG_WARN("Failed to abort");
408  }
409  return status;
410 }
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual const char * getDefaultName() override
Definition: dome_script.cpp:56
virtual IPState Park() override
Goto Park Position. The park position is an absolute azimuth value.
virtual IPState UnPark() override
UnPark dome. The action of the Unpark command is dome specific, but it may include opening the shutte...
virtual bool Abort() override
Abort all dome motion.
bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual IPState Move(DomeDirection dir, DomeMotionCommand operation) override
Move the Dome in a particular direction.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: dome_script.cpp:61
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
Definition: dome_script.cpp:88
virtual IPState MoveAbs(double az) override
Move the Dome to an absolute azimuth.
void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
Definition: dome_script.cpp:95
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual IPState ControlShutter(ShutterOperation operation) override
Open or Close shutter.
virtual bool Disconnect() override
Disconnect from device.
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
@ DOME_CAN_PARK
Definition: indidome.h:159
@ DOME_CAN_ABS_MOVE
Definition: indidome.h:157
@ DOME_HAS_SHUTTER
Definition: indidome.h:161
@ DOME_CAN_ABORT
Definition: indidome.h:156
void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
Definition: indidome.cpp:1633
void SetDomeCapability(uint32_t cap)
SetDomeCapability set the dome capabilities. All capabilities must be initialized.
Definition: indidome.cpp:1561
bool isParked()
isParked is dome currently parked?
Definition: indidome.cpp:1639
@ DOME_UNPARKING
Definition: indidome.h:135
@ DOME_PARKED
Definition: indidome.h:136
@ DOME_UNPARKED
Definition: indidome.h:137
@ DOME_PARKING
Definition: indidome.h:134
@ DOME_MOVING
Definition: indidome.h:132
@ DOME_SYNCED
Definition: indidome.h:133
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...
Definition: indidome.cpp:259
ShutterState m_ShutterState
Definition: indidome.h:624
void SetAxis1Park(double value)
SetRAPark Set current AZ parking position. The data park file (stored in ~/.indi/ParkData....
Definition: indidome.cpp:1861
void UpdateMountCoords()
updateCoords updates the horizontal coordinates (Az & Alt) of the mount from the snooped RA,...
Definition: indidome.cpp:1488
INumber DomeAbsPosN[1]
Definition: indidome.h:534
void SetAxis1ParkDefault(double steps)
SetAxis1Park Set default AZ parking position.
Definition: indidome.cpp:1868
ShutterOperation
Shutter operation command.
Definition: indidome.h:114
@ SHUTTER_OPEN
Definition: indidome.h:115
DomeMotionCommand
Definition: indidome.h:97
@ MOTION_START
Definition: indidome.h:98
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indidome.cpp:93
INumberVectorProperty DomeAbsPosNP
Definition: indidome.h:533
ISwitchVectorProperty DomeShutterSP
Definition: indidome.h:548
@ SHUTTER_OPENED
Definition: indidome.h:147
@ SHUTTER_CLOSED
Definition: indidome.h:148
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indidome.cpp:279
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
Definition: indidome.cpp:1043
void setDomeState(const DomeState &value)
Definition: indidome.cpp:1156
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
Definition: indidome.cpp:1644
void SetParkDataType(DomeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
Definition: indidome.cpp:1587
DomeState getDomeState() const
Definition: indidome.h:285
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
#define MAXARGS
Definition: dome_script.cpp:30
scripts
Definition: dome_script.cpp:33
@ SCRIPT_FOLDER
Definition: dome_script.cpp:34
@ SCRIPT_MOVE_CW
Definition: dome_script.cpp:43
@ SCRIPT_OPEN
Definition: dome_script.cpp:38
@ SCRIPT_DISCONNECT
Definition: dome_script.cpp:36
@ SCRIPT_PARK
Definition: dome_script.cpp:40
@ SCRIPT_STATUS
Definition: dome_script.cpp:37
@ SCRIPT_CONNECT
Definition: dome_script.cpp:35
@ SCRIPT_MOVE_CCW
Definition: dome_script.cpp:44
@ SCRIPT_COUNT
Definition: dome_script.cpp:46
@ SCRIPT_UNPARK
Definition: dome_script.cpp:41
@ SCRIPT_ABORT
Definition: dome_script.cpp:45
@ SCRIPT_CLOSE
Definition: dome_script.cpp:39
@ SCRIPT_GOTO
Definition: dome_script.cpp:42
int errno
@ 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
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition: indicom.c:1245
Implementations for common driver routines.
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
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 IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#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
double round(double value, int decimal_places)