Instrument Neutral Distributed Interface INDI  1.9.2
baseclient.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2011 Jasem Mutlaq. 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 #define NOMINMAX
20 #define WIN32_LEAN_AND_MEAN
21 
22 #include "baseclient.h"
23 
24 #include "indistandardproperty.h"
25 #include "base64.h"
26 #include "basedevice.h"
27 #include "locale_compat.h"
28 
29 #include <cerrno>
30 #include <fcntl.h>
31 #include <cstdlib>
32 #include <stdarg.h>
33 #include <cstring>
34 #include <algorithm>
35 #include <chrono>
36 #include <functional>
37 #include <assert.h>
38 
39 #include "indiuserio.h"
40 
41 #ifdef _WINDOWS
42 #include <ws2tcpip.h>
43 #include <windows.h>
44 
45 #define net_read(x,y,z) recv(x,y,z,0)
46 #define net_write(x,y,z) send(x,(const char *)(y),z,0)
47 #define net_close closesocket
48 
49 #pragma comment(lib, "Ws2_32.lib")
50 #else
51 #include <netdb.h>
52 #include <unistd.h>
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #define net_read read
57 #define net_write write
58 #define net_close close
59 #endif
60 
61 #ifdef _MSC_VER
62 # define snprintf _snprintf
63 #endif
64 
65 #define MAXINDIBUF 49152
66 #define DISCONNECTION_DELAY_US 500000
67 
68 static userio io;
69 
70 #include "baseclient_p.h"
71 
72 namespace INDI
73 {
74 
76  : parent(parent)
77  , cServer("localhost")
78  , cPort(7624)
79  , sConnected(false)
80  , verbose(false)
81  , timeout_sec(3)
82  , timeout_us(0)
83 {
84  io.write = [](void *user, const void * ptr, size_t count) -> size_t
85  {
86  auto self = static_cast<BaseClientPrivate *>(user);
87  return self->sendData(ptr, count);
88  };
89 
90  io.vprintf = [](void *user, const char * format, va_list ap) -> int
91  {
92  auto self = static_cast<BaseClientPrivate *>(user);
93  char message[MAXRBUF];
94  vsnprintf(message, MAXRBUF, format, ap);
95  return self->sendData(message, strlen(message));
96  };
97 }
98 
100 {
101  if (sConnected)
102  disconnect(0);
103 
104  std::unique_lock<std::mutex> locker(sSocketBusy);
105  if (!sSocketChanged.wait_for(locker, std::chrono::milliseconds(500), [this] { return sConnected == false; }))
106  {
107  IDLog("BaseClient::~BaseClient: Probability of detecting a deadlock.\n");
108  /* #PS:
109  * KStars bug - suspicion
110  * The function thread 'BaseClient::listenINDI' could not be terminated
111  * because the 'dispatchCommand' function is in progress.
112  *
113  * The function 'dispatchCommand' cannot be completed
114  * because it is related to the function call 'ClientManager::newProperty'.
115  *
116  * There is a call that uses BlockingQueuedConnection to the thread that is currently busy
117  * destroying the BaseClient object.
118  *
119  */
120  }
121 }
122 
124 {
125  while (!cDevices.empty())
126  {
127  delete cDevices.back();
128  cDevices.pop_back();
129  }
130  cDevices.clear();
131  blobModes.clear();
132  // cDeviceNames.clear(); // #PS: missing?
133 }
134 
136 {
137  std::unique_lock<std::mutex> locker(sSocketBusy);
138  if (sConnected == true)
139  {
140  IDLog("INDI::BaseClient::connectServer: Already connected.\n");
141  return false;
142  }
143 
144  IDLog("INDI::BaseClient::connectServer: creating new connection...\n");
145 
146 #ifdef _WINDOWS
147  WSADATA wsaData;
148  int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
149  if (iResult != NO_ERROR)
150  {
151  IDLog("Error at WSAStartup()\n");
152  return false;
153  }
154 #endif
155 
156  struct timeval ts;
157  ts.tv_sec = timeout_sec;
158  ts.tv_usec = timeout_us;
159 
160  struct sockaddr_in serv_addr;
161  struct hostent *hp;
162  int ret = 0;
163 
164  /* lookup host address */
165  hp = gethostbyname(cServer.c_str());
166  if (!hp)
167  {
168  perror("gethostbyname");
169  return false;
170  }
171 
172  /* create a socket to the INDI server */
173  (void)memset((char *)&serv_addr, 0, sizeof(serv_addr));
174  serv_addr.sin_family = AF_INET;
175  serv_addr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
176  serv_addr.sin_port = htons(cPort);
177 #ifdef _WINDOWS
178  if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
179  {
180  IDLog("Socket error: %d\n", WSAGetLastError());
181  WSACleanup();
182  return false;
183  }
184 #else
185  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
186  {
187  perror("socket");
188  return false;
189  }
190 #endif
191 
192  /* set the socket in non-blocking */
193  //set socket nonblocking flag
194 #ifdef _WINDOWS
195  u_long iMode = 0;
196  iResult = ioctlsocket(sockfd, FIONBIO, &iMode);
197  if (iResult != NO_ERROR)
198  {
199  IDLog("ioctlsocket failed with error: %ld\n", iResult);
200  return false;
201  }
202 #else
203  int flags = 0;
204  if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
205  return false;
206 
207  if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0)
208  return false;
209 #endif
210 
211  //clear out descriptor sets for select
212  //add socket to the descriptor sets
213  fd_set rset, wset;
214  FD_ZERO(&rset);
215  FD_SET(sockfd, &rset);
216  wset = rset; //structure assignment okok
217 
218  /* connect */
219  if ((ret = ::connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0)
220  {
221  if (errno != EINPROGRESS)
222  {
223  perror("connect");
224  net_close(sockfd);
225  return false;
226  }
227  }
228 
229  /* If it is connected, continue, otherwise wait */
230  if (ret != 0)
231  {
232  //we are waiting for connect to complete now
233  if ((ret = select(sockfd + 1, &rset, &wset, nullptr, &ts)) < 0)
234  return false;
235  //we had a timeout
236  if (ret == 0)
237  {
238 #ifdef _WINDOWS
239  IDLog("select timeout\n");
240 #else
241  errno = ETIMEDOUT;
242  perror("select timeout");
243 #endif
244  return false;
245  }
246  }
247 
248  /* we had a positivite return so a descriptor is ready */
249 #ifndef _WINDOWS
250  int error = 0;
251  socklen_t len = sizeof(error);
252  if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset))
253  {
254  if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
255  {
256  perror("getsockopt");
257  return false;
258  }
259  }
260  else
261  return false;
262 
263  /* check if we had a socket error */
264  if (error)
265  {
266  errno = error;
267  perror("socket");
268  return false;
269  }
270 #endif
271 
272 #ifndef _WINDOWS
273  int pipefd[2];
274  ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
275 
276  if (ret < 0)
277  {
278  IDLog("notify pipe: %s\n", strerror(errno));
279  return false;
280  }
281 
282  receiveFd = pipefd[0];
283  sendFd = pipefd[1];
284 #endif
285 
286  sConnected = true;
287  sAboutToClose = false;
288  sSocketChanged.notify_all();
290  std::thread(std::bind(&BaseClientPrivate::listenINDI, this)).detach();
291 
292  return true;
293 }
294 
295 bool BaseClientPrivate::disconnect(int exit_code)
296 {
297  //IDLog("Server disconnected called\n");
298  std::lock_guard<std::mutex> locker(sSocketBusy);
299  if (sConnected == false)
300  {
301  IDLog("INDI::BaseClient::disconnectServer: Already disconnected.\n");
302  return false;
303  }
304  sAboutToClose = true;
305  sSocketChanged.notify_all();
306 #ifdef _WINDOWS
307  net_close(sockfd); // close and wakeup 'select' function
308  WSACleanup();
309  sockfd = INVALID_SOCKET;
310 #else
311  shutdown(sockfd, SHUT_RDWR); // no needed
312  size_t c = 1;
313  // wakeup 'select' function
314  ssize_t ret = write(sendFd, &c, sizeof(c));
315  if (ret != sizeof(c))
316  {
317  IDLog("INDI::BaseClient::disconnectServer: Error. The socket cannot be woken up.\n");
318  }
319 #endif
320  sExitCode = exit_code;
321  return true;
322 }
323 
325 {
326  char buffer[MAXINDIBUF];
327  char msg[MAXRBUF];
328 #ifdef _WINDOWS
329  SOCKET maxfd = 0;
330 #else
331  int maxfd = 0;
332 #endif
333  fd_set rs;
334  XMLEle **nodes = nullptr;
335  XMLEle *root = nullptr;
336  int inode = 0;
337 
338  connect();
339 
340  if (cDeviceNames.empty())
341  {
342  IUUserIOGetProperties(&io, this, nullptr, nullptr);
343  if (verbose)
344  IUUserIOGetProperties(userio_file(), stderr, nullptr, nullptr);
345  }
346  else
347  {
348  for (const auto &oneDevice : cDeviceNames)
349  {
350  // If there are no specific properties to watch, we watch the complete device
351  if (cWatchProperties.find(oneDevice) == cWatchProperties.end())
352  {
353  IUUserIOGetProperties(&io, this, oneDevice.c_str(), nullptr);
354  if (verbose)
355  IUUserIOGetProperties(userio_file(), stderr, oneDevice.c_str(), nullptr);
356  }
357  else
358  {
359  for (const auto &oneProperty : cWatchProperties[oneDevice])
360  {
361  IUUserIOGetProperties(&io, this, oneDevice.c_str(), oneProperty.c_str());
362  if (verbose)
363  IUUserIOGetProperties(userio_file(), stderr, oneDevice.c_str(), oneProperty.c_str());
364  }
365  }
366  }
367  }
368 
369  FD_ZERO(&rs);
370 
371  FD_SET(sockfd, &rs);
372  maxfd = std::max(maxfd, sockfd);
373 
374 #ifndef _WINDOWS
375  FD_SET(receiveFd, &rs);
376  maxfd = std::max(maxfd, receiveFd);
377 #endif
378 
379  clear();
380  LilXML *lillp = newLilXML();
381 
382  /* read from server, exit if find all requested properties */
383  while (!sAboutToClose)
384  {
385  int n = select(maxfd + 1, &rs, nullptr, nullptr, nullptr);
386 
387  // Woken up by disconnectServer function.
388  if (sAboutToClose == true)
389  {
390  break;
391  }
392 
393  if (n < 0)
394  {
395  IDLog("INDI server %s/%d disconnected.\n", cServer.c_str(), cPort);
396  break;
397  }
398 
399  if (n == 0)
400  {
401  continue;
402  }
403 
404  if (FD_ISSET(sockfd, &rs))
405  {
406 #ifdef _WINDOWS
407  n = recv(sockfd, buffer, MAXINDIBUF, 0);
408 #else
409  n = recv(sockfd, buffer, MAXINDIBUF, MSG_DONTWAIT);
410 #endif
411  if (n < 0)
412  {
413  continue;
414  }
415 
416  if (n == 0)
417  {
418  IDLog("INDI server %s/%d disconnected.\n", cServer.c_str(), cPort);
419  break;
420  }
421 
422  nodes = parseXMLChunk(lillp, buffer, n, msg);
423 
424  if (!nodes)
425  {
426  if (msg[0])
427  {
428  IDLog("Bad XML from %s/%d: %s\n%s\n", cServer.c_str(), cPort, msg, buffer);
429  }
430  break;
431  }
432  root = nodes[inode];
433  while (root)
434  {
435  if (verbose)
436  prXMLEle(stderr, root, 0);
437 
438  int err_code = dispatchCommand(root, msg);
439 
440  if (err_code < 0)
441  {
442  // Silenty ignore property duplication errors
443  if (err_code != INDI_PROPERTY_DUPLICATED)
444  {
445  IDLog("Dispatch command error(%d): %s\n", err_code, msg);
446  prXMLEle(stderr, root, 0);
447  }
448  }
449 
450  delXMLEle(root); // not yet, delete and continue
451  inode++;
452  root = nodes[inode];
453  }
454  free(nodes);
455  inode = 0;
456  }
457  }
458 
459  delLilXML(lillp);
460 
461  int exit_code;
462 
463  {
464  std::lock_guard<std::mutex> locker(sSocketBusy);
465 #ifdef _WINDOWS
466  if (sockfd != INVALID_SOCKET)
467  {
468  net_close(sockfd);
469  WSACleanup();
470  sockfd = INVALID_SOCKET;
471  }
472 #else
473  close(sockfd);
474  close(receiveFd);
475  close(sendFd);
476 #endif
477 
478  exit_code = sAboutToClose ? sExitCode : -1;
479  sConnected = false;
480  parent->serverDisconnected(exit_code);
481 
482  clear();
483  cDeviceNames.clear();
484  sSocketChanged.notify_all();
485  }
486 }
487 
488 size_t BaseClientPrivate::sendData(const void *data, size_t size)
489 {
490  int ret;
491 
492  do
493  {
494  std::lock_guard<std::mutex> locker(sSocketBusy);
495  if (sConnected == false)
496  return 0;
497  ret = net_write(sockfd, data, size);
498  }
499  while(ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
500 
501  if (ret < 0)
502  {
503  disconnect(-1);
504  }
505 
506  return std::max(ret, 0);
507 }
508 
509 void BaseClientPrivate::sendString(const char *fmt, ...)
510 {
511  char message[MAXRBUF];
512  va_list ap;
513 
514  va_start(ap, fmt);
515  vsnprintf(message, MAXRBUF, fmt, ap);
516  va_end(ap);
517  sendData(message, strlen(message));
518 }
519 
521 {
522  const char *tag = tagXMLEle(root);
523 
524  if (!strcmp(tag, "message"))
525  return messageCmd(root, errmsg);
526  else if (!strcmp(tag, "delProperty"))
527  return delPropertyCmd(root, errmsg);
528  // Just ignore any getProperties we might get
529  else if (!strcmp(tag, "getProperties"))
531 
532  /* Get the device, if not available, create it */
533  INDI::BaseDevice *dp = findDev(root, 1, errmsg);
534  if (dp == nullptr)
535  {
536  strcpy(errmsg, "No device available and none was created");
537  return INDI_DEVICE_NOT_FOUND;
538  }
539 
540  // Ignore echoed newXXX
541  if (strstr(tag, "new"))
542  return 0;
543 
544  // If device is set to BLOB_ONLY, we ignore everything else
545  // not related to blobs
546  if (parent->getBLOBMode(dp->getDeviceName()) == B_ONLY)
547  {
548  if (!strcmp(tag, "defBLOBVector"))
549  return dp->buildProp(root, errmsg);
550  else if (!strcmp(tag, "setBLOBVector"))
551  return dp->setValue(root, errmsg);
552 
553  // Ignore everything else
554  return 0;
555  }
556 
557  // If we are asked to watch for specific properties only, we ignore everything else
558  if (cWatchProperties.size() > 0)
559  {
560  const char *device = findXMLAttValu(root, "device");
561  const char *name = findXMLAttValu(root, "name");
562  if (device && name)
563  {
564  if (cWatchProperties.find(device) == cWatchProperties.end() ||
565  cWatchProperties[device].find(name) == cWatchProperties[device].end())
566  return 0;
567  }
568  }
569 
570  if ((!strcmp(tag, "defTextVector")) || (!strcmp(tag, "defNumberVector")) ||
571  (!strcmp(tag, "defSwitchVector")) || (!strcmp(tag, "defLightVector")) ||
572  (!strcmp(tag, "defBLOBVector")))
573  return dp->buildProp(root, errmsg);
574  else if (!strcmp(tag, "setTextVector") || !strcmp(tag, "setNumberVector") ||
575  !strcmp(tag, "setSwitchVector") || !strcmp(tag, "setLightVector") ||
576  !strcmp(tag, "setBLOBVector"))
577  return dp->setValue(root, errmsg);
578 
579  return INDI_DISPATCH_ERROR;
580 }
581 
582 
583 int BaseClientPrivate::deleteDevice(const char *devName, char *errmsg)
584 {
585  for (auto devicei = cDevices.begin(); devicei != cDevices.end();)
586  {
587  if ((*devicei)->isDeviceNameMatch(devName))
588  {
589  parent->removeDevice(*devicei);
590  delete *devicei;
591  devicei = cDevices.erase(devicei);
592  return 0;
593  }
594  else
595  ++devicei;
596  }
597 
598  snprintf(errmsg, MAXRBUF, "Device %s not found", devName);
599  return INDI_DEVICE_NOT_FOUND;
600 }
601 
602 
603 /* delete the property in the given device, including widgets and data structs.
604  * when last property is deleted, delete the device too.
605  * if no property name attribute at all, delete the whole device regardless.
606  * return 0 if ok, else -1 with reason in errmsg[].
607  */
609 {
610  XMLAtt *ap;
611  INDI::BaseDevice *dp;
612 
613  /* dig out device and optional property name */
614  dp = findDev(root, 0, errmsg);
615  if (!dp)
616  return INDI_DEVICE_NOT_FOUND;
617 
618  dp->checkMessage(root);
619 
620  ap = findXMLAtt(root, "name");
621 
622  /* Delete property if it exists, otherwise, delete the whole device */
623  if (ap)
624  {
625  INDI::Property *rProp = dp->getProperty(valuXMLAtt(ap));
626  if (rProp == nullptr)
627  {
628  // Silently ignore B_ONLY clients.
629  if (blobModes.empty() || blobModes.front().blobMode == B_ONLY)
630  return 0;
631 
632  snprintf(errmsg, MAXRBUF, "Cannot delete property %s as it is not defined yet. Check driver.", valuXMLAtt(ap));
633  return -1;
634  }
635  if (sConnected)
636  parent->removeProperty(rProp);
637  int errCode = dp->removeProperty(valuXMLAtt(ap), errmsg);
638 
639  return errCode;
640  }
641  // delete the whole device
642  else
643  return deleteDevice(dp->getDeviceName(), errmsg);
644 }
645 
646 
647 INDI::BaseDevice *BaseClientPrivate::findDev(const char *devName, char *errmsg)
648 {
649  auto pos = std::find_if(cDevices.begin(), cDevices.end(), [devName](INDI::BaseDevice * oneDevice)
650  {
651  return oneDevice->isDeviceNameMatch(devName);
652  });
653 
654  if (pos != cDevices.end())
655  return *pos;
656 
657  snprintf(errmsg, MAXRBUF, "Device %s not found", devName);
658  return nullptr;
659 }
660 
661 /* add new device */
663 {
664  char *device_name;
665 
666  /* allocate new INDI::BaseDriver */
667  XMLAtt *ap = findXMLAtt(dep, "device");
668  if (!ap)
669  {
670  strncpy(errmsg, "Unable to find device attribute in XML element. Cannot add device.", MAXRBUF);
671  return nullptr;
672  }
673 
675 
676  device_name = valuXMLAtt(ap);
677 
678  dp->setMediator(parent);
679  dp->setDeviceName(device_name);
680 
681  cDevices.push_back(dp);
682 
683  parent->newDevice(dp);
684 
685  /* ok */
686  return dp;
687 }
688 
689 INDI::BaseDevice *BaseClientPrivate::findDev(XMLEle *root, int create, char *errmsg)
690 {
691  XMLAtt *ap;
692  INDI::BaseDevice *dp;
693  char *dn;
694 
695  /* get device name */
696  ap = findXMLAtt(root, "device");
697  if (!ap)
698  {
699  snprintf(errmsg, MAXRBUF, "No device attribute found in element %s", tagXMLEle(root));
700  return (nullptr);
701  }
702 
703  dn = valuXMLAtt(ap);
704 
705  if (*dn == '\0')
706  {
707  snprintf(errmsg, MAXRBUF, "Device name is empty! %s", tagXMLEle(root));
708  return (nullptr);
709  }
710 
711  dp = findDev(dn, errmsg);
712 
713  if (dp)
714  return dp;
715 
716  /* not found, create if ok */
717  if (create)
718  return (addDevice(root, errmsg));
719 
720  snprintf(errmsg, MAXRBUF, "INDI: <%s> no such device %s", tagXMLEle(root), dn);
721  return nullptr;
722 }
723 
724 /* a general message command received from the device.
725  * return 0 if ok, else -1 with reason in errmsg[].
726  */
727 int BaseClientPrivate::messageCmd(XMLEle *root, char *errmsg)
728 {
729  INDI::BaseDevice *dp = findDev(root, 0, errmsg);
730 
731  if (dp)
732  dp->checkMessage(root);
733  else
734  {
735  XMLAtt *message;
736  XMLAtt *time_stamp;
737 
738  char msgBuffer[MAXRBUF];
739 
740  /* prefix our timestamp if not with msg */
741  time_stamp = findXMLAtt(root, "timestamp");
742 
743  /* finally! the msg */
744  message = findXMLAtt(root, "message");
745  if (!message)
746  {
747  strncpy(errmsg, "No message content found.", MAXRBUF);
748  return -1;
749  }
750 
751  if (time_stamp)
752  snprintf(msgBuffer, MAXRBUF, "%s: %s", valuXMLAtt(time_stamp), valuXMLAtt(message));
753  else
754  {
755  char ts[32];
756  struct tm *tp;
757  time_t t;
758  time(&t);
759  tp = gmtime(&t);
760  strftime(ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
761  snprintf(msgBuffer, MAXRBUF, "%s: %s", ts, valuXMLAtt(message));
762  }
763 
764  parent->newUniversalMessage(msgBuffer);
765  }
766 
767  return (0);
768 }
769 
770 
771 BLOBMode *INDI::BaseClientPrivate::findBLOBMode(const std::string &device, const std::string &property)
772 {
773  for (auto &blob : blobModes)
774  {
775  if (blob.device == device && (property.empty() || blob.property == property))
776  return &blob;
777  }
778 
779  return nullptr;
780 }
781 
782 void BaseClientPrivate::setDriverConnection(bool status, const char *deviceName)
783 {
784  INDI::BaseDevice *drv = parent->getDevice(deviceName);
785 
786  if (!drv)
787  {
788  IDLog("INDI::BaseClient: Error. Unable to find driver %s\n", deviceName);
789  return;
790  }
791 
792  auto drv_connection = drv->getSwitch(INDI::SP::CONNECTION);
793 
794  if (!drv_connection)
795  return;
796 
797  // If we need to connect
798  if (status)
799  {
800  // If there is no need to do anything, i.e. already connected.
801  if (drv_connection->at(0)->getState() == ISS_ON)
802  return;
803 
804  drv_connection->reset();
805  drv_connection->setState(IPS_BUSY);
806  drv_connection->at(0)->setState(ISS_ON);
807  drv_connection->at(1)->setState(ISS_OFF);
808 
809  parent->sendNewSwitch(drv_connection);
810  }
811  else
812  {
813  // If there is no need to do anything, i.e. already disconnected.
814  if (drv_connection->at(1)->getState() == ISS_ON)
815  return;
816 
817  drv_connection->reset();
818  drv_connection->setState(IPS_BUSY);
819  drv_connection->at(0)->setState(ISS_OFF);
820  drv_connection->at(1)->setState(ISS_ON);
821 
822  parent->sendNewSwitch(drv_connection);
823  }
824 }
825 
826 }
827 
829  : d_ptr(new BaseClientPrivate(this))
830 { }
831 
833 {
834 
835 }
836 
838 {
839  D_PTR(BaseClient);
840  d->verbose = enable;
841 }
842 
844 {
845  D_PTR(const BaseClient);
846  return d->verbose;
847 }
848 
849 void INDI::BaseClient::setConnectionTimeout(uint32_t seconds, uint32_t microseconds)
850 {
851  D_PTR(BaseClient);
852  d->timeout_sec = seconds;
853  d->timeout_us = microseconds;
854 }
855 
856 void INDI::BaseClient::setServer(const char *hostname, unsigned int port)
857 {
858  D_PTR(BaseClient);
859  d->cServer = hostname;
860  d->cPort = port;
861 }
862 
863 void INDI::BaseClient::watchDevice(const char *deviceName)
864 {
865  D_PTR(BaseClient);
866  d->cDeviceNames.insert(deviceName);
867 }
868 
869 void INDI::BaseClient::watchProperty(const char *deviceName, const char *propertyName)
870 {
871  D_PTR(BaseClient);
872  watchDevice(deviceName);
873  d->cWatchProperties[deviceName].insert(propertyName);
874 }
875 
877 {
878  D_PTR(BaseClient);
879  return d->connect();
880 }
881 
883 {
884  D_PTR(BaseClient);
885  return d->disconnect(exit_code);
886 }
887 
888 // #PS: avoid calling pure virtual method
890 {
891  INDI_UNUSED(exit_code);
892 }
893 
895 {
896  D_PTR(const BaseClient);
897  return d->sConnected;
898 }
899 
900 void INDI::BaseClient::connectDevice(const char *deviceName)
901 {
902  D_PTR(BaseClient);
903  d->setDriverConnection(true, deviceName);
904 }
905 
906 void INDI::BaseClient::disconnectDevice(const char *deviceName)
907 {
908  D_PTR(BaseClient);
909  d->setDriverConnection(false, deviceName);
910 }
911 
913 {
914  D_PTR(BaseClient);
915  for (auto &device : d->cDevices)
916  {
917  if (device->isDeviceNameMatch(deviceName))
918  return device;
919  }
920  return nullptr;
921 }
922 
923 const std::vector<INDI::BaseDevice *> &INDI::BaseClient::getDevices() const
924 {
925  D_PTR(const BaseClient);
926  return d->cDevices;
927 }
928 
929 const char *INDI::BaseClient::getHost() const
930 {
931  D_PTR(const BaseClient);
932  return d->cServer.c_str();
933 }
934 
936 {
937  D_PTR(const BaseClient);
938  return d->cPort;
939 }
940 
941 void INDI::BaseClient::newUniversalMessage(std::string message)
942 {
943  IDLog("%s\n", message.c_str());
944 }
945 
947 {
948  D_PTR(BaseClient);
949  tvp->s = IPS_BUSY;
950  IUUserIONewText(&io, d, tvp);
951 }
952 
953 void INDI::BaseClient::sendNewText(const char *deviceName, const char *propertyName, const char *elementName,
954  const char *text)
955 {
956  INDI::BaseDevice *drv = getDevice(deviceName);
957 
958  if (!drv)
959  return;
960 
961  auto tvp = drv->getText(propertyName);
962 
963  if (!tvp)
964  return;
965 
966  auto tp = tvp->findWidgetByName(elementName);
967 
968  if (!tp)
969  return;
970 
971  tp->setText(text);
972 
973  sendNewText(tvp);
974 }
975 
977 {
978  D_PTR(BaseClient);
979  nvp->s = IPS_BUSY;
980  IUUserIONewNumber(&io, d, nvp);
981 }
982 
983 void INDI::BaseClient::sendNewNumber(const char *deviceName, const char *propertyName, const char *elementName,
984  double value)
985 {
986  INDI::BaseDevice *drv = getDevice(deviceName);
987 
988  if (!drv)
989  return;
990 
991  auto nvp = drv->getNumber(propertyName);
992 
993  if (!nvp)
994  return;
995 
996  auto np = nvp->findWidgetByName(elementName);
997 
998  if (!np)
999  return;
1000 
1001  np->setValue(value);
1002 
1003  sendNewNumber(nvp);
1004 }
1005 
1007 {
1008  D_PTR(BaseClient);
1009  svp->s = IPS_BUSY;
1010  IUUserIONewSwitch(&io, d, svp);
1011 }
1012 
1013 void INDI::BaseClient::sendNewSwitch(const char *deviceName, const char *propertyName, const char *elementName)
1014 {
1015  INDI::BaseDevice *drv = getDevice(deviceName);
1016 
1017  if (!drv)
1018  return;
1019 
1020  auto svp = drv->getSwitch(propertyName);
1021 
1022  if (!svp)
1023  return;
1024 
1025  auto sp = svp->findWidgetByName(elementName);
1026 
1027  if (!sp)
1028  return;
1029 
1030  sp->setState(ISS_ON);
1031 
1032  sendNewSwitch(svp);
1033 }
1034 
1035 void INDI::BaseClient::startBlob(const char *devName, const char *propName, const char *timestamp)
1036 {
1037  D_PTR(BaseClient);
1038  IUUserIONewBLOBStart(&io, d, devName, propName, timestamp);
1039 }
1040 
1042 {
1043  D_PTR(BaseClient);
1045  &io, d,
1046  bp->name, bp->size, bp->bloblen, bp->blob, bp->format
1047  );
1048 }
1049 
1050 void INDI::BaseClient::sendOneBlob(const char *blobName, unsigned int blobSize, const char *blobFormat,
1051  void *blobBuffer)
1052 {
1053  D_PTR(BaseClient);
1055  &io, d,
1056  blobName, blobSize, blobSize, blobBuffer, blobFormat
1057  );
1058 }
1059 
1061 {
1062  D_PTR(BaseClient);
1063  IUUserIONewBLOBFinish(&io, d);
1064 }
1065 
1066 void INDI::BaseClient::setBLOBMode(BLOBHandling blobH, const char *dev, const char *prop)
1067 {
1068  D_PTR(BaseClient);
1069  if (!dev[0])
1070  return;
1071 
1072  BLOBMode *bMode = d->findBLOBMode(std::string(dev), (prop ? std::string(prop) : std::string()));
1073 
1074  if (bMode == nullptr)
1075  {
1076  BLOBMode newMode;
1077  newMode.device = std::string(dev);
1078  newMode.property = (prop ? std::string(prop) : std::string());
1079  newMode.blobMode = blobH;
1080  d->blobModes.push_back(std::move(newMode));
1081  }
1082  else
1083  {
1084  // If nothing changed, nothing to to do
1085  if (bMode->blobMode == blobH)
1086  return;
1087 
1088  bMode->blobMode = blobH;
1089  }
1090 
1091  IUUserIOEnableBLOB(&io, d, dev, prop, blobH);
1092 }
1093 
1094 BLOBHandling INDI::BaseClient::getBLOBMode(const char *dev, const char *prop)
1095 {
1096  D_PTR(BaseClient);
1097  BLOBHandling bHandle = B_ALSO;
1098 
1099  BLOBMode *bMode = d->findBLOBMode(dev, (prop ? std::string(prop) : std::string()));
1100 
1101  if (bMode)
1102  bHandle = bMode->blobMode;
1103 
1104  return bHandle;
1105 }
1106 
1107 bool INDI::BaseClient::getDevices(std::vector<INDI::BaseDevice *> &deviceList, uint16_t driverInterface )
1108 {
1109  D_PTR(BaseClient);
1110  for (INDI::BaseDevice *device : d->cDevices)
1111  {
1112  if (device->getDriverInterface() & driverInterface)
1113  deviceList.push_back(device);
1114  }
1115 
1116  return (deviceList.size() > 0);
1117 }
INDI::BaseClientPrivate::sendFd
int sendFd
Definition: baseclient_p.h:91
IUUserIOBLOBContextOne
void IUUserIOBLOBContextOne(const userio *io, void *user, const char *name, unsigned int size, unsigned int bloblen, const void *blob, const char *format)
Definition: indiuserio.c:100
INDI::BaseClient::connectDevice
void connectDevice(const char *deviceName)
Connect to INDI driver.
Definition: baseclient.cpp:900
INDI::BaseMediator::serverConnected
virtual void serverConnected()=0
Emmited when the server is connected.
INDI::BaseDevice::getText
INDI::PropertyView< IText > * getText(const char *name) const
Definition: basedevice.cpp:104
INDI::BaseClientPrivate::dispatchCommand
int dispatchCommand(XMLEle *root, char *errmsg)
Dispatch command received from INDI server to respective devices handled by the client.
Definition: baseclient.cpp:520
indistandardproperty.h
newLilXML
LilXML * newLilXML()
Create a new lilxml parser.
Definition: lilxml.c:148
IUUserIOGetProperties
void IUUserIOGetProperties(const userio *io, void *user, const char *dev, const char *name)
Definition: indiuserio.c:297
INDI::BaseClientPrivate::receiveFd
int receiveFd
Definition: baseclient_p.h:90
INDI::BaseClient::sendOneBlob
void sendOneBlob(IBLOB *bp)
Send ONE blob content to server. The BLOB data in raw binary format and will be converted to base64 a...
Definition: baseclient.cpp:1041
basedevice.h
INDI::BaseClientPrivate::sSocketBusy
std::mutex sSocketBusy
Definition: baseclient_p.h:103
INDI::BaseClientPrivate::sockfd
int sockfd
Definition: baseclient_p.h:89
INDI::BaseClientPrivate::sendData
size_t sendData(const void *data, size_t size)
Definition: baseclient.cpp:488
INDI::PropertyView::findWidgetByName
WidgetType * findWidgetByName(const char *name) const
Definition: indipropertyview.h:668
INDI::BaseDevice::buildProp
int buildProp(XMLEle *root, char *errmsg)
Build a property given the supplied XML element (defXXX)
Definition: basedevice.cpp:299
INDI::BaseClientPrivate::verbose
bool verbose
Definition: baseclient_p.h:106
INDI::BaseClientPrivate::cPort
uint32_t cPort
Definition: baseclient_p.h:100
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
INDI::BaseClientPrivate::timeout_sec
uint32_t timeout_sec
Definition: baseclient_p.h:110
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
INDI::BaseClientPrivate::cDeviceNames
std::set< std::string > cDeviceNames
Definition: baseclient_p.h:95
locale_compat.h
INDI::BaseClient::disconnectServer
bool disconnectServer(int exit_code=0)
Disconnect from INDI server.
Definition: baseclient.cpp:882
B_ONLY
@ B_ONLY
Definition: indidevapi.h:272
INDI::BaseClientPrivate::connect
bool connect()
Definition: baseclient.cpp:135
INDI::BaseClient::finishBlob
void finishBlob()
Send closing tag for BLOB command to server.
Definition: baseclient.cpp:1060
INDI::BaseDevice::getNumber
INDI::PropertyView< INumber > * getNumber(const char *name) const
Definition: basedevice.cpp:99
timestamp
const char * timestamp()
Create an ISO 8601 formatted time stamp. The format is YYYY-MM-DDTHH:MM:SS.
Definition: indicom.c:334
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
INDI::BaseClient::getPort
int getPort() const
Get the port number.
Definition: baseclient.cpp:935
indiuserio.h
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
INDI::BaseClient::BaseClient
BaseClient()
Definition: baseclient.cpp:828
INDI::BaseDevice::setMediator
void setMediator(INDI::BaseMediator *mediator)
Set the driver's mediator to receive notification of news devices and updated property values.
Definition: basedevice.cpp:1019
INDI::BaseClientPrivate::findDev
INDI::BaseDevice * findDev(const char *devName, char *errmsg)
Find and return a particular device.
Definition: baseclient.cpp:647
INDI::BaseClient::setVerbose
void setVerbose(bool enable)
setVerbose Set verbose mode
Definition: baseclient.cpp:837
INDI::BaseDevice::getSwitch
INDI::PropertyView< ISwitch > * getSwitch(const char *name) const
Definition: basedevice.cpp:109
INDI::BaseClientPrivate::listenINDI
void listenINDI()
Definition: baseclient.cpp:324
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
IUUserIONewBLOBFinish
void IUUserIONewBLOBFinish(const userio *io, void *user)
Definition: indiuserio.c:271
IUUserIONewNumber
void IUUserIONewNumber(const userio *io, void *user, const INumberVectorProperty *nvp)
Definition: indiuserio.c:189
INDI::BaseClient::getDevices
const std::vector< INDI::BaseDevice * > & getDevices() const
Definition: baseclient.cpp:923
INDI::BaseClient::setConnectionTimeout
void setConnectionTimeout(uint32_t seconds, uint32_t microseconds)
setConnectionTimeout Set connection timeout. By default it is 3 seconds.
Definition: baseclient.cpp:849
max
double max(void)
INDI::BaseClient::isVerbose
bool isVerbose() const
isVerbose Is client in verbose mode?
Definition: baseclient.cpp:843
INDI::BaseClientPrivate::sConnected
std::atomic_bool sConnected
Definition: baseclient_p.h:101
IDLog
void void void void void IDLog(const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(1
Function Drivers call to log a message locally.
INDI::Property
Provides generic container for INDI properties.
Definition: indiproperty.h:43
INDI::BaseClientPrivate
Definition: baseclient_p.h:35
INDI::BaseClient::watchDevice
void watchDevice(const char *deviceName)
Add a device to the watch list.
Definition: baseclient.cpp:863
net_close
#define net_close
Definition: baseclient.cpp:58
_ITextVectorProperty
Text vector property descriptor.
Definition: indiapi.h:244
INDI::BaseDevice::getProperty
Property getProperty(const char *name, INDI_PROPERTY_TYPE type=INDI_UNKNOWN) const
Return a property and its type given its name.
Definition: basedevice.cpp:148
INDI::BaseClient::sendNewNumber
void sendNewNumber(INumberVectorProperty *pp)
Send new Number command to server.
Definition: baseclient.cpp:976
INDI::BaseClientPrivate::timeout_us
uint32_t timeout_us
Definition: baseclient_p.h:110
device
hid_device * device
Definition: activefocuser_utils.cpp:92
INDI::BaseClientPrivate::BaseClientPrivate
BaseClientPrivate(BaseClient *parent)
Definition: baseclient.cpp:75
MAXINDIBUF
#define MAXINDIBUF
Definition: baseclient.cpp:65
INDI::BaseClientPrivate::cWatchProperties
std::map< std::string, std::set< std::string > > cWatchProperties
Definition: baseclient_p.h:97
INDI::BaseClientPrivate::parent
BaseClient * parent
Definition: baseclient_p.h:84
_INumberVectorProperty
Number vector property descriptor.
Definition: indiapi.h:317
delLilXML
void delLilXML(LilXML *lp)
Delete a lilxml parser.
Definition: lilxml.c:157
INDI::BaseDevice::setDeviceName
void setDeviceName(const char *dev)
Set the device name.
Definition: basedevice.cpp:793
INDI::BaseClientPrivate::delPropertyCmd
int delPropertyCmd(XMLEle *root, char *errmsg)
Delete property command.
Definition: baseclient.cpp:608
IUUserIONewText
void IUUserIONewText(const userio *io, void *user, const ITextVectorProperty *tvp)
Definition: indiuserio.c:206
INDI::BaseClientPrivate::findBLOBMode
BLOBMode * findBLOBMode(const std::string &device, const std::string &property)
Definition: baseclient.cpp:771
INDI::BaseClient::newUniversalMessage
virtual void newUniversalMessage(std::string message)
newUniversalMessage Universal messages are sent from INDI server without a specific device....
Definition: baseclient.cpp:941
B_ALSO
@ B_ALSO
Definition: indidevapi.h:271
xml_att_
Definition: lilxml.c:120
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
base64.h
INDI::BaseClientPrivate::cDevices
std::vector< INDI::BaseDevice * > cDevices
Definition: baseclient_p.h:94
INDI::BaseClientPrivate::messageCmd
int messageCmd(XMLEle *root, char *errmsg)
Definition: baseclient.cpp:727
INDI_DEVICE_NOT_FOUND
@ INDI_DEVICE_NOT_FOUND
Definition: indibasetypes.h:57
INDI::BaseClient::serverDisconnected
void serverDisconnected(int exit_code) override
Emmited when the server gets disconnected.
Definition: baseclient.cpp:889
INDI::BaseClient::getDevice
INDI::BaseDevice * getDevice(const char *deviceName)
Definition: baseclient.cpp:912
INDI::BaseClientPrivate::cServer
std::string cServer
Definition: baseclient_p.h:99
tagXMLEle
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition: lilxml.c:569
xml_ele_
Definition: lilxml.c:105
BLOBHandling
BLOBHandling
How drivers handle BLOBs incoming from snooping drivers.
Definition: indidevapi.h:268
IUUserIONewSwitch
void IUUserIONewSwitch(const userio *io, void *user, const ISwitchVectorProperty *svp)
Definition: indiuserio.c:230
INDI::BaseDevice::checkMessage
void checkMessage(XMLEle *root)
Definition: basedevice.cpp:820
INDI::BaseMediator::removeDevice
virtual void removeDevice(INDI::BaseDevice *dp)=0
Emmited when a device is deleted from INDI server.
INDI::BaseClient::setBLOBMode
void setBLOBMode(BLOBHandling blobH, const char *dev, const char *prop=nullptr)
Set Binary Large Object policy mode.
Definition: baseclient.cpp:1066
INDI::BaseClient::isServerConnected
bool isServerConnected() const
Get status of the connection.
Definition: baseclient.cpp:894
IUUserIOEnableBLOB
void IUUserIOEnableBLOB(const userio *io, void *user, const char *dev, const char *name, BLOBHandling blobH)
Definition: indiuserio.c:331
INDI::BaseClientPrivate::disconnect
bool disconnect(int exit_code)
Definition: baseclient.cpp:295
INDI::BaseClientPrivate::blobModes
std::list< BLOBMode > blobModes
Definition: baseclient_p.h:96
INDI::BLOBMode::property
std::string property
Definition: baseclient_p.h:31
INDI::BaseClient::getHost
const char * getHost() const
Get the server host name.
Definition: baseclient.cpp:929
INDI::BaseClient
Class to provide basic client functionality.
Definition: baseclient.h:54
INDI::BaseClient::sendNewSwitch
void sendNewSwitch(ISwitchVectorProperty *pp)
Send new Switch command to server.
Definition: baseclient.cpp:1006
INDI::BaseClientPrivate::~BaseClientPrivate
virtual ~BaseClientPrivate()
Definition: baseclient.cpp:99
INDI::BaseDevice::setValue
int setValue(XMLEle *root, char *errmsg)
handle SetXXX commands from client
Definition: basedevice.cpp:532
INDI::BaseMediator::removeProperty
virtual void removeProperty(INDI::Property *property)=0
Emmited when a property is deleted for an INDI driver.
INDI::BaseClient::getBLOBMode
BLOBHandling getBLOBMode(const char *dev, const char *prop=nullptr)
getBLOBMode Get Binary Large Object policy mode IF set previously by setBLOBMode
Definition: baseclient.cpp:1094
baseclient_p.h
INDI::BLOBMode::device
std::string device
Definition: baseclient_p.h:30
INDI::BaseClient::~BaseClient
virtual ~BaseClient()
Definition: baseclient.cpp:832
INDI::BaseClientPrivate::sendString
void sendString(const char *fmt,...)
Definition: baseclient.cpp:509
INDI::BaseDevice::removeProperty
int removeProperty(const char *name, char *errmsg)
Remove a property.
Definition: basedevice.cpp:180
INDI::BaseClientPrivate::sExitCode
int sExitCode
Definition: baseclient_p.h:105
net_write
#define net_write
Definition: baseclient.cpp:57
parseXMLChunk
XMLEle ** parseXMLChunk(LilXML *lp, char *buf, int size, char ynot[])
Process an XML chunk.
Definition: lilxml.c:213
INDI::BaseClient::sendNewText
void sendNewText(ITextVectorProperty *pp)
Send new Text command to server.
Definition: baseclient.cpp:946
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
INDI::BaseClientPrivate::clear
void clear()
clear Clear devices and blob modes
Definition: baseclient.cpp:123
valuXMLAtt
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition: lilxml.c:593
_ITextVectorProperty::s
IPState s
Definition: indiapi.h:259
INDI
Namespace to encapsulate INDI client, drivers, and mediator classes.
Definition: AlignmentSubsystemForClients.cpp:11
baseclient.h
INDI::BaseClient::disconnectDevice
void disconnectDevice(const char *deviceName)
Disconnect INDI driver.
Definition: baseclient.cpp:906
prXMLEle
void prXMLEle(FILE *fp, XMLEle *ep, int level)
Print an XML element.
Definition: lilxml.c:699
userio::write
size_t(* write)(void *user, const void *ptr, size_t count)
Definition: userio.h:34
INDI::BaseClientPrivate::sSocketChanged
std::condition_variable sSocketChanged
Definition: baseclient_p.h:104
userio_file
const struct userio * userio_file()
Definition: userio.c:38
verbose
int verbose
Definition: indidriver.c:49
findXMLAttValu
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition: lilxml.c:613
INDI::BaseClient::startBlob
void startBlob(const char *devName, const char *propName, const char *timestamp)
Send opening tag for BLOB command to server.
Definition: baseclient.cpp:1035
INDI::BaseClientPrivate::addDevice
INDI::BaseDevice * addDevice(XMLEle *dep, char *errmsg)
Add a new device.
Definition: baseclient.cpp:662
INDI_DISPATCH_ERROR
@ INDI_DISPATCH_ERROR
Definition: indibasetypes.h:60
LilXML_
Definition: lilxml.c:91
INDI::BLOBMode
Definition: baseclient_p.h:28
userio
Definition: userio.h:32
INDI::BaseClientPrivate::sAboutToClose
std::atomic_bool sAboutToClose
Definition: baseclient_p.h:102
INDI::BaseClientPrivate::deleteDevice
int deleteDevice(const char *devName, char *errmsg)
Remove device.
Definition: baseclient.cpp:583
INDI::SP::CONNECTION
const char * CONNECTION
Connect to and disconnect from device.
Definition: indistandardproperty.cpp:63
INDI::BaseClient::setServer
void setServer(const char *hostname, unsigned int port)
Set the server host name and port.
Definition: baseclient.cpp:856
IUUserIONewBLOBStart
void IUUserIONewBLOBStart(const userio *io, void *user, const char *dev, const char *name, const char *timestamp)
Definition: indiuserio.c:250
INDI::BaseClient::watchProperty
void watchProperty(const char *deviceName, const char *propertyName)
watchProperties Add a property to the watch list. When communicating with INDI server.
Definition: baseclient.cpp:869
INDI::PropertyView::reset
void reset()
Definition: indipropertyview.h:125
INDI_PROPERTY_DUPLICATED
@ INDI_PROPERTY_DUPLICATED
Definition: indibasetypes.h:59
INDI::BaseMediator::newDevice
virtual void newDevice(INDI::BaseDevice *dp)=0
Emmited when a new device is created from INDI server.
delXMLEle
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
Definition: lilxml.c:165
INDI::BLOBMode::blobMode
BLOBHandling blobMode
Definition: baseclient_p.h:32
INDI::BaseClientPrivate::setDriverConnection
void setDriverConnection(bool status, const char *deviceName)
Connect/Disconnect to INDI driver.
Definition: baseclient.cpp:782
errno
int errno
INDI::BaseDevice
Class to provide basic INDI device functionality.
Definition: basedevice.h:45
IBLOB
One Blob (Binary Large Object) descriptor.
findXMLAtt
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition: lilxml.c:493
userio::vprintf
int(* vprintf)(void *user, const char *format, va_list arg)
Definition: userio.h:35
Aux::buffer
std::vector< uint8_t > buffer
Definition: celestronauxpacket.h:38
_ISwitchVectorProperty
Switch vector property descriptor.
Definition: indiapi.h:365
INDI::BaseClient::connectServer
bool connectServer()
Connect to INDI server.
Definition: baseclient.cpp:876
ISS_ON
@ ISS_ON
Definition: indiapi.h:151