| 1 | /* evaluate an expression of INDI operands
 | 
|---|
| 2 |  */
 | 
|---|
| 3 | 
 | 
|---|
| 4 | /* Overall design:
 | 
|---|
| 5 |  * compile expression, building operand table, if trouble exit 2
 | 
|---|
| 6 |  * open INDI connection, if trouble exit 2
 | 
|---|
| 7 |  * send getProperties as required to get operands flowing
 | 
|---|
| 8 |  * watch for messages until get initial values of each operand
 | 
|---|
| 9 |  * evaluate expression, repeat if -w each time an op arrives until true
 | 
|---|
| 10 |  * exit val==0
 | 
|---|
| 11 |  */
 | 
|---|
| 12 | 
 | 
|---|
| 13 | #include <stdio.h>
 | 
|---|
| 14 | #include <stdlib.h>
 | 
|---|
| 15 | #include <math.h>
 | 
|---|
| 16 | #include <string.h>
 | 
|---|
| 17 | #include <signal.h>
 | 
|---|
| 18 | #include <errno.h>
 | 
|---|
| 19 | #include <time.h>
 | 
|---|
| 20 | #include <unistd.h>
 | 
|---|
| 21 | #include <sys/types.h>
 | 
|---|
| 22 | #include <sys/socket.h>
 | 
|---|
| 23 | #include <netinet/in.h>
 | 
|---|
| 24 | #include <netdb.h>
 | 
|---|
| 25 | 
 | 
|---|
| 26 | #include "indiapi.h"
 | 
|---|
| 27 | #include "lilxml.h"
 | 
|---|
| 28 | 
 | 
|---|
| 29 | extern int compileExpr (char *expr, char *errmsg);
 | 
|---|
| 30 | extern int evalExpr (double *vp, char *errmsg);
 | 
|---|
| 31 | extern int allOperandsSet (void);
 | 
|---|
| 32 | extern int getAllOperands (char ***ops);
 | 
|---|
| 33 | extern int getSetOperands (char ***ops);
 | 
|---|
| 34 | extern int getUnsetOperands (char ***ops);
 | 
|---|
| 35 | extern int setOperand (char *name, double valu);
 | 
|---|
| 36 | 
 | 
|---|
| 37 | static void usage (void);
 | 
|---|
| 38 | static void compile (char *expr);
 | 
|---|
| 39 | static FILE *openINDIServer (void);
 | 
|---|
| 40 | static void getProps(FILE *fp);
 | 
|---|
| 41 | static void initProps (FILE *fp);
 | 
|---|
| 42 | static int pstatestr (char *state);
 | 
|---|
| 43 | static time_t timestamp (char *ts);
 | 
|---|
| 44 | static int devcmp (char *op1, char *op2);
 | 
|---|
| 45 | static int runEval (FILE *fp);
 | 
|---|
| 46 | static int setOp (XMLEle *root);
 | 
|---|
| 47 | static XMLEle *nxtEle (FILE *fp);
 | 
|---|
| 48 | static int readServerChar (FILE *fp);
 | 
|---|
| 49 | static void onAlarm (int dummy);
 | 
|---|
| 50 | 
 | 
|---|
| 51 | static char *me;
 | 
|---|
| 52 | static char host_def[] = "localhost";   /* default host name */
 | 
|---|
| 53 | static char *host = host_def;           /* working host name */
 | 
|---|
| 54 | #define INDIPORT        7624            /* default port */
 | 
|---|
| 55 | static int port = INDIPORT;             /* working port number */
 | 
|---|
| 56 | #define TIMEOUT         2               /* default timeout, secs */
 | 
|---|
| 57 | static int timeout = TIMEOUT;           /* working timeout, secs */
 | 
|---|
| 58 | static LilXML *lillp;                   /* XML parser context */
 | 
|---|
| 59 | static int directfd = -1;               /* direct filedes to server, if >= 0 */
 | 
|---|
| 60 | static int verbose;                     /* more tracing */
 | 
|---|
| 61 | static int eflag;                       /* print each updated expression value*/
 | 
|---|
| 62 | static int fflag;                       /* print final expression value */
 | 
|---|
| 63 | static int iflag;                       /* read expresion from stdin */
 | 
|---|
| 64 | static int oflag;                       /* print operands as they change */
 | 
|---|
| 65 | static int wflag;                       /* wait for expression to be true */
 | 
|---|
| 66 | static int bflag;                       /* beep when true */
 | 
|---|
| 67 | 
 | 
|---|
| 68 | int
 | 
|---|
| 69 | main (int ac, char *av[])
 | 
