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