Instrument Neutral Distributed Interface INDI 2.2.5
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
48
49static std::unique_ptr<DomeScript> scope_script(new DomeScript());
50
54
56{
57 return "Dome Scripting Gateway";
58}
59
61{
63#if defined(__APPLE__)
64 ScriptsTP[SCRIPT_FOLDER].fill("SCRIPT_FOLDER", "Folder", "/usr/local/share/indi/scripts");
65#else
66 ScriptsTP[SCRIPT_FOLDER].fill("SCRIPT_FOLDER", "Folder", "/usr/share/indi/scripts");
67#endif
68
69 ScriptsTP[SCRIPT_CONNECT].fill("SCRIPT_CONNECT", "Connect script", "connect.py");
70 ScriptsTP[SCRIPT_DISCONNECT].fill("SCRIPT_DISCONNECT", "Disconnect script", "disconnect.py");
71 ScriptsTP[SCRIPT_STATUS].fill("SCRIPT_STATUS", "Get status script", "status.py");
72 ScriptsTP[SCRIPT_OPEN].fill("SCRIPT_OPEN", "Open shutter script", "open.py");
73 ScriptsTP[SCRIPT_CLOSE].fill("SCRIPT_CLOSE", "Close shutter script", "close.py");
74 ScriptsTP[SCRIPT_PARK].fill("SCRIPT_PARK", "Park script", "park.py");
75 ScriptsTP[SCRIPT_UNPARK].fill("SCRIPT_UNPARK", "Unpark script", "unpark.py");
76 ScriptsTP[SCRIPT_GOTO].fill("SCRIPT_GOTO", "Goto script", "goto.py");
77 ScriptsTP[SCRIPT_MOVE_CW].fill("SCRIPT_MOVE_CW", "Move clockwise script", "move_cw.py");
78 ScriptsTP[SCRIPT_MOVE_CCW].fill("SCRIPT_MOVE_CCW", "Move counter clockwise script", "move_ccw.py");
79 ScriptsTP[SCRIPT_ABORT].fill("SCRIPT_ABORT", "Abort motion script", "abort.py");
80 ScriptsTP.fill(getDeviceName(), "SCRIPTS", "Scripts", OPTIONS_TAB, IP_RW, 60, IPS_IDLE);
81 ScriptsTP.load();
82
83 // Dome Type
84 TypeSP[Dome].fill("DOME", "Dome", ISS_ON);
85 TypeSP[Rolloff].fill("ROLLOFF", "Roll off", ISS_OFF);
86 TypeSP.fill(getDeviceName(), "DOME_TYPE", "Type", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
87 TypeSP.load();
88
89 if (TypeSP[Dome].getState() == ISS_ON)
90 {
93 }
94 else
95 {
98 }
99
101 return true;
102}
103
108{
110
111 TypeSP.save(fp);
112 ScriptsTP.save(fp);
113 return true;
114}
115
119void DomeScript::ISGetProperties(const char *dev)
120{
122
123 defineProperty(TypeSP);
124 defineProperty(ScriptsTP);
125}
126
130bool DomeScript::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
131{
132 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
133 {
134 if (ScriptsTP.isNameMatch(name))
135 {
136 ScriptsTP.update(texts, names, n);
137 ScriptsTP.setState(IPS_OK);
138 ScriptsTP.apply();
139 saveConfig(ScriptsTP);
140 return true;
141 }
142 }
143 return Dome::ISNewText(dev, name, texts, names, n);
144}
145
149bool DomeScript::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
150{
151 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
152 {
153 if (TypeSP.isNameMatch(name))
154 {
155 TypeSP.update(states, names, n);
156 TypeSP.setState(IPS_OK);
157 TypeSP.apply();
158 saveConfig(TypeSP);
159 LOG_INFO("Driver must be restarted for this change to take effect");
160 return true;
161 }
162 }
163 return Dome::ISNewSwitch(dev, name, states, names, n);
164}
165
169bool DomeScript::RunScript(int script, ...)
170{
171 char tmp[256];
172 snprintf(tmp, sizeof(tmp), "%s", ScriptsTP[script].getText());
173
174 char **args = (char **)malloc(MAXARGS * sizeof(char *));
175 int arg = 1;
176 char *p = tmp;
177 args[0] = p;
178 while (arg < MAXARGS)
179 {
180 char *pp = strstr(p, " ");
181 if (pp == nullptr)
182 break;
183 *pp++ = 0;
184 args[arg++] = pp;
185 p = pp;
186 }
187 va_list ap;
188 va_start(ap, script);
189 while (arg < MAXARGS)
190 {
191 char *pp = va_arg(ap, char *);
192 args[arg++] = pp;
193 if (pp == nullptr)
194 break;
195 }
196 va_end(ap);
197 char path[1024];
198 snprintf(path, sizeof(path), "%s/%s", ScriptsTP[0].getText(), tmp);
199
200 if (access(path, F_OK | X_OK) != 0)
201 {
202 LOGF_ERROR("Cannot use script [%s], %s", path, strerror(errno));
203 return false;
204 }
205 if (isDebug())
206 {
207 char dbg[8 * 1024];
208 snprintf(dbg, sizeof(dbg), "execvp('%s'", path);
209 for (int i = 0; args[i]; i++)
210 {
211 strcat(dbg, ", '");
212 strcat(dbg, args[i]);
213 strcat(dbg, "'");
214 }
215 strcat(dbg, ", nullptr)");
216 LOG_DEBUG(dbg);
217 }
218
219 int pid = fork();
220 if (pid == -1)
221 {
222 LOG_ERROR("Fork failed");
223 return false;
224 }
225 else if (pid == 0)
226 {
227 execvp(path, args);
228 LOG_ERROR("Failed to execute script");
229 exit(1);
230 }
231 else
232 {
233 int status;
234 waitpid(pid, &status, 0);
235 LOGF_DEBUG("Script %s returned %d", ScriptsTP[script].getText(), status);
236 return status == 0;
237 }
238}
239
244{
246 if (isConnected())
247 {
248 if (InitPark())
249 {
251 }
252 else
253 {
254 SetAxis1Park(0);
256 }
257 TimerHit();
258 }
259 return true;
260}
261
266{
267 if (!isConnected())
268 return;
269 char tmpfile[] = "/tmp/indi_dome_script_status_XXXXXX";
270 int fd = mkstemp(tmpfile);
271 if (fd == -1)
272 {
273 LOGF_ERROR("Temp file %s creation for status script failed, %s", tmpfile, strerror(errno));
274 unlink(tmpfile);
275 return;
276 }
277 close(fd);
278 bool status = RunScript(SCRIPT_STATUS, tmpfile, nullptr);
279 if (status)
280 {
281 int parked = 0, shutter = 0;
282 float az = 0;
283 FILE *file = fopen(tmpfile, "r");
284 int rc = fscanf(file, "%d %d %f", &parked, &shutter, &az);
285 INDI_UNUSED(rc);
286 fclose(file);
287 unlink(tmpfile);
288 az = round(range360(az) * 10) / 10;
289 DomeAbsPosNP[0].setValue(az);
290
291 if (parked != 0)
292 {
294 {
295 SetParked(true);
296 TargetAz = az;
297 LOG_INFO("Park successfully executed");
298 }
299 }
300 else
301 {
303 {
304 SetParked(false);
305 TargetAz = az;
306 LOG_INFO("Unpark successfully executed");
307 }
308 }
309 if (TypeSP[Dome].getState() == ISS_ON && std::round(az * 10) != std::round(TargetAz * 10))
310 {
311 LOGF_INFO("Moving %g -> %g %d", std::round(az * 10) / 10, std::round(TargetAz * 10) / 10, getDomeState());
313 }
314 else if (getDomeState() == DOME_MOVING)
315 {
318 }
319
320 if (TypeSP[Dome].getState() == ISS_ON)
321 {
323 {
324 if (shutter == 0)
325 {
329 LOG_INFO("Shutter was successfully closed");
330 }
331 }
332 else
333 {
334 if (shutter == 1)
335 {
339 LOG_INFO("Shutter was successfully opened");
340 }
341 }
342 }
343 }
344 else
345 {
346 LOG_ERROR("Failed to read status");
347 }
349
350 if (TypeSP[Dome].getState() == ISS_ON && !isParked() && TimeSinceUpdate++ > 4)
351 {
352 TimeSinceUpdate = 0;
354 }
355}
356
361{
362 if (isConnected())
363 return true;
364
365 bool status = RunScript(SCRIPT_CONNECT, nullptr);
366 if (status)
367 {
368 LOG_INFO("Successfully connected");
369 }
370 else
371 {
372 LOG_WARN("Failed to connect");
373 }
374 return status;
375}
376
381{
382 bool status = RunScript(SCRIPT_DISCONNECT, nullptr);
383 if (status)
384 {
385 LOG_INFO("Successfully disconnected");
386 }
387 else
388 {
389 LOG_WARN("Failed to disconnect");
390 }
391 return status;
392}
393
398{
399 bool status = RunScript(SCRIPT_PARK, nullptr);
400 if (status)
401 {
402 return IPS_BUSY;
403 }
404 LOG_ERROR("Failed to park");
405 return IPS_ALERT;
406}
407
412{
413 bool status = RunScript(SCRIPT_UNPARK, nullptr);
414 if (status)
415 {
416 return IPS_BUSY;
417 }
418 LOG_ERROR("Failed to unpark");
419 return IPS_ALERT;
420}
421
426{
427 if (RunScript(operation == SHUTTER_OPEN ? SCRIPT_OPEN : SCRIPT_CLOSE, nullptr))
428 {
429 return IPS_BUSY;
430 }
431 LOGF_ERROR("Failed to %s shutter", operation == SHUTTER_OPEN ? "open" : "close");
432 return IPS_ALERT;
433}
434
439{
440 char _az[16];
441 snprintf(_az, 16, "%f", round(az * 10) / 10);
442 bool status = RunScript(SCRIPT_GOTO, _az, nullptr);
443 if (status)
444 {
445 TargetAz = az;
446 return IPS_BUSY;
447 }
448 LOG_ERROR("Failed to MoveAbs");
449 return IPS_ALERT;
450}
451
456{
457 auto commandOk = false;
458 if (operation == MOTION_START)
459 {
460 if (RunScript(dir == DOME_CW ? SCRIPT_MOVE_CW : SCRIPT_MOVE_CCW, nullptr))
461 {
462 commandOk = true;
463 TargetAz = -1;
464 }
465 else
466 {
467 commandOk = false;
468 }
469 }
470 else
471 {
472 commandOk = RunScript(SCRIPT_ABORT, nullptr);
473 }
474
475 auto state = commandOk ? (operation == MOTION_START ? IPS_BUSY : IPS_OK) : IPS_ALERT;
476 if (TypeSP[Dome].getState())
477 {
478 DomeAbsPosNP.setState(state);
480 }
481
482 return state;
483}
484
489{
490 bool status = RunScript(SCRIPT_ABORT, nullptr);
491 if (status)
492 {
493 LOG_INFO("Successfully aborted");
494 }
495 else
496 {
497 LOG_WARN("Failed to abort");
498 }
499 return status;
500}
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual const char * getDefaultName() override
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.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, 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
Initialize properties initial state and value. The child class must implement this function.
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
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...
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.
Definition dome.h:33
bool isConnected() const
INDI::PropertyText getText(const char *name) const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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.
void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
void SetDomeCapability(uint32_t cap)
SetDomeCapability set the dome capabilities. All capabilities must be initialized.
bool isParked()
isParked is dome currently parked?
@ DOME_UNPARKING
Definition indidome.h:135
@ DOME_UNPARKED
Definition indidome.h:137
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:270
ShutterState m_ShutterState
Definition indidome.h:612
void SetAxis1Park(double value)
SetRAPark Set current AZ parking position. The data park file (stored in ~/.indi/ParkData....
void UpdateMountCoords()
updateCoords updates the horizontal coordinates (Az & Alt) of the mount from the snooped RA,...
void SetAxis1ParkDefault(double steps)
SetAxis1Park Set default AZ parking position.
ShutterOperation
Shutter operation command.
Definition indidome.h:114
DomeMotionCommand
Definition indidome.h:97
@ MOTION_START
Definition indidome.h:98
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition indidome.cpp:105
@ 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
@ 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:284
INDI::PropertyNumber DomeAbsPosNP
Definition indidome.h:536
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
void setDomeState(const DomeState &value)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
void SetParkDataType(DomeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
INDI::PropertySwitch DomeShutterSP
Definition indidome.h:546
DomeState getDomeState() const
Definition indidome.h:285
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
bool isNameMatch(const char *otherName) const
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
bool update(const char *const texts[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
#define MAXARGS
scripts
@ SCRIPT_FOLDER
@ SCRIPT_MOVE_CW
@ SCRIPT_OPEN
@ SCRIPT_DISCONNECT
@ SCRIPT_PARK
@ SCRIPT_STATUS
@ SCRIPT_CONNECT
@ SCRIPT_MOVE_CCW
@ SCRIPT_COUNT
@ SCRIPT_UNPARK
@ SCRIPT_ABORT
@ SCRIPT_CLOSE
@ SCRIPT_GOTO
int errno
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ 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
@ ISR_1OFMANY
Definition indiapi.h:171
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition indicom.c:1321
Implementations for common driver routines.
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#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
#define getDeviceName
int fd
double round(double value, int decimal_places)