|---|
| 70 | {
 | 
|---|
| 71 |         FILE *fp;
 | 
|---|
| 72 | 
 | 
|---|
| 73 |         /* save our name for usage() */
 | 
|---|
| 74 |         me = av[0];
 | 
|---|
| 75 | 
 | 
|---|
| 76 |         /* crack args */
 | 
|---|
| 77 |         while (--ac && **++av == '-') {
 | 
|---|
| 78 |             char *s = *av;
 | 
|---|
| 79 |             while (*++s) {
 | 
|---|
| 80 |                 switch (*s) {
 | 
|---|
| 81 |                 case 'b':       /* beep when true */
 | 
|---|
| 82 |                     bflag++;
 | 
|---|
| 83 |                     break;
 | 
|---|
| 84 |                 case 'd':
 | 
|---|
| 85 |                     if (ac < 2) {
 | 
|---|
| 86 |                         fprintf (stderr, "-d requires open fileno\n");
 | 
|---|
| 87 |                         usage();
 | 
|---|
| 88 |                     }
 | 
|---|
| 89 |                     directfd = atoi(*++av);
 | 
|---|
| 90 |                     ac--;
 | 
|---|
| 91 |                     break;
 | 
|---|
| 92 |                 case 'e':       /* print each updated expression value */
 | 
|---|
| 93 |                     eflag++;
 | 
|---|
| 94 |                     break;
 | 
|---|
| 95 |                 case 'f':       /* print final expression value */
 | 
|---|
| 96 |                     fflag++;
 | 
|---|
| 97 |                     break;
 | 
|---|
| 98 |                 case 'h':
 | 
|---|
| 99 |                     if (directfd >= 0) {
 | 
|---|
| 100 |                         fprintf (stderr, "Can not combine -d and -h\n");
 | 
|---|
| 101 |                         usage();
 | 
|---|
| 102 |                     }
 | 
|---|
| 103 |                     if (ac < 2) {
 | 
|---|
| 104 |                         fprintf (stderr, "-h requires host name\n");
 | 
|---|
| 105 |                         usage();
 | 
|---|
| 106 |                     }
 | 
|---|
| 107 |                     host = *++av;
 | 
|---|
| 108 |                     ac--;
 | 
|---|
| 109 |                     break;
 | 
|---|
| 110 |                 case 'i':       /* read expression from stdin */
 | 
|---|
| 111 |                     iflag++;
 | 
|---|
| 112 |                     break;
 | 
|---|
| 113 |                 case 'o':       /* print operands as they change */
 | 
|---|
| 114 |                     oflag++;
 | 
|---|
| 115 |                     break;
 | 
|---|
| 116 |                 case 'p':
 | 
|---|
| 117 |                     if (directfd >= 0) {
 | 
|---|
| 118 |                         fprintf (stderr, "Can not combine -d and -p\n");
 | 
|---|
| 119 |                         usage();
 | 
|---|
| 120 |                     }
 | 
|---|
| 121 |                     if (ac < 2) {
 | 
|---|
| 122 |                         fprintf (stderr, "-p requires tcp port number\n");
 | 
|---|
| 123 |                         usage();
 | 
|---|
| 124 |                     }
 | 
|---|
| 125 |                     port = atoi(*++av);
 | 
|---|
| 126 |                     ac--;
 | 
|---|
| 127 |                     break;
 | 
|---|
| 128 |                 case 't':
 | 
|---|
| 129 |                     if (ac < 2) {
 | 
|---|
| 130 |                         fprintf (stderr, "-t requires timeout\n");
 | 
|---|
| 131 |                         usage();
 | 
|---|
| 132 |                     }
 | 
|---|
| 133 |                     timeout = atoi(*++av);
 | 
|---|
| 134 |                     ac--;
 | 
|---|
| 135 |                     break;
 | 
|---|
| 136 |                 case 'v':       /* verbose */
 | 
|---|
| 137 |                     verbose++;
 | 
|---|
| 138 |                     break;
 | 
|---|
| 139 |                 case 'w':       /* wait for expression to be true */
 | 
|---|
| 140 |                     wflag++;
 | 
|---|
| 141 |                     break;
 | 
|---|
| 142 |                 default:
 | 
|---|
| 143 |                     fprintf (stderr, "Unknown flag: %c\n", *s);
 | 
|---|
| 144 |                     usage();
 | 
|---|
| 145 |                 }
 | 
|---|
| 146 |             }
 | 
|---|
| 147 |         }
 | 
|---|
| 148 | 
 | 
|---|
| 149 |         /* now there are ac args starting with av[0] */
 | 
|---|
| 150 | 
 | 
|---|
| 151 |         /* compile expression from av[0] or stdin */
 | 
|---|
| 152 |         if (ac == 0)
 | 
|---|
| 153 |             compile (NULL);
 | 
|---|
| 154 |         else if (ac == 1)
 | 
|---|
| 155 |             compile (av[0]);
 | 
|---|
| 156 |         else
 | 
|---|
| 157 |             usage();
 | 
|---|
| 158 | 
 | 
|---|
| 159 |         /* open connection */
 | 
|---|
| 160 |         if (directfd >= 0) {
 | 
|---|
| 161 |             fp = fdopen (directfd, "r+");
 | 
|---|
| 162 |             setbuf (fp, NULL);          /* don't absorb next guy's stuff */
 | 
|---|
| 163 |             if (!fp) {
 | 
|---|
| 164 |                 fprintf (stderr, "Direct fd %d: %s\n",directfd,strerror(errno));
 | 
|---|
| 165 |                 exit(1);
 | 
|---|
| 166 |             }
 | 
|---|
| 167 |             if (verbose)
 | 
|---|
| 168 |                 fprintf (stderr, "Using direct fd %d\n", directfd);
 | 
|---|
| 169 |         } else {
 | 
|---|
| 170 |             fp = openINDIServer();
 | 
|---|
| 171 |             if (verbose)
 | 
|---|
| 172 |                 fprintf (stderr, "Connected to %s on port %d\n", host, port);
 | 
|---|
| 173 |         }
 | 
|---|
| 174 | 
 | 
|---|
| 175 |         /* build a parser context for cracking XML responses */
 | 
|---|
| 176 |         lillp = newLilXML();
 | 
|---|
| 177 | 
 | 
|---|
| 178 |         /* set up to catch an io timeout function */
 | 
|---|
| 179 |         signal (SIGALRM, onAlarm);
 | 
|---|
| 180 | 
 | 
|---|
| 181 |         /* send getProperties */
 | 
|---|
| 182 |         getProps(fp);
 | 
|---|
| 183 | 
 | 
|---|
| 184 |         /* initialize all properties */
 | 
|---|
| 185 |         initProps(fp);
 | 
|---|
| 186 | 
 | 
|---|
| 187 |         /* evaluate expression, return depending on flags */
 | 
|---|
| 188 |         return (runEval(fp));
 | 
|---|
| 189 | }
 | 
