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