Instrument Neutral Distributed Interface INDI  0.9.6
indidriver.c
1 #if 0
2  INDI
3  Copyright (C) 2003-2006 Elwood C. Downey
4 
5  Updated by Jasem Mutlaq (2003-2010)
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public
9  License as published by the Free Software Foundation; either
10  version 2.1 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21 #endif
22 
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 
34 #include "lilxml.h"
35 #include "base64.h"
36 #include "eventloop.h"
37 #include "indidevapi.h"
38 #include "indicom.h"
39 #include "indidriver.h"
40 
41 #define MAXRBUF 2048
42 
43 /* tell Client to delete the property with given name on given device, or
44  * entire device if !name
45  */
46 void
47 IDDelete (const char *dev, const char *name, const char *fmt, ...)
48 {
49  xmlv1();
50  printf ("<delProperty\n device='%s'\n", dev);
51  if (name)
52  printf (" name='%s'\n", name);
53  printf (" timestamp='%s'\n", timestamp());
54  if (fmt) {
55  va_list ap;
56  va_start (ap, fmt);
57  printf (" message='");
58  vprintf (fmt, ap);
59  printf ("'\n");
60  va_end (ap);
61  }
62  printf ("/>\n");
63  fflush (stdout);
64 }
65 
66 /* tell indiserver we want to snoop on the given device/property.
67  * name ignored if NULL or empty.
68  */
69 void
70 IDSnoopDevice (const char *snooped_device_name, const char *snooped_property_name)
71 {
72  xmlv1();
73  if (snooped_property_name && snooped_property_name[0])
74  printf ("<getProperties device='%s' name='%s'/>\n",
75  snooped_device_name, snooped_property_name);
76  else
77  printf ("<getProperties device='%s'/>\n", snooped_device_name);
78  fflush (stdout);
79 }
80 
81 /* tell indiserver whether we want BLOBs from the given snooped device.
82  * silently ignored if given device is not already registered for snooping.
83  */
84 void
85 IDSnoopBLOBs (const char *snooped_device, BLOBHandling bh)
86 {
87  const char *how;
88 
89  switch (bh) {
90  case B_NEVER: how = "Never"; break;
91  case B_ALSO: how = "Also"; break;
92  case B_ONLY: how = "Only"; break;
93  default: return;
94  }
95 
96  xmlv1();
97  printf ("<enableBLOB device='%s'>%s</enableBLOB>\n",
98  snooped_device, how);
99  fflush (stdout);
100 }
101 
102 /* "INDI" wrappers to the more generic eventloop facility. */
103 
104 int
105 IEAddCallback (int readfiledes, IE_CBF *fp, void *p)
106 {
107  return (addCallback (readfiledes, (CBF*)fp, p));
108 }
109 
110 void
111 IERmCallback (int callbackid)
112 {
113  rmCallback (callbackid);
114 }
115 
116 int
117 IEAddTimer (int millisecs, IE_TCF *fp, void *p)
118 {
119  return (addTimer (millisecs, (TCF*)fp, p));
120 }
121 
122 void
123 IERmTimer (int timerid)
124 {
125  rmTimer (timerid);
126 }
127 
128 int
129 IEAddWorkProc (IE_WPF *fp, void *p)
130 {
131  return (addWorkProc ((WPF*)fp, p));
132 }
133 
134 void
135 IERmWorkProc (int workprocid)
136 {
137  rmWorkProc (workprocid);
138 }
139 
140 
141 int
142 IEDeferLoop (int maxms, int *flagp)
143 {
144  return (deferLoop (maxms, flagp));
145 }
146 
147 int
148 IEDeferLoop0 (int maxms, int *flagp)
149 {
150  return (deferLoop0 (maxms, flagp));
151 }
152 
153 /* Update property switches in accord with states and names. */
154 int
155 IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
156 {
157  int i=0;
158  ISwitch *sp;
159  char sn[MAXINDINAME];
160 
161  /* store On switch name */
162  if (svp->r == ISR_1OFMANY)
163  {
164  sp = IUFindOnSwitch(svp);
165  if (sp) strncpy(sn, sp->name, MAXINDINAME);
166 
167  IUResetSwitch(svp);
168  }
169 
170  for (i = 0; i < n ; i++)
171  {
172  sp = IUFindSwitch(svp, names[i]);
173 
174  if (!sp)
175  {
176  svp->s = IPS_IDLE;
177  IDSetSwitch(svp, "Error: %s is not a member of %s property.", names[i], svp->name);
178  return -1;
179  }
180 
181  sp->s = states[i];
182  }
183 
184  /* Consistency checks for ISR_1OFMANY after update. */
185  if (svp->r == ISR_1OFMANY)
186  {
187  int t_count=0;
188  for (i=0; i < svp->nsp; i++)
189  {
190  if (svp->sp[i].s == ISS_ON)
191  t_count++;
192  }
193  if (t_count != 1)
194  {
195  IUResetSwitch(svp);
196  sp = IUFindSwitch(svp, sn);
197  if (sp) sp->s = ISS_ON;
198  svp->s = IPS_IDLE;
199  IDSetSwitch(svp, "Error: invalid state switch for property %s. %s.", svp->name, t_count == 0 ? "No switch is on" : "Too many switches are on");
200  return -1;
201  }
202  }
203 
204  return 0;
205 
206 }
207 
208 /* Update property numbers in accord with values and names */
209 int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
210 {
211  int i=0;
212 
213  INumber *np;
214 
215  for (i = 0; i < n; i++)
216  {
217  np = IUFindNumber(nvp, names[i]);
218  if (!np)
219  {
220  nvp->s = IPS_IDLE;
221  IDSetNumber(nvp, "Error: %s is not a member of %s property.", names[i], nvp->name);
222  return -1;
223  }
224 
225  if (values[i] < np->min || values[i] > np->max)
226  {
227  nvp->s = IPS_IDLE;
228  IDSetNumber(nvp, "Error: Invalid range. Valid range is from %g to %g", np->min, np->max);
229  return -1;
230  }
231 
232  }
233 
234  /* First loop checks for error, second loop set all values atomically*/
235  for (i=0; i < n; i++)
236  {
237  np = IUFindNumber(nvp, names[i]);
238  np->value = values[i];
239  }
240 
241  return 0;
242 
243 }
244 
245 /* Update property text in accord with texts and names */
246 int IUUpdateText(ITextVectorProperty *tvp, char * texts[], char *names[], int n)
247 {
248  int i=0;
249 
250  IText *tp;
251 
252  for (i = 0; i < n; i++)
253  {
254  tp = IUFindText(tvp, names[i]);
255  if (!tp)
256  {
257  tvp->s = IPS_IDLE;
258  IDSetText(tvp, "Error: %s is not a member of %s property.", names[i], tvp->name);
259  return -1;
260  }
261  }
262 
263  /* First loop checks for error, second loop set all values atomically*/
264  for (i=0; i < n; i++)
265  {
266  tp = IUFindText(tvp, names[i]);
267  IUSaveText(tp, texts[i]);
268  }
269 
270  return 0;
271 
272 }
273 
274 
275 /* Update property BLOB in accord with BLOBs and names */
276 int IUUpdateBLOB(IBLOBVectorProperty *bvp, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
277 {
278  int i=0;
279 
280  IBLOB *bp;
281 
282  for (i = 0; i < n; i++)
283  {
284  bp = IUFindBLOB(bvp, names[i]);
285  if (!bp)
286  {
287  bvp->s = IPS_IDLE;
288  IDSetBLOB(bvp, "Error: %s is not a member of %s property.", names[i], bvp->name);
289  return -1;
290  }
291  }
292 
293  /* First loop checks for error, second loop set all values atomically*/
294  for (i=0; i < n; i++)
295  {
296  bp = IUFindBLOB(bvp, names[i]);
297  IUSaveBLOB(bp, sizes[i], blobsizes[i], blobs[i], formats[i]);
298  }
299 
300  return 0;
301 
302 }
303 
304 int IUSaveBLOB(IBLOB *bp, int size, int blobsize, char *blob, char *format)
305 {
306  bp->bloblen = blobsize;
307  bp->size = size;
308  bp->blob = blob;
309  strncpy(bp->format, format, MAXINDIFORMAT);
310  return 0;
311 }
312 
313 void IUFillSwitch(ISwitch *sp, const char *name, const char * label, ISState s)
314 {
315  strncpy(sp->name, name, MAXINDINAME);
316  strncpy(sp->label, label, MAXINDILABEL);
317  sp->s = s;
318  sp->svp = NULL;
319  sp->aux = NULL;
320 }
321 
322 void IUFillLight(ILight *lp, const char *name, const char * label, IPState s)
323 {
324  strncpy(lp->name, name, MAXINDINAME);
325  strncpy(lp->label, label, MAXINDILABEL);
326  lp->s = s;
327  lp->lvp = NULL;
328  lp->aux = NULL;
329 }
330 
331 
332 void IUFillNumber(INumber *np, const char *name, const char * label, const char *format, double min, double max, double step, double value)
333 {
334 
335  strncpy(np->name, name, MAXINDINAME);
336  strncpy(np->label, label, MAXINDILABEL);
337  strncpy(np->format, format, MAXINDIFORMAT);
338 
339  np->min = min;
340  np->max = max;
341  np->step = step;
342  np->value = value;
343  np->nvp = NULL;
344  np->aux0 = NULL;
345  np->aux1 = NULL;
346 }
347 
348 void IUFillText(IText *tp, const char *name, const char * label, const char *initialText)
349 {
350 
351  strncpy(tp->name, name, MAXINDINAME);
352  strncpy(tp->label, label, MAXINDILABEL);
353  tp->text = NULL;
354  tp->tvp = NULL;
355  tp->aux0 = NULL;
356  tp->aux1 = NULL;
357 
358  IUSaveText(tp, initialText);
359 
360 }
361 
362 void IUFillBLOB(IBLOB *bp, const char *name, const char * label, const char *format)
363 {
364  memset(bp, 0, sizeof(IBLOB));
365  strncpy(bp->name, name, MAXINDINAME);
366  strncpy(bp->label, label, MAXINDILABEL);
367  strncpy(bp->format, label, MAXINDIBLOBFMT);
368  bp->blob = 0;
369  bp->bloblen = 0;
370  bp->size = 0;
371  bp->bvp = 0;
372  bp->aux0 = 0;
373  bp->aux1 = 0;
374  bp->aux2 = 0;
375 }
376 
377 void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char * dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
378 {
379  strncpy(svp->device, dev, MAXINDIDEVICE);
380  strncpy(svp->name, name, MAXINDINAME);
381  strncpy(svp->label, label, MAXINDILABEL);
382  strncpy(svp->group, group, MAXINDIGROUP);
383 
384  svp->p = p;
385  svp->r = r;
386  svp->timeout = timeout;
387  svp->s = s;
388  svp->sp = sp;
389  svp->nsp = nsp;
390 }
391 
392 void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char * dev, const char *name, const char *label, const char *group, IPState s)
393 {
394  strncpy(lvp->device, dev, MAXINDIDEVICE);
395  strncpy(lvp->name, name, MAXINDINAME);
396  strncpy(lvp->label, label, MAXINDILABEL);
397  strncpy(lvp->group, group, MAXINDIGROUP);
398  strcpy(lvp->timestamp, "");
399 
400  lvp->s = s;
401  lvp->lp = lp;
402  lvp->nlp = nlp;
403 }
404 
405 void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char * dev, const char *name, const char *label, const char* group, IPerm p, double timeout, IPState s)
406 {
407 
408  strncpy(nvp->device, dev, MAXINDIDEVICE);
409  strncpy(nvp->name, name, MAXINDINAME);
410  strncpy(nvp->label, label, MAXINDILABEL);
411  strncpy(nvp->group, group, MAXINDIGROUP);
412  strcpy(nvp->timestamp, "");
413 
414  nvp->p = p;
415  nvp->timeout = timeout;
416  nvp->s = s;
417  nvp->np = np;
418  nvp->nnp = nnp;
419 
420 }
421 
422 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)
423 {
424  strncpy(tvp->device, dev, MAXINDIDEVICE);
425  strncpy(tvp->name, name, MAXINDINAME);
426  strncpy(tvp->label, label, MAXINDILABEL);
427  strncpy(tvp->group, group, MAXINDIGROUP);
428  strcpy(tvp->timestamp, "");
429 
430  tvp->p = p;
431  tvp->timeout = timeout;
432  tvp->s = s;
433  tvp->tp = tp;
434  tvp->ntp = ntp;
435 
436 }
437 
438 void IUFillBLOBVector(IBLOBVectorProperty *bvp, IBLOB *bp, int nbp, const char * dev, const char *name, const char *label, const char* group, IPerm p, double timeout, IPState s)
439 {
440  memset(bvp, 0, sizeof(IBLOBVectorProperty));
441  strncpy(bvp->device, dev, MAXINDIDEVICE);
442  strncpy(bvp->name, name, MAXINDINAME);
443  strncpy(bvp->label, label, MAXINDILABEL);
444  strncpy(bvp->group, group, MAXINDIGROUP);
445  strcpy(bvp->timestamp, "");
446 
447  bvp->p = p;
448  bvp->timeout = timeout;
449  bvp->s = s;
450  bvp->bp = bp;
451  bvp->nbp = nbp;
452 }
453 
454 /*****************************************************************************
455  * convenience functions for use in your implementation of ISSnoopDevice().
456  */
457 
458 /* crack the snooped driver setNumberVector or defNumberVector message into
459  * the given INumberVectorProperty.
460  * return 0 if type, device and name match and all members are present, else
461  * return -1
462  */
463 int
465 {
466  char *dev, *name;
467  XMLEle *ep;
468  int i;
469 
470  /* check and crack type, device, name and state */
471  if (strcmp (tagXMLEle(root)+3, "NumberVector") ||
472  crackDN (root, &dev, &name, NULL) < 0)
473  return (-1);
474  if (strcmp (dev, nvp->device) || strcmp (name, nvp->name))
475  return (-1); /* not this property */
476  (void) crackIPState (findXMLAttValu (root,"state"), &nvp->s);
477 
478  /* match each INumber with a oneNumber */
479  for (i = 0; i < nvp->nnp; i++) {
480  for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
481  if (!strcmp (tagXMLEle(ep)+3, "Number") &&
482  !strcmp (nvp->np[i].name, findXMLAttValu(ep, "name"))) {
483  if (f_scansexa (pcdataXMLEle(ep), &nvp->np[i].value) < 0)
484  return (-1); /* bad number format */
485  break;
486  }
487  }
488  if (!ep)
489  return (-1); /* element not found */
490  }
491 
492  /* ok */
493  return (0);
494 }
495 
496 /* crack the snooped driver setTextVector or defTextVector message into
497  * the given ITextVectorProperty.
498  * return 0 if type, device and name match and all members are present, else
499  * return -1
500  */
501 int
502 IUSnoopText (XMLEle *root, ITextVectorProperty *tvp)
503 {
504  char *dev, *name;
505  XMLEle *ep;
506  int i;
507 
508  /* check and crack type, device, name and state */
509  if (strcmp (tagXMLEle(root)+3, "TextVector") ||
510  crackDN (root, &dev, &name, NULL) < 0)
511  return (-1);
512  if (strcmp (dev, tvp->device) || strcmp (name, tvp->name))
513  return (-1); /* not this property */
514  (void) crackIPState (findXMLAttValu (root,"state"), &tvp->s);
515 
516  /* match each IText with a oneText */
517  for (i = 0; i < tvp->ntp; i++) {
518  for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
519  if (!strcmp (tagXMLEle(ep)+3, "Text") &&
520  !strcmp (tvp->tp[i].name, findXMLAttValu(ep, "name"))) {
521  IUSaveText (&tvp->tp[i], pcdataXMLEle(ep));
522  break;
523  }
524  }
525  if (!ep)
526  return (-1); /* element not found */
527  }
528 
529  /* ok */
530  return (0);
531 }
532 
533 /* crack the snooped driver setLightVector or defLightVector message into
534  * the given ILightVectorProperty. it is not necessary that all ILight names
535  * be found.
536  * return 0 if type, device and name match, else return -1.
537  */
538 int
539 IUSnoopLight (XMLEle *root, ILightVectorProperty *lvp)
540 {
541  char *dev, *name;
542  XMLEle *ep;
543  int i;
544 
545  /* check and crack type, device, name and state */
546  if (strcmp (tagXMLEle(root)+3, "LightVector") ||
547  crackDN (root, &dev, &name, NULL) < 0)
548  return (-1);
549  if (strcmp (dev, lvp->device) || strcmp (name, lvp->name))
550  return (-1); /* not this property */
551 
552  (void) crackIPState (findXMLAttValu (root,"state"), &lvp->s);
553 
554  /* match each oneLight with one ILight */
555  for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
556  if (!strcmp (tagXMLEle(ep)+3, "Light")) {
557  const char *name = findXMLAttValu (ep, "name");
558  for (i = 0; i < lvp->nlp; i++) {
559  if (!strcmp (lvp->lp[i].name, name)) {
560  if (crackIPState(pcdataXMLEle(ep), &lvp->lp[i].s) < 0) {
561  return (-1); /* unrecognized state */
562  }
563  break;
564  }
565  }
566  }
567  }
568 
569  /* ok */
570  return (0);
571 }
572 
573 /* crack the snooped driver setSwitchVector or defSwitchVector message into the
574  * given ISwitchVectorProperty. it is not necessary that all ISwitch names be
575  * found.
576  * return 0 if type, device and name match, else return -1.
577  */
578 int
580 {
581  char *dev, *name;
582  XMLEle *ep;
583  int i;
584 
585  /* check and crack type, device, name and state */
586  if (strcmp (tagXMLEle(root)+3, "SwitchVector") ||
587  crackDN (root, &dev, &name, NULL) < 0)
588  return (-1);
589  if (strcmp (dev, svp->device) || strcmp (name, svp->name))
590  return (-1); /* not this property */
591  (void) crackIPState (findXMLAttValu (root,"state"), &svp->s);
592 
593  /* match each oneSwitch with one ISwitch */
594  for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
595  if (!strcmp (tagXMLEle(ep)+3, "Switch")) {
596  const char *name = findXMLAttValu (ep, "name");
597  for (i = 0; i < svp->nsp; i++) {
598  if (!strcmp (svp->sp[i].name, name)) {
599  if (crackISState(pcdataXMLEle(ep), &svp->sp[i].s) < 0) {
600  return (-1); /* unrecognized state */
601  }
602  break;
603  }
604  }
605  }
606  }
607 
608  /* ok */
609  return (0);
610 }
611 
612 /* crack the snooped driver setBLOBVector message into the given
613  * IBLOBVectorProperty. it is not necessary that all IBLOB names be found.
614  * return 0 if type, device and name match, else return -1.
615  * N.B. we assume any existing blob in bvp has been malloced, which we free
616  * and replace with a newly malloced blob if found.
617  */
618 int
619 IUSnoopBLOB (XMLEle *root, IBLOBVectorProperty *bvp)
620 {
621  char *dev, *name;
622  XMLEle *ep;
623  int i;
624 
625  /* check and crack type, device, name and state */
626  if (strcmp (tagXMLEle(root), "setBLOBVector") ||
627  crackDN (root, &dev, &name, NULL) < 0)
628  return (-1);
629  if (strcmp (dev, bvp->device) || strcmp (name, bvp->name))
630  return (-1); /* not this property */
631  (void) crackIPState (findXMLAttValu (root,"state"), &bvp->s);
632 
633  /* match each oneBLOB with one IBLOB */
634  for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
635  if (!strcmp (tagXMLEle(ep)+3, "BLOB")) {
636  const char *name = findXMLAttValu (ep, "name");
637  for (i = 0; i < bvp->nbp; i++) {
638  IBLOB *bp = &bvp->bp[i];
639  if (!strcmp (bp->name, name)) {
640  strcpy (bp->format, findXMLAttValu (ep,"format"));
641  bp->size = atof (findXMLAttValu (ep,"size"));
642  bp->bloblen = pcdatalenXMLEle(ep)+1;
643  if (bp->blob)
644  free (bp->blob);
645  bp->blob = strcpy(malloc(bp->bloblen),pcdataXMLEle(ep));
646  break;
647  }
648  }
649  }
650  }
651 
652  /* ok */
653  return (0);
654 }
655 
656 /* callback when INDI client message arrives on stdin.
657  * collect and dispatch when see outter element closure.
658  * exit if OS trouble or see incompatable INDI version.
659  * arg is not used.
660  */
661 void
662 clientMsgCB (int fd, void *arg)
663 {
664  char buf[1024], msg[1024], *bp;
665  int nr;
666  arg=arg;
667 
668  /* one read */
669  nr = read (fd, buf, sizeof(buf));
670  if (nr < 0) {
671  fprintf (stderr, "%s: %s\n", me, strerror(errno));
672  exit(1);
673  }
674  if (nr == 0) {
675  fprintf (stderr, "%s: EOF\n", me);
676  exit(1);
677  }
678 
679  /* crack and dispatch when complete */
680  for (bp = buf; nr-- > 0; bp++) {
681  XMLEle *root = readXMLEle (clixml, *bp, msg);
682  if (root) {
683  if (dispatch (root, msg) < 0)
684  fprintf (stderr, "%s dispatch error: %s\n", me, msg);
685  delXMLEle (root);
686  } else if (msg[0])
687  fprintf (stderr, "%s XML error: %s\n", me, msg);
688  }
689 
690 }
691 
692 /* crack the given INDI XML element and call driver's IS* entry points as they
693  * are recognized.
694  * return 0 if ok else -1 with reason in msg[].
695  * N.B. exit if getProperties does not proclaim a compatible version.
696  */
697 int
698 dispatch (XMLEle *root, char msg[])
699 {
700  char *rtag = tagXMLEle(root);
701  XMLEle *ep;
702  int n,i=0;
703 
704  if (verbose)
705  prXMLEle (stderr, root, 0);
706 
707  /* check tag in surmised decreasing order of likelyhood */
708 
709  if (!strcmp (rtag, "newNumberVector")) {
710  static double *doubles;
711  static char **names;
712  static int maxn;
713  char *dev, *name;
714 
715  /* pull out device and name */
716  if (crackDN (root, &dev, &name, msg) < 0)
717  return (-1);
718 
719  /* seed for reallocs */
720  if (!doubles) {
721  doubles = (double *) malloc (1);
722  names = (char **) malloc (1);
723  }
724 
725  /* pull out each name/value pair */
726  for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
727  if (strcmp (tagXMLEle(ep), "oneNumber") == 0) {
728  XMLAtt *na = findXMLAtt (ep, "name");
729  if (na) {
730  if (n >= maxn) {
731  /* grow for this and another */
732  int newsz = (maxn=n+1)*sizeof(double);
733  doubles = (double *) realloc(doubles,newsz);
734  newsz = maxn*sizeof(char *);
735  names = (char **) realloc (names, newsz);
736  }
737  if (f_scansexa (pcdataXMLEle(ep), &doubles[n]) < 0)
738  IDMessage (dev,"%s: Bad format %s", name,
739  pcdataXMLEle(ep));
740  else
741  names[n++] = valuXMLAtt(na);
742  }
743  }
744  }
745 
746  /* insure property is not RO */
747  for (i=0; i < nroCheck; i++)
748  {
749  if (!strcmp(roCheck[i].propName, name))
750  {
751  if (roCheck[i].perm == IP_RO)
752  return -1;
753  }
754  }
755 
756  /* invoke driver if something to do, but not an error if not */
757  if (n > 0)
758  ISNewNumber (dev, name, doubles, names, n);
759  else
760  IDMessage(dev,"%s: newNumberVector with no valid members",name);
761  return (0);
762  }
763 
764  if (!strcmp (rtag, "newSwitchVector")) {
765  static ISState *states;
766  static char **names;
767  static int maxn;
768  char *dev, *name;
769  XMLEle *ep;
770 
771  /* pull out device and name */
772  if (crackDN (root, &dev, &name, msg) < 0)
773  return (-1);
774 
775  /* seed for reallocs */
776  if (!states) {
777  states = (ISState *) malloc (1);
778  names = (char **) malloc (1);
779  }
780 
781  /* pull out each name/state pair */
782  for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
783  if (strcmp (tagXMLEle(ep), "oneSwitch") == 0) {
784  XMLAtt *na = findXMLAtt (ep, "name");
785  if (na) {
786  if (n >= maxn) {
787  int newsz = (maxn=n+1)*sizeof(ISState);
788  states = (ISState *) realloc(states, newsz);
789  newsz = maxn*sizeof(char *);
790  names = (char **) realloc (names, newsz);
791  }
792  if (strcmp (pcdataXMLEle(ep),"On") == 0) {
793  states[n] = ISS_ON;
794  names[n] = valuXMLAtt(na);
795  n++;
796  } else if (strcmp (pcdataXMLEle(ep),"Off") == 0) {
797  states[n] = ISS_OFF;
798  names[n] = valuXMLAtt(na);
799  n++;
800  } else
801  IDMessage (dev, "%s: must be On or Off: %s", name,
802  pcdataXMLEle(ep));
803  }
804  }
805  }
806 
807  /* insure property is not RO */
808  for (i=0; i < nroCheck; i++)
809  {
810  if (!strcmp(roCheck[i].propName, name))
811  {
812  if (roCheck[i].perm == IP_RO)
813  return -1;
814  }
815  }
816 
817  /* invoke driver if something to do, but not an error if not */
818  if (n > 0)
819  ISNewSwitch (dev, name, states, names, n);
820  else
821  IDMessage(dev,"%s: newSwitchVector with no valid members",name);
822  return (0);
823  }
824 
825  if (!strcmp (rtag, "newTextVector")) {
826  static char **texts;
827  static char **names;
828  static int maxn;
829  char *dev, *name;
830 
831  /* pull out device and name */
832  if (crackDN (root, &dev, &name, msg) < 0)
833  return (-1);
834 
835  /* seed for reallocs */
836  if (!texts) {
837  texts = (char **) malloc (1);
838  names = (char **) malloc (1);
839  }
840 
841  /* pull out each name/text pair */
842  for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
843  if (strcmp (tagXMLEle(ep), "oneText") == 0) {
844  XMLAtt *na = findXMLAtt (ep, "name");
845  if (na) {
846  if (n >= maxn) {
847  int newsz = (maxn=n+1)*sizeof(char *);
848  texts = (char **) realloc (texts, newsz);
849  names = (char **) realloc (names, newsz);
850  }
851  texts[n] = pcdataXMLEle(ep);
852  names[n] = valuXMLAtt(na);
853  n++;
854  }
855  }
856  }
857 
858  /* insure property is not RO */
859  for (i=0; i < nroCheck; i++)
860  {
861  if (!strcmp(roCheck[i].propName, name))
862  {
863  if (roCheck[i].perm == IP_RO)
864  return -1;
865  }
866  }
867 
868  /* invoke driver if something to do, but not an error if not */
869  if (n > 0)
870  ISNewText (dev, name, texts, names, n);
871  else
872  IDMessage (dev, "%s: set with no valid members", name);
873  return (0);
874  }
875 
876  if (!strcmp (rtag, "newBLOBVector")) {
877  static char **blobs;
878  static char **names;
879  static char **formats;
880  static int *blobsizes;
881  static int *sizes;
882  static int maxn;
883  char *dev, *name;
884  int i;
885 
886  /* pull out device and name */
887  if (crackDN (root, &dev, &name, msg) < 0)
888  return (-1);
889 
890  /* seed for reallocs */
891  if (!blobs) {
892  blobs = (char **) malloc (1);
893  names = (char **) malloc (1);
894  formats = (char **) malloc (1);
895  blobsizes = (int *) malloc (1);
896  sizes = (int *) malloc (1);
897  }
898 
899  /* pull out each name/BLOB pair, decode */
900  for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
901  if (strcmp (tagXMLEle(ep), "oneBLOB") == 0) {
902  XMLAtt *na = findXMLAtt (ep, "name");
903  XMLAtt *fa = findXMLAtt (ep, "format");
904  XMLAtt *sa = findXMLAtt (ep, "size");
905  if (na && fa && sa) {
906  if (n >= maxn) {
907  int newsz = (maxn=n+1)*sizeof(char *);
908  blobs = (char **) realloc (blobs, newsz);
909  names = (char **) realloc (names, newsz);
910  formats = (char **) realloc(formats,newsz);
911  newsz = maxn*sizeof(int);
912  sizes = (int *) realloc(sizes,newsz);
913  blobsizes = (int *) realloc(blobsizes,newsz);
914  }
915  blobs[n] = malloc (3*pcdatalenXMLEle(ep)/4);
916  blobsizes[n] = from64tobits(blobs[n], pcdataXMLEle(ep));
917  names[n] = valuXMLAtt(na);
918  formats[n] = valuXMLAtt(fa);
919  sizes[n] = atoi(valuXMLAtt(sa));
920  n++;
921  }
922  }
923  }
924 
925  /* invoke driver if something to do, but not an error if not */
926  if (n > 0) {
927  ISNewBLOB (dev, name, sizes, blobsizes, blobs, formats,names,n);
928  for (i = 0; i < n; i++)
929  free (blobs[i]);
930  } else
931  IDMessage (dev, "%s: newBLOBVector with no valid members",name);
932  return (0);
933  }
934 
935  if (!strcmp (rtag, "getProperties")) {
936  XMLAtt *ap;
937  double v;
938 
939  /* check version */
940  ap = findXMLAtt (root, "version");
941  if (!ap) {
942  fprintf (stderr, "%s: getProperties missing version\n", me);
943  exit(1);
944  }
945  v = atof (valuXMLAtt(ap));
946  if (v > INDIV) {
947  fprintf (stderr, "%s: client version %g > %g\n", me, v, INDIV);
948  exit(1);
949  }
950 
951  /* ok */
952  ap = findXMLAtt (root, "device");
953  ISGetProperties (ap ? valuXMLAtt(ap) : NULL);
954  return (0);
955  }
956 
957  /* other commands might be from a snooped device.
958  * we don't know here which devices are being snooped so we send
959  * all remaining valid messages
960  */
961  if ( !strcmp (rtag, "setNumberVector") ||
962  !strcmp (rtag, "setTextVector") ||
963  !strcmp (rtag, "setLightVector") ||
964  !strcmp (rtag, "setSwitchVector") ||
965  !strcmp (rtag, "setBLOBVector") ||
966  !strcmp (rtag, "defNumberVector") ||
967  !strcmp (rtag, "defTextVector") ||
968  !strcmp (rtag, "defLightVector") ||
969  !strcmp (rtag, "defSwitchVector") ||
970  !strcmp (rtag, "defBLOBVector") ||
971  !strcmp (rtag, "message") ||
972  !strcmp (rtag, "delProperty")) {
973  ISSnoopDevice (root);
974  return (0);
975  }
976 
977  sprintf (msg, "Unknown command: %s", rtag);
978  return(1);
979 }
980 
981 int IUReadConfig(const char *filename, const char *dev, char errmsg[])
982 {
983  char configFileName[MAXRBUF], configDefaultFileName[MAXRBUF];
984  char *rname, *rdev;
985  XMLEle *root = NULL, *fproot = NULL;
986  LilXML *lp = newLilXML();
987 
988  FILE *fp = NULL;
989 
990  if (filename)
991  strncpy(configFileName, filename, MAXRBUF);
992  else
993  {
994  if (getenv("INDICONFIG"))
995  strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
996  else
997  snprintf(configFileName, MAXRBUF, "%s/.indi/%s_config.xml", getenv("HOME"), dev);
998 
999  }
1000 
1001  fp = fopen(configFileName, "r");
1002  if (fp == NULL)
1003  {
1004  snprintf(errmsg, MAXRBUF, "Unable to read config file. Error loading file %s: %s\n", filename, strerror(errno));
1005  return -1;
1006  }
1007 
1008  fproot = readXMLFile(fp, lp, errmsg);
1009 
1010  if (fproot == NULL)
1011  {
1012  snprintf(errmsg, MAXRBUF, "Unable to parse config XML: %s", errmsg);
1013  return -1;
1014  }
1015 
1016  for (root = nextXMLEle (fproot, 1); root != NULL; root = nextXMLEle (fproot, 0))
1017  {
1018 
1019  /* pull out device and name */
1020  if (crackDN (root, &rdev, &rname, errmsg) < 0)
1021  return -1;
1022 
1023  // It doesn't belong to our device??
1024  if (strcmp(dev, rdev))
1025  continue;
1026 
1027  dispatch(root, errmsg);
1028 
1029  }
1030 
1031  fclose(fp);
1032  delXMLEle(fproot);
1033  delXMLEle(root);
1034  delLilXML(lp);
1035 
1036  return (0);
1037 
1038 }
1039 
1040 
1041 void IUSaveDefaultConfig(const char *source_config, const char *dest_config, const char *dev)
1042 {
1043 
1044  char configFileName[MAXRBUF], configDefaultFileName[MAXRBUF];
1045 
1046  if (source_config)
1047  strncpy(configFileName, source_config, MAXRBUF);
1048  else
1049  {
1050  if (getenv("INDICONFIG"))
1051  strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1052  else
1053  snprintf(configFileName, MAXRBUF, "%s/.indi/%s_config.xml", getenv("HOME"), dev);
1054 
1055  }
1056 
1057  if (dest_config)
1058  strncpy(configDefaultFileName, dest_config, MAXRBUF);
1059  else if (getenv("INDICONFIG"))
1060  snprintf(configDefaultFileName, MAXRBUF, "%s.default", getenv("INDICONFIG"));
1061  else
1062  snprintf(configDefaultFileName, MAXRBUF, "%s/.indi/%s_config.xml.default", getenv("HOME"), dev);
1063 
1064  // If the default doesn't exist, create it.
1065  if (access(configDefaultFileName, F_OK))
1066  {
1067  FILE *fpin = fopen(configFileName, "r");
1068  if(fpin != NULL)
1069  {
1070  FILE *fpout = fopen(configDefaultFileName, "w");
1071  if(fpout != NULL)
1072  {
1073  int ch = 0;
1074  while((ch = getc(fpin)) != EOF)
1075  putc(ch, fpout);
1076 
1077  fclose(fpin);
1078  fclose(fpout);
1079  }
1080  }
1081  }
1082 
1083 }
1084 
1085 
1086 
1087 
1088 /* send client a message for a specific device or at large if !dev */
1089 void
1090 IDMessage (const char *dev, const char *fmt, ...)
1091 {
1092 
1093  xmlv1();
1094  printf ("<message\n");
1095  if (dev)
1096  printf (" device='%s'\n", dev);
1097  printf (" timestamp='%s'\n", timestamp());
1098  if (fmt) {
1099  va_list ap;
1100  va_start (ap, fmt);
1101  printf (" message='");
1102  vprintf (fmt, ap);
1103  printf ("'\n");
1104  va_end (ap);
1105  }
1106  printf ("/>\n");
1107  fflush (stdout);
1108 }
1109 
1110 FILE * IUGetConfigFP(const char *filename, const char *dev, char errmsg[])
1111 {
1112  char configFileName[MAXRBUF];
1113  char configDir[MAXRBUF];
1114  struct stat st;
1115  FILE *fp = NULL;
1116 
1117  snprintf(configDir, MAXRBUF, "%s/.indi/", getenv("HOME"));
1118 
1119  if (filename)
1120  strncpy(configFileName, filename, MAXRBUF);
1121  else
1122  {
1123  if (getenv("INDICONFIG"))
1124  strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1125  else
1126  snprintf(configFileName, MAXRBUF, "%s%s_config.xml", configDir, dev);
1127 
1128  }
1129 
1130  if(stat(configDir,&st) != 0)
1131  {
1132  if (mkdir(configDir, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH) < 0)
1133  {
1134  snprintf(errmsg, MAXRBUF, "Unable to create config directory. Error %s: %s\n", configDir, strerror(errno));
1135  return NULL;
1136  }
1137  }
1138 
1139  fp = fopen(configFileName, "w");
1140  if (fp == NULL)
1141  {
1142  snprintf(errmsg, MAXRBUF, "Unable to open config file. Error loading file %s: %s\n", configFileName, strerror(errno));
1143  return NULL;
1144  }
1145 
1146  return fp;
1147 }
1148 
1149 void IUSaveConfigTag(FILE *fp, int ctag)
1150 {
1151  if (!fp)
1152  return;
1153 
1154  /* Opening tag */
1155  if (ctag == 0)
1156  fprintf(fp, "<INDIDriver>\n");
1157  /* Closing tag */
1158  else
1159  fprintf(fp, "</INDIDriver>\n");
1160 }
1161 
1162 void IUSaveConfigNumber (FILE *fp, const INumberVectorProperty *nvp)
1163 {
1164  int i;
1165 
1166  fprintf (fp, "<newNumberVector device='%s' name='%s'>\n", nvp->device, nvp->name);
1167 
1168  for (i = 0; i < nvp->nnp; i++)
1169  {
1170  INumber *np = &nvp->np[i];
1171  fprintf (fp, " <oneNumber name='%s'>\n", np->name);
1172  fprintf (fp, " %.20g\n", np->value);
1173  fprintf (fp, " </oneNumber>\n");
1174  }
1175 
1176  fprintf (fp, "</newNumberVector>\n");
1177 }
1178 
1179 void IUSaveConfigText (FILE *fp, const ITextVectorProperty *tvp)
1180 {
1181  int i;
1182 
1183  fprintf (fp, "<newTextVector device='%s' name='%s'>\n", tvp->device, tvp->name);
1184 
1185  for (i = 0; i < tvp->ntp; i++)
1186  {
1187  IText *tp = &tvp->tp[i];
1188  fprintf (fp, " <oneText name='%s'>\n", tp->name);
1189  fprintf (fp, " %s\n", tp->text ? tp->text : "");
1190  fprintf (fp, " </oneText>\n");
1191  }
1192 
1193  fprintf (fp, "</newTextVector>\n");
1194 
1195 }
1196 
1197 void IUSaveConfigSwitch (FILE *fp, const ISwitchVectorProperty *svp)
1198 {
1199  int i;
1200 
1201  fprintf (fp, "<newSwitchVector device='%s' name='%s'>\n", svp->device, svp->name);
1202 
1203  for (i = 0; i < svp->nsp; i++)
1204  {
1205  ISwitch *sp = &svp->sp[i];
1206  fprintf (fp, " <oneSwitch name='%s'>\n", sp->name);
1207  fprintf (fp, " %s\n", sstateStr(sp->s));
1208  fprintf (fp, " </oneSwitch>\n");
1209  }
1210 
1211  fprintf (fp, "</newSwitchVector>\n");
1212 
1213 }
1214 
1215 void IUSaveConfigBLOB (FILE *fp, const IBLOBVectorProperty *bvp)
1216 {
1217  int i;
1218 
1219  fprintf (fp, "<newBLOBVector device='%s' name='%s'>\n", bvp->device, bvp->name);
1220 
1221  for (i = 0; i < bvp->nbp; i++)
1222  {
1223  IBLOB *bp = &bvp->bp[i];
1224  unsigned char *encblob;
1225  int j, l;
1226 
1227  fprintf (fp, " <oneBLOB\n");
1228  fprintf (fp, " name='%s'\n", bp->name);
1229  fprintf (fp, " size='%d'\n", bp->size);
1230  fprintf (fp, " format='%s'>\n", bp->format);
1231 
1232  encblob = malloc (4*bp->bloblen/3+4);
1233  l = to64frombits(encblob, bp->blob, bp->bloblen);
1234  for (j = 0; j < l; j += 72)
1235  fprintf (fp, "%.72s\n", encblob+j);
1236  free (encblob);
1237 
1238  fprintf (fp, " </oneBLOB>\n");
1239  }
1240 
1241  fprintf (fp, "</newBLOBVector>\n");
1242 
1243 }
1244 
1245 /* tell client to create a text vector property */
1246 void
1247 IDDefText (const ITextVectorProperty *tvp, const char *fmt, ...)
1248 {
1249  int i;
1250  ROSC *SC;
1251 
1252  xmlv1();
1253  printf ("<defTextVector\n");
1254  printf (" device='%s'\n", tvp->device);
1255  printf (" name='%s'\n", tvp->name);
1256  printf (" label='%s'\n", tvp->label);
1257  printf (" group='%s'\n", tvp->group);
1258  printf (" state='%s'\n", pstateStr(tvp->s));
1259  printf (" perm='%s'\n", permStr(tvp->p));
1260  printf (" timeout='%g'\n", tvp->timeout);
1261  printf (" timestamp='%s'\n", timestamp());
1262  if (fmt) {
1263  va_list ap;
1264  va_start (ap, fmt);
1265  printf (" message='");
1266  vprintf (fmt, ap);
1267  printf ("'\n");
1268  va_end (ap);
1269  }
1270  printf (">\n");
1271 
1272  for (i = 0; i < tvp->ntp; i++) {
1273  IText *tp = &tvp->tp[i];
1274  printf (" <defText\n");
1275  printf (" name='%s'\n", tp->name);
1276  printf (" label='%s'>\n", tp->label);
1277  printf (" %s\n", tp->text ? tp->text : "");
1278  printf (" </defText>\n");
1279  }
1280 
1281  printf ("</defTextVector>\n");
1282 
1283  if (!isPropDefined(tvp->name))
1284  {
1285  /* Add this property to insure proper sanity check */
1286  roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
1287  : (ROSC *) malloc ( sizeof(ROSC));
1288  SC = &roCheck[nroCheck++];
1289 
1290  strcpy(SC->propName, tvp->name);
1291  SC->perm = tvp->p;
1292  }
1293 
1294  fflush (stdout);
1295 }
1296 
1297 /* tell client to create a new numeric vector property */
1298 void
1299 IDDefNumber (const INumberVectorProperty *n, const char *fmt, ...)
1300 {
1301  int i;
1302  ROSC *SC;
1303 
1304  xmlv1();
1305  printf ("<defNumberVector\n");
1306  printf (" device='%s'\n", n->device);
1307  printf (" name='%s'\n", n->name);
1308  printf (" label='%s'\n", n->label);
1309  printf (" group='%s'\n", n->group);
1310  printf (" state='%s'\n", pstateStr(n->s));
1311  printf (" perm='%s'\n", permStr(n->p));
1312  printf (" timeout='%g'\n", n->timeout);
1313  printf (" timestamp='%s'\n", timestamp());
1314 
1315 
1316  if (fmt) {
1317  va_list ap;
1318  va_start (ap, fmt);
1319  printf (" message='");
1320  vprintf (fmt, ap);
1321  printf ("'\n");
1322  va_end (ap);
1323  }
1324  printf (">\n");
1325 
1326 
1327  for (i = 0; i < n->nnp; i++) {
1328 
1329  INumber *np = &n->np[i];
1330 
1331  printf (" <defNumber\n");
1332  printf (" name='%s'\n", np->name);
1333  printf (" label='%s'\n", np->label);
1334  printf (" format='%s'\n", np->format);
1335  printf (" min='%.20g'\n", np->min);
1336  printf (" max='%.20g'\n", np->max);
1337  printf (" step='%.20g'>\n", np->step);
1338  printf (" %.20g\n", np->value);
1339 
1340  printf (" </defNumber>\n");
1341  }
1342 
1343  printf ("</defNumberVector>\n");
1344 
1345  if (!isPropDefined(n->name))
1346  {
1347  /* Add this property to insure proper sanity check */
1348  roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
1349  : (ROSC *) malloc ( sizeof(ROSC));
1350  SC = &roCheck[nroCheck++];
1351 
1352  strcpy(SC->propName, n->name);
1353  SC->perm = n->p;
1354 
1355  }
1356 
1357  fflush (stdout);
1358 }
1359 
1360 /* tell client to create a new switch vector property */
1361 void
1362 IDDefSwitch (const ISwitchVectorProperty *s, const char *fmt, ...)
1363 
1364 {
1365  int i;
1366  ROSC *SC;
1367 
1368  xmlv1();
1369  printf ("<defSwitchVector\n");
1370  printf (" device='%s'\n", s->device);
1371  printf (" name='%s'\n", s->name);
1372  printf (" label='%s'\n", s->label);
1373  printf (" group='%s'\n", s->group);
1374  printf (" state='%s'\n", pstateStr(s->s));
1375  printf (" perm='%s'\n", permStr(s->p));
1376  printf (" rule='%s'\n", ruleStr (s->r));
1377  printf (" timeout='%g'\n", s->timeout);
1378  printf (" timestamp='%s'\n", timestamp());
1379  if (fmt) {
1380  va_list ap;
1381  va_start (ap, fmt);
1382  printf (" message='");
1383  vprintf (fmt, ap);
1384  printf ("'\n");
1385  va_end (ap);
1386  }
1387  printf (">\n");
1388 
1389  for (i = 0; i < s->nsp; i++) {
1390  ISwitch *sp = &s->sp[i];
1391  printf (" <defSwitch\n");
1392  printf (" name='%s'\n", sp->name);
1393  printf (" label='%s'>\n", sp->label);
1394  printf (" %s\n", sstateStr(sp->s));
1395  printf (" </defSwitch>\n");
1396  }
1397 
1398  printf ("</defSwitchVector>\n");
1399 
1400  if (!isPropDefined(s->name))
1401  {
1402  /* Add this property to insure proper sanity check */
1403  roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
1404  : (ROSC *) malloc ( sizeof(ROSC));
1405  SC = &roCheck[nroCheck++];
1406 
1407  strcpy(SC->propName, s->name);
1408  SC->perm = s->p;
1409  }
1410 
1411  fflush (stdout);
1412 }
1413 
1414 /* tell client to create a new lights vector property */
1415 void
1416 IDDefLight (const ILightVectorProperty *lvp, const char *fmt, ...)
1417 {
1418  int i;
1419 
1420  xmlv1();
1421  printf ("<defLightVector\n");
1422  printf (" device='%s'\n", lvp->device);
1423  printf (" name='%s'\n", lvp->name);
1424  printf (" label='%s'\n", lvp->label);
1425  printf (" group='%s'\n", lvp->group);
1426  printf (" state='%s'\n", pstateStr(lvp->s));
1427  printf (" timestamp='%s'\n", timestamp());
1428  if (fmt) {
1429  va_list ap;
1430  va_start (ap, fmt);
1431  printf (" message='");
1432  vprintf (fmt, ap);
1433  printf ("'\n");
1434  va_end (ap);
1435  }
1436  printf (">\n");
1437 
1438  for (i = 0; i < lvp->nlp; i++) {
1439  ILight *lp = &lvp->lp[i];
1440  printf (" <defLight\n");
1441  printf (" name='%s'\n", lp->name);
1442  printf (" label='%s'>\n", lp->label);
1443  printf (" %s\n", pstateStr(lp->s));
1444  printf (" </defLight>\n");
1445  }
1446 
1447  printf ("</defLightVector>\n");
1448  fflush (stdout);
1449 }
1450 
1451 /* tell client to create a new BLOB vector property */
1452 void
1453 IDDefBLOB (const IBLOBVectorProperty *b, const char *fmt, ...)
1454 {
1455  int i;
1456  ROSC *SC;
1457 
1458  xmlv1();
1459  printf ("<defBLOBVector\n");
1460  printf (" device='%s'\n", b->device);
1461  printf (" name='%s'\n", b->name);
1462  printf (" label='%s'\n", b->label);
1463  printf (" group='%s'\n", b->group);
1464  printf (" state='%s'\n", pstateStr(b->s));
1465  printf (" perm='%s'\n", permStr(b->p));
1466  printf (" timeout='%g'\n", b->timeout);
1467  printf (" timestamp='%s'\n", timestamp());
1468  if (fmt) {
1469  va_list ap;
1470  va_start (ap, fmt);
1471  printf (" message='");
1472  vprintf (fmt, ap);
1473  printf ("'\n");
1474  va_end (ap);
1475  }
1476  printf (">\n");
1477 
1478  for (i = 0; i < b->nbp; i++) {
1479  IBLOB *bp = &b->bp[i];
1480  printf (" <defBLOB\n");
1481  printf (" name='%s'\n", bp->name);
1482  printf (" label='%s'\n", bp->label);
1483  printf (" />\n");
1484  }
1485 
1486  printf ("</defBLOBVector>\n");
1487 
1488  if (!isPropDefined(b->name))
1489  {
1490  /* Add this property to insure proper sanity check */
1491  roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
1492  : (ROSC *) malloc ( sizeof(ROSC));
1493  SC = &roCheck[nroCheck++];
1494 
1495  strcpy(SC->propName, b->name);
1496  SC->perm = b->p;
1497  }
1498 
1499  fflush (stdout);
1500 }
1501 
1502 /* tell client to update an existing text vector property */
1503 void
1504 IDSetText (const ITextVectorProperty *tvp, const char *fmt, ...)
1505 {
1506  int i;
1507 
1508  xmlv1();
1509  printf ("<setTextVector\n");
1510  printf (" device='%s'\n", tvp->device);
1511  printf (" name='%s'\n", tvp->name);
1512  printf (" state='%s'\n", pstateStr(tvp->s));
1513  printf (" timeout='%g'\n", tvp->timeout);
1514  printf (" timestamp='%s'\n", timestamp());
1515  if (fmt) {
1516  va_list ap;
1517  va_start (ap, fmt);
1518  printf (" message='");
1519  vprintf (fmt, ap);
1520  printf ("'\n");
1521  va_end (ap);
1522  }
1523  printf (">\n");
1524 
1525  for (i = 0; i < tvp->ntp; i++) {
1526  IText *tp = &tvp->tp[i];
1527  printf (" <oneText name='%s'>\n", tp->name);
1528  printf (" %s\n", tp->text ? tp->text : "");
1529  printf (" </oneText>\n");
1530  }
1531 
1532  printf ("</setTextVector>\n");
1533  fflush (stdout);
1534 }
1535 
1536 /* tell client to update an existing numeric vector property */
1537 void
1538 IDSetNumber (const INumberVectorProperty *nvp, const char *fmt, ...)
1539 {
1540  int i;
1541 
1542  xmlv1();
1543  printf ("<setNumberVector\n");
1544  printf (" device='%s'\n", nvp->device);
1545  printf (" name='%s'\n", nvp->name);
1546  printf (" state='%s'\n", pstateStr(nvp->s));
1547  printf (" timeout='%g'\n", nvp->timeout);
1548  printf (" timestamp='%s'\n", timestamp());
1549  if (fmt) {
1550  va_list ap;
1551  va_start (ap, fmt);
1552  printf (" message='");
1553  vprintf (fmt, ap);
1554  printf ("'\n");
1555  va_end (ap);
1556  }
1557  printf (">\n");
1558 
1559  for (i = 0; i < nvp->nnp; i++) {
1560  INumber *np = &nvp->np[i];
1561  printf (" <oneNumber name='%s'>\n", np->name);
1562  printf (" %.20g\n", np->value);
1563  printf (" </oneNumber>\n");
1564  }
1565 
1566  printf ("</setNumberVector>\n");
1567  fflush (stdout);
1568 }
1569 
1570 /* tell client to update an existing switch vector property */
1571 void
1572 IDSetSwitch (const ISwitchVectorProperty *svp, const char *fmt, ...)
1573 {
1574  int i;
1575 
1576  xmlv1();
1577  printf ("<setSwitchVector\n");
1578  printf (" device='%s'\n", svp->device);
1579  printf (" name='%s'\n", svp->name);
1580  printf (" state='%s'\n", pstateStr(svp->s));
1581  printf (" timeout='%g'\n", svp->timeout);
1582  printf (" timestamp='%s'\n", timestamp());
1583  if (fmt) {
1584  va_list ap;
1585  va_start (ap, fmt);
1586  printf (" message='");
1587  vprintf (fmt, ap);
1588  printf ("'\n");
1589  va_end (ap);
1590  }
1591  printf (">\n");
1592 
1593  for (i = 0; i < svp->nsp; i++) {
1594  ISwitch *sp = &svp->sp[i];
1595  printf (" <oneSwitch name='%s'>\n", sp->name);
1596  printf (" %s\n", sstateStr(sp->s));
1597  printf (" </oneSwitch>\n");
1598  }
1599 
1600  printf ("</setSwitchVector>\n");
1601  fflush (stdout);
1602 }
1603 
1604 /* tell client to update an existing lights vector property */
1605 void
1606 IDSetLight (const ILightVectorProperty *lvp, const char *fmt, ...)
1607 {
1608  int i;
1609 
1610  xmlv1();
1611  printf ("<setLightVector\n");
1612  printf (" device='%s'\n", lvp->device);
1613  printf (" name='%s'\n", lvp->name);
1614  printf (" state='%s'\n", pstateStr(lvp->s));
1615  printf (" timestamp='%s'\n", timestamp());
1616  if (fmt) {
1617  va_list ap;
1618  va_start (ap, fmt);
1619  printf (" message='");
1620  vprintf (fmt, ap);
1621  printf ("'\n");
1622  va_end (ap);
1623  }
1624  printf (">\n");
1625 
1626  for (i = 0; i < lvp->nlp; i++) {
1627  ILight *lp = &lvp->lp[i];
1628  printf (" <oneLight name='%s'>\n", lp->name);
1629  printf (" %s\n", pstateStr(lp->s));
1630  printf (" </oneLight>\n");
1631  }
1632 
1633  printf ("</setLightVector>\n");
1634  fflush (stdout);
1635 }
1636 
1637 /* tell client to update an existing BLOB vector property */
1638 void
1639 IDSetBLOB (const IBLOBVectorProperty *bvp, const char *fmt, ...)
1640 {
1641  int i;
1642 
1643  xmlv1();
1644  printf ("<setBLOBVector\n");
1645  printf (" device='%s'\n", bvp->device);
1646  printf (" name='%s'\n", bvp->name);
1647  printf (" state='%s'\n", pstateStr(bvp->s));
1648  printf (" timeout='%g'\n", bvp->timeout);
1649  printf (" timestamp='%s'\n", timestamp());
1650  if (fmt) {
1651  va_list ap;
1652  va_start (ap, fmt);
1653  printf (" message='");
1654  vprintf (fmt, ap);
1655  printf ("'\n");
1656  va_end (ap);
1657  }
1658  printf (">\n");
1659 
1660  for (i = 0; i < bvp->nbp; i++) {
1661  IBLOB *bp = &bvp->bp[i];
1662  unsigned char *encblob;
1663  int j, l;
1664 
1665  printf (" <oneBLOB\n");
1666  printf (" name='%s'\n", bp->name);
1667  printf (" size='%d'\n", bp->size);
1668  printf (" format='%s'>\n", bp->format);
1669 
1670  encblob = malloc (4*bp->bloblen/3+4);
1671  l = to64frombits(encblob, bp->blob, bp->bloblen);
1672  for (j = 0; j < l; j += 72)
1673  printf ("%.72s\n", encblob+j);
1674  free (encblob);
1675 
1676  printf (" </oneBLOB>\n");
1677  }
1678 
1679  printf ("</setBLOBVector>\n");
1680  fflush (stdout);
1681 }
1682 
1683 /* tell client to update min/max elements of an existing number vector property */
1685 {
1686  int i;
1687 
1688  xmlv1();
1689  printf ("<setNumberVector\n");
1690  printf (" device='%s'\n", nvp->device);
1691  printf (" name='%s'\n", nvp->name);
1692  printf (" state='%s'\n", pstateStr(nvp->s));
1693  printf (" timeout='%g'\n", nvp->timeout);
1694  printf (" timestamp='%s'\n", timestamp());
1695  printf (">\n");
1696 
1697  for (i = 0; i < nvp->nnp; i++) {
1698  INumber *np = &nvp->np[i];
1699  printf (" <oneNumber name='%s'\n", np->name);
1700  printf (" min='%g'\n", np->min);
1701  printf (" max='%g'\n", np->max);
1702  printf (" step='%g'\n", np->step);
1703  printf(">\n");
1704  printf (" %g\n", np->value);
1705  printf (" </oneNumber>\n");
1706  }
1707 
1708  printf ("</setNumberVector>\n");
1709  fflush (stdout);
1710 }
1711 
1712 /* Return 1 is property is already cached, 0 otherwise */
1713 int isPropDefined(const char *property_name)
1714 {
1715  int i=0;
1716 
1717  for (i=0; i < nroCheck; i++)
1718  if (!strcmp(property_name, roCheck[i].propName))
1719  return 1;
1720 
1721  return 0;
1722 
1723 }
1724 
1725