|---|
| 190 | 
 | 
|---|
| 191 | static void
 | 
|---|
| 192 | usage()
 | 
|---|
| 193 | {
 | 
|---|
| 194 |         fprintf (stderr, "Usage: %s [options] [exp]\n", me);
 | 
|---|
| 195 |         fprintf (stderr, "Purpose: evaluate an expression of INDI operands\n");
 | 
|---|
| 196 |         fprintf (stderr, "Version: $Revision: 1.5 $\n");
 | 
|---|
| 197 |         fprintf (stderr, "Options:\n");
 | 
|---|
| 198 |         fprintf (stderr, "   -b   : beep when expression evaluates as true\n");
 | 
|---|
| 199 |         fprintf (stderr, "   -d f : use file descriptor f already open to server\n");
 | 
|---|
| 200 | 
 | 
|---|
| 201 |         fprintf (stderr, "   -e   : print each updated expression value\n");
 | 
|---|
| 202 |         fprintf (stderr, "   -f   : print final expression value\n");
 | 
|---|
| 203 |         fprintf (stderr, "   -h h : alternate host, default is %s\n", host_def);
 | 
|---|
| 204 |         fprintf (stderr, "   -i   : read expression from stdin\n");
 | 
|---|
| 205 |         fprintf (stderr, "   -o   : print operands as they change\n");
 | 
|---|
| 206 |         fprintf (stderr, "   -p p : alternate port, default is %d\n", INDIPORT);
 | 
|---|
| 207 |         fprintf (stderr, "   -t t : max secs to wait, 0 is forever, default is %d\n",TIMEOUT);
 | 
|---|
| 208 |         fprintf (stderr, "   -v   : verbose (cummulative)\n");
 | 
|---|
| 209 |         fprintf (stderr, "   -w   : wait for expression to evaluate as true\n");
 | 
|---|
| 210 |         fprintf (stderr, "[exp] is an arith expression built from the following operators and functons:\n");
 | 
|---|
| 211 |         fprintf (stderr, "     ! + - * / && || > >= == != < <=\n");
 | 
|---|
| 212 |         fprintf (stderr, "     pi sin(rad) cos(rad) tan(rad) asin(x) acos(x) atan(x) atan2(y,x) abs(x)\n");
 | 
|---|
| 213 |         fprintf (stderr, "     degrad(deg) raddeg(rad) floor(x) log(x) log10(x) exp(x) sqrt(x) pow(x,exp)\n");
 | 
|---|
| 214 |         fprintf (stderr, "   operands are of the form \"device.name.element\" (including quotes), where\n");
 | 
|---|
| 215 |         fprintf (stderr, "   element may be:\n");
 | 
|---|
| 216 |         fprintf (stderr, "     _STATE evaluated to 0,1,2,3 from Idle,Ok,Busy,Alert.\n");
 | 
|---|
| 217 |         fprintf (stderr, "     _TS evaluated to UNIX seconds from epoch.\n");
 | 
|---|
| 218 |         fprintf (stderr, "   Switch vectors are evaluated to 0,1 from Off,On.\n");
 | 
|---|
| 219 |         fprintf (stderr, "   Light vectors are evaluated to 0-3 as per _STATE.\n");
 | 
|---|
| 220 |         fprintf (stderr, "Examples:\n");
 | 
|---|
| 221 |         fprintf (stderr, "   To print 0/1 whether Security.Doors.Front or .Rear are in Alert:\n");
 | 
|---|
| 222 |         fprintf (stderr, "     evalINDI -f '\"Security.Doors.Front\"==3 || \"Security.Doors.Rear\"==3'\n");
 | 
|---|
| 223 |         fprintf (stderr, "   To exit 0 if the Security property as a whole is in a state of Ok:\n");
 | 
|---|
| 224 |         fprintf (stderr, "     evalINDI '\"Security.Security._STATE\"==1'\n");
 | 
|---|
| 225 |         fprintf (stderr, "   To wait for RA and Dec to be near zero and watch their values as they change:\n");
 | 
|---|
| 226 |         fprintf (stderr, "     evalINDI -t 0 -wo 'abs(\"Mount.EqJ2K.RA\")<.01 && abs(\"Mount.EqJ2K.Dec\")<.01'\n");
 | 
|---|
| 227 |         fprintf (stderr, "Exit 0 if expression evaluates to non-0, 1 if 0, else 2\n");
 | 
|---|
| 228 | 
 | 
|---|
| 229 |         exit (1);
 | 
|---|
| 230 | }
 | 
|---|
| 231 | 
 | 
|---|
| 232 | /* compile the given expression else read from stdin.
 | 
|---|
| 233 |  * exit(2) if trouble.
 | 
|---|
| 234 |  */
 | 
|---|
| 235 | static void
 | 
|---|
| 236 | compile (char *expr)
 | 
|---|
| 237 | {
 | 
|---|
| 238 |         char errmsg[1024];
 | 
|---|
| 239 |         char *exp = expr;
 | 
|---|
| 240 | 
 | 
|---|
| 241 |         if (!exp) {
 | 
|---|
| 242 |             /* read expression from stdin */
 | 
|---|
| 243 |             int nr, nexp = 0;
 | 
|---|
| 244 |             exp = malloc(1024);
 | 
|---|
| 245 |             while ((nr = fread (exp+nexp, 1, 1024, stdin)) > 0)
 | 
|---|
| 246 |                 exp = realloc (exp, (nexp+=nr)+1024);
 | 
|---|
| 247 |             exp[nexp] = '\0';
 | 
|---|
| 248 |         }
 | 
|---|
| 249 | 
 | 
|---|
| 250 |         if (verbose)
 | 
|---|
| 251 |             fprintf (stderr, "Compiling: %s\n", exp);
 | 
|---|
| 252 |         if (compileExpr (exp, errmsg) < 0) {
 | 
|---|
| 253 |             fprintf (stderr, "Compile err: %s\n", errmsg);
 | 
|---|
| 254 |             exit(2);
 | 
|---|
| 255 |         }
 | 
|---|
| 256 | 
 | 
|---|
| 257 |         if (exp != expr)
 | 
|---|
| 258 |             free (exp);
 | 
|---|
| 259 | }
 | 
|---|
| 260 | 
 | 
|---|
| 261 | /* open a connection to the given host and port or die.
 | 
|---|
| 262 |  * return FILE pointer to socket.
 | 
|---|
| 263 |  */
 | 
|---|
| 264 | static FILE *
 | 
|---|
| 265 | openINDIServer (void)
 | 
|---|
| 266 | {
 | 
|---|
| 267 |         struct sockaddr_in serv_addr;
 | 
|---|
| 268 |         struct hostent *hp;
 | 
|---|
| 269 |         int sockfd;
 | 
|---|
| 270 | 
 | 
|---|
| 271 |         /* lookup host address */
 | 
|---|
| 272 |         hp = gethostbyname (host);
 | 
|---|
| 273 |         if (!hp) {
 | 
|---|
| 274 |             perror ("gethostbyname");
 | 
|---|
| 275 |             exit (2);
 | 
|---|
| 276 |         }
 | 
|---|
| 277 | 
 | 
|---|
| 278 |         /* create a socket to the INDI server */
 | 
|---|
| 279 |         (void) memset ((char *)&serv_addr, 0, sizeof(serv_addr));
 | 
|---|
| 280 |         serv_addr.sin_family = AF_INET;
 | 
|---|
| 281 |         serv_addr.sin_addr.s_addr =
 | 
|---|
| 282 |                             ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
 | 
|---|
| 283 |         serv_addr.sin_port = htons(port);
 | 
|---|
| 284 |         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
 | 
|---|
| 285 |             perror ("socket");
 | 
|---|
| 286 |             exit(2);
 | 
|---|
| 287 |         }
 | 
|---|
| 288 | 
 | 
|---|
| 289 |         /* connect */
 | 
|---|
| 290 |         if (connect (sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0){
 | 
|---|
| 291 |             perror ("connect");
 | 
|---|
| 292 |             exit(2);
 | 
|---|
| 293 |         }
 | 
|---|
| 294 | 
 | 
|---|
| 295 |         /* prepare for line-oriented i/o with client */
 | 
|---|
| 296 |         return (fdopen (sockfd, "r+"));
 | 
|---|
| 297 | }
 | 
|---|
| 298 | 
 | 
|---|
| 299 | /* invite each device referenced in the expression to report its properties.
 | 
|---|
| 300 |  */
 | 
|---|
| 301 | static void
 | 
|---|
| 302 | getProps(FILE *fp)
 | 
|---|
| 303 | {
 | 
|---|
| 304 |         char **ops;
 | 
|---|
| 305 |         int nops;
 | 
|---|
| 306 |         int i, j;
 | 
|---|
| 307 | 
 | 
|---|
| 308 |         /* get each operand used in the expression */
 | 
|---|
| 309 |         nops = getAllOperands (&ops);
 | 
|---|
| 310 | 
 | 
|---|
| 311 |         /* send getProperties for each unique device referenced */
 | 
|---|
| 312 |         for (i = 0; i < nops; i++) {
 | 
|---|
| 313 |             for (j = 0; j < i; j++)
 | 
|---|
| 314 |                 if (devcmp (ops[i], ops[j]) == 0)
 | 
|---|
| 315 |                     break;
 | 
|---|
| 316 |             if (j < i)
 | 
|---|
| 317 |                 continue;
 | 
|---|
| 318 |             if (verbose)
 | 
|---|
| 319 |                 fprintf (stderr, "sending getProperties for %.*s\n",
 | 
|---|
| 320 |                                         strchr (ops[i],'.')-ops[i], ops[i]);
 | 
|---|
| 321 |             fprintf (fp, "<getProperties version='%g' device='%.*s'/>\n", INDIV,
 | 
|---|
| 322 |                                         strchr (ops[i],'.')-ops[i], ops[i]);
 | 
|---|
| 323 |         }
 | 
|---|
| 324 | }
 | 
|---|
| 325 | 
 | 
|---|
| 326 | /* wait for defXXX or setXXX for each property in the expression.
 | 
|---|
| 327 |  * return when find all operands are found or
 | 
|---|
| 328 |  * exit(2) if time out waiting for all known operands.
 | 
|---|
| 329 |  */
 | 
|---|
| 330 | static void
 | 
|---|
| 331 | initProps (FILE *fp)
 | 
|---|
| 332 | {
 | 
|---|
| 333 |         alarm (timeout);
 | 
|---|
| 334 |         while (allOperandsSet() < 0) {
 | 
|---|
| 335 |             if (setOp (nxtEle (fp)) == 0)
 | 
|---|
| 336 |                 alarm(timeout);
 | 
|---|
| 337 |         }
 | 
|---|
| 338 |         alarm (0);
 | 
|---|
| 339 | }
 | 
|---|
| 340 | 
 | 
|---|
| 341 | /* pull apart the name and value from the given message, and set operand value.
 | 
|---|
| 342 |  * ignore any other messages.
 | 
|---|
| 343 |  * return 0 if found a recognized operand else -1
 | 
|---|
| 344 |  */
 | 
|---|
| 345 | static int
 | 
|---|
| 346 | setOp (XMLEle *root)
 | 
|---|
| 347 | {
 | 
|---|
| 348 |         char *t = tagXMLEle (root);
 | 
|---|
| 349 |         char *d = findXMLAttValu (root, "device");
 | 
|---|
| 350 |         char *n = findXMLAttValu (root, "name");
 | 
|---|
| 351 |         int nset = 0;
 | 
|---|
| 352 |         double v;
 | 
|---|
| 353 |         char prop[1024];
 | 
|---|
| 354 |         XMLEle *ep;
 | 
|---|
| 355 | 
 | 
|---|
| 356 |         /* check values */
 | 
|---|
| 357 |         if (!strcmp (t,"defNumberVector") || !strcmp (t,"setNumberVector")) {
 | 
|---|
| 358 |             for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 359 |                 char *et = tagXMLEle(ep);
 | 
|---|
| 360 |                 if (!strcmp (et,"defNumber") || !strcmp (et,"oneNumber")) {
 | 
|---|
| 361 |                     sprintf (prop, "%s.%s.%s", d, n, findXMLAttValu(ep,"name"));
 | 
|---|
| 362 |                     v = atof(pcdataXMLEle(ep));
 | 
|---|
| 363 |                     if (setOperand (prop, v) == 0) {
 | 
|---|
| 364 |                         nset++;
 | 
|---|
| 365 |                         if (oflag)
 | 
|---|
| 366 |                             fprintf (stderr, "%s=%g\n", prop, v);
 | 
|---|
| 367 |                     }
 | 
|---|
| 368 |                 }
 | 
|---|
| 369 |             }
 | 
|---|
| 370 |         } else if(!strcmp(t,"defSwitchVector") || !strcmp(t,"setSwitchVector")){
 | 
|---|
| 371 |             for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 372 |                 char *et = tagXMLEle(ep);
 | 
|---|
| 373 |                 if (!strcmp (et,"defSwitch") || !strcmp (et,"oneSwitch")) {
 | 
|---|
| 374 |                     sprintf (prop, "%s.%s.%s", d, n, findXMLAttValu(ep,"name"));
 | 
|---|
| 375 |                     v = (double)!strcmp(pcdataXMLEle(ep),"On");
 | 
|---|
| 376 |                     if (setOperand (prop, v) == 0) {
 | 
|---|
| 377 |                         nset++;
 | 
|---|
| 378 |                         if (oflag)
 | 
|---|
| 379 |                             fprintf (stderr, "%s=%g\n", prop, v);
 | 
|---|
| 380 |                     }
 | 
|---|
| 381 |                 }
 | 
|---|
| 382 |             }
 | 
|---|
| 383 |         } else if(!strcmp(t,"defLightVector") || !strcmp(t,"setLightVector")){
 | 
|---|
| 384 |             for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 385 |                 char *et = tagXMLEle(ep);
 | 
|---|
| 386 |                 if (!strcmp (et,"defLight") || !strcmp (et,"oneLight")) {
 | 
|---|
| 387 |                     sprintf (prop, "%s.%s.%s", d, n, findXMLAttValu(ep,"name"));
 | 
|---|
| 388 |                     v = (double)pstatestr(pcdataXMLEle(ep));
 | 
|---|
| 389 |                     if (setOperand (prop, v) == 0) {
 | 
|---|
| 390 |                         nset++;
 | 
|---|
| 391 |                         if (oflag)
 | 
|---|
| 392 |                             fprintf (stderr, "%s=%g\n", prop, v);
 | 
|---|
| 393 |                     }
 | 
|---|
| 394 |                 }
 | 
|---|
| 395 |             }
 | 
|---|
| 396 |         }
 | 
|---|
| 397 | 
 | 
|---|
| 398 |         /* check special elements */
 | 
|---|
| 399 |         t = findXMLAttValu (root, "state");
 | 
|---|
| 400 |         if (t[0]) {
 | 
|---|
| 401 |             sprintf (prop, "%s.%s._STATE", d, n);
 | 
|---|
| 402 |             v = (double)pstatestr(t);
 | 
|---|
| 403 |             if (setOperand (prop, v) == 0) {
 | 
|---|
| 404 |                 nset++;
 | 
|---|
| 405 |                 if (oflag)
 | 
|---|
| 406 |                     fprintf (stderr, "%s=%g\n", prop, v);
 | 
|---|
| 407 |             }
 | 
|---|
| 408 |         }
 | 
|---|
| 409 |         t = findXMLAttValu (root, "timestamp");
 | 
|---|
| 410 |         if (t[0]) {
 | 
|---|
| 411 |             sprintf (prop, "%s.%s._TS", d, n);
 | 
|---|
| 412 |             v = (double)timestamp(t);
 | 
|---|
| 413 |             if (setOperand (prop, v) == 0) {
 | 
|---|
| 414 |                 nset++;
 | 
|---|
| 415 |                 if (oflag)
 | 
|---|
| 416 |                     fprintf (stderr, "%s=%g\n", prop, v);
 | 
|---|
| 417 |             }
 | 
|---|
| 418 |         }
 | 
|---|
| 419 | 
 | 
|---|
| 420 |         /* return whether any were set */
 | 
|---|
| 421 |         return (nset > 0 ? 0 : -1);
 | 
|---|
| 422 | }
 | 
|---|
| 423 | 
 | 
|---|
| 424 | /* evaluate the expression after seeing any operand change.
 | 
|---|
| 425 |  * return whether expression evaluated to 0.
 | 
|---|
| 426 |  * exit(2) is trouble or timeout waiting for operands we expect.
 | 
|---|
| 427 |  */
 | 
|---|
| 428 | static int
 | 
|---|
| 429 | runEval (FILE *fp)
 | 
|---|
| 430 | {
 | 
|---|
| 431 |         char errmsg[1024];
 | 
|---|
| 432 |         double v;
 | 
|---|
| 433 | 
 | 
|---|
| 434 |         alarm(timeout);
 | 
|---|
| 435 |         while (1) {
 | 
|---|
| 436 |             if (evalExpr (&v, errmsg) < 0) {
 | 
|---|
| 437 |                 fprintf (stderr, "Eval: %s\n", errmsg);
 | 
|---|
| 438 |                 exit(2);
 | 
|---|
| 439 |             }
 | 
|---|
| 440 |             if (bflag && v)
 | 
|---|
| 441 |                 fprintf (stderr, "\a");
 | 
|---|
| 442 |             if (eflag)
 | 
|---|
| 443 |                 fprintf (stderr, "%g\n", v);
 | 
|---|
| 444 |             if (!wflag || v != 0)
 | 
|---|
| 445 |                 break;
 | 
|---|
| 446 |             while (setOp (nxtEle (fp)) < 0)
 | 
|---|
| 447 |                 continue;
 | 
|---|
| 448 |             alarm(timeout);
 | 
|---|
| 449 |         }
 | 
|---|
| 450 |         alarm(0);
 | 
|---|
| 451 | 
 | 
|---|
| 452 |         if (!eflag && fflag)
 | 
|---|
| 453 |             fprintf (stderr, "%g\n", v);
 | 
|---|
| 454 | 
 | 
|---|
| 455 |         return (v == 0);
 | 
|---|
| 456 | }
 | 
|---|
| 457 | 
 | 
|---|
| 458 | /* return 0|1|2|3 depending on whether state is Idle|Ok|Busy|<other>.
 | 
|---|
| 459 |  */
 | 
|---|
| 460 | static int
 | 
|---|
| 461 | pstatestr (char *state)
 | 
|---|
| 462 | {
 | 
|---|
| 463 |         if (!strcmp (state, "Idle"))
 | 
|---|
| 464 |             return (0);
 | 
|---|
| 465 |         if (!strcmp (state, "Ok"))
 | 
|---|
| 466 |             return (1);
 | 
|---|
| 467 |         if (!strcmp (state, "Busy"))
 | 
|---|
| 468 |             return (2);
 | 
|---|
| 469 |         return (3);
 | 
|---|
| 470 | }
 | 
|---|
| 471 | 
 | 
|---|
| 472 | /* return UNIX time for the given ISO 8601 time string
 | 
|---|
| 473 |  */
 | 
|---|
| 474 | static time_t
 | 
|---|
| 475 | timestamp (char *ts)
 | 
|---|
| 476 | {
 | 
|---|
| 477 |         struct tm tm;
 | 
|---|
| 478 | 
 | 
|---|
| 479 |         if (6 == sscanf (ts, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon,
 | 
|---|
| 480 |                         &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec)) {
 | 
|---|
| 481 |             tm.tm_mon -= 1;             /* want 0..11 */
 | 
|---|
| 482 |             tm.tm_year -= 1900;         /* want years since 1900 */
 | 
|---|
| 483 |             return (mktime (&tm));
 | 
|---|
| 484 |         } else
 | 
|---|
| 485 |             return ((time_t)-1);
 | 
|---|
| 486 | }
 | 
|---|
| 487 | 
 | 
|---|
| 488 | /* return 0 if the device portion of the two given property specs match, else 1
 | 
|---|
| 489 |  */
 | 
|---|
| 490 | static int
 | 
|---|
| 491 | devcmp (char *op1, char *op2)
 | 
|---|
| 492 | {
 | 
|---|
| 493 |         int n1 = strchr(op1,'.') - op1;
 | 
|---|
| 494 |         int n2 = strchr(op2,'.') - op2;
 | 
|---|
| 495 |         return (n1 != n2 || strncmp (op1,op2,n1));
 | 
|---|
| 496 | }
 | 
|---|
| 497 | 
 | 
|---|
| 498 | /* monitor server and return the next complete XML message.
 | 
|---|
| 499 |  * exit(2) if time out.
 | 
|---|
| 500 |  * N.B. caller must call delXMLEle()
 | 
|---|
| 501 |  */
 | 
|---|
| 502 | static XMLEle *
 | 
|---|
| 503 | nxtEle (FILE *fp)
 | 
|---|
| 504 | {
 | 
|---|
| 505 |         char msg[1024];
 | 
|---|
| 506 | 
 | 
|---|
| 507 |         /* read from server, exit if trouble or see malformed XML */
 | 
|---|
| 508 |         while(1) {
 | 
|---|
| 509 |             XMLEle *root = readXMLEle (lillp, readServerChar(fp), msg);
 | 
|---|
| 510 |             if (root) {
 | 
|---|
| 511 |                 /* found a complete XML element */
 | 
|---|
| 512 |                 if (verbose > 1)
 | 
|---|
| 513 |                     prXMLEle (stderr, root, 0);
 | 
|---|
| 514 |                 return (root);
 | 
|---|
| 515 |             } else if (msg[0]) {
 | 
|---|
| 516 |                 fprintf (stderr, "Bad XML from %s/%d: %s\n", host, port, msg);
 | 
|---|
| 517 |                 exit(2);
 | 
|---|
| 518 |             }
 | 
|---|
| 519 |         }
 | 
|---|
| 520 | }
 | 
|---|
| 521 | 
 | 
|---|
| 522 | /* read next char from the INDI server connected by fp */
 | 
|---|
| 523 | static int
 | 
|---|
| 524 | readServerChar (FILE *fp)
 | 
|---|
| 525 | {
 | 
|---|
| 526 |         int c = fgetc (fp);
 | 
|---|
| 527 | 
 | 
|---|
| 528 |         if (c == EOF) {
 | 
|---|
| 529 |             if (ferror(fp))
 | 
|---|
| 530 |                 perror ("read");
 | 
|---|
| 531 |             else
 | 
|---|
| 532 |                 fprintf (stderr,"INDI server %s/%d disconnected\n", host, port);
 | 
|---|
| 533 |             exit (2);
 | 
|---|
| 534 |         }
 | 
|---|
| 535 | 
 | 
|---|
| 536 |         if (verbose > 2)
 | 
|---|
| 537 |             fprintf (stderr, "Read %c\n", c);
 | 
|---|
| 538 | 
 | 
|---|
| 539 |         return (c);
 | 
|---|
| 540 | }
 | 
|---|
| 541 | 
 | 
|---|
| 542 | /* called after timeout seconds waiting to hear from server.
 | 
|---|
| 543 |  * print reason for trouble and exit(2).
 | 
|---|
| 544 |  */
 | 
|---|
| 545 | static void
 | 
|---|
| 546 | onAlarm (int dummy)
 | 
|---|
| 547 | {
 | 
|---|
| 548 |         char **ops;
 | 
|---|
| 549 |         int nops;
 | 
|---|
| 550 | 
 | 
|---|
| 551 |         /* report any unseen operands if any, else just say timed out */
 | 
|---|
| 552 |         if ((nops = getUnsetOperands (&ops)) > 0) {
 | 
|---|
| 553 |             fprintf (stderr, "No values seen for");
 | 
|---|
| 554 |             while (nops-- > 0)
 | 
|---|
| 555 |                 fprintf (stderr, " %s", ops[nops]);
 | 
|---|
| 556 |             fprintf (stderr, "\n");
 | 
|---|
| 557 |         } else 
 | 
|---|
| 558 |             fprintf (stderr, "Timed out waiting for new values\n");
 | 
|---|
| 559 | 
 | 
|---|
| 560 |         exit (2);
 | 
|---|
| 561 | } | 
|---|