Kstars

indidriver.cpp
1/*
2 SPDX-FileCopyrightText: 2001 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "indidriver.h"
8
9#include "config-kstars.h"
10
11#include "devicemanager.h"
12#include "indidevice.h"
13#include "indimenu.h"
14#include "kstars.h"
15#include "kstarsdata.h"
16#include "ksutils.h"
17#include "Options.h"
18#include "ui_indihostconf.h"
19#include "oal/log.h"
20#include "oal/scope.h"
21
22#include <KMessageBox>
23#include <KProcess>
24#include <KActionCollection>
25
26#include <QRadioButton>
27#include <QFile>
28#include <QDir>
29#include <QTextStream>
30#include <QTreeWidget>
31#include <QIcon>
32#include <QDialog>
33#include <QTcpServer>
34#include <QMenu>
35#include <QPushButton>
36#include <QLineEdit>
37#include <QAction>
38#include <QStandardPaths>
39
40#include <unistd.h>
41#include <sys/socket.h>
42#include <netinet/in.h>
43#include <netdb.h>
44
45#define MAX_RETRIES 3
46#define ERRMSG_SIZE 1024
47
48DeviceManagerUI::DeviceManagerUI(QWidget *parent) : QFrame(parent)
49{
50#ifdef Q_OS_MACOS
51 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
52#endif
53 setupUi(this);
54
55 localTreeWidget->setSortingEnabled(false);
56 localTreeWidget->setRootIsDecorated(true);
57
58 clientTreeWidget->setSortingEnabled(false);
59
60 runningPix = QIcon::fromTheme("system-run");
61 stopPix = QIcon::fromTheme("dialog-cancel");
62 localMode = QIcon::fromTheme("computer");
63 serverMode = QIcon::fromTheme("network-server");
64
65 connected = QIcon::fromTheme("network-connect");
66 disconnected = QIcon::fromTheme("network-disconnect");
67
68 connect(localTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this,
69 SLOT(makePortEditable(QTreeWidgetItem*,int)));
70}
71
72void DeviceManagerUI::makePortEditable(QTreeWidgetItem *selectedItem, int column)
73{
74 // If it's the port column, then make it user-editable
75 if (column == INDIDriver::LOCAL_PORT_COLUMN)
77
78 localTreeWidget->editItem(selectedItem, INDIDriver::LOCAL_PORT_COLUMN);
79}
80
81INDIDriver::INDIDriver(KStars *_ks) : QDialog(_ks), ksw(_ks)
82{
83 currentPort = Options::serverPortStart().toInt() - 1;
84 lastGroup = nullptr;
85 lastDevice = nullptr;
86
87 ui = new DeviceManagerUI(this);
88 setMainWidget(ui);
89 setCaption(xi18n("Device Manager"));
90 setButtons(QDialog::Close);
91
92 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
93 {
94 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget, lastGroup);
95 lastGroup = item;
96 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
97 item->setText(HOST_NAME_COLUMN, host->name);
98 item->setText(HOST_PORT_COLUMN, host->portnumber);
99 }
100
101 lastGroup = nullptr;
102
103 QObject::connect(ui->addB, SIGNAL(clicked()), this, SLOT(addINDIHost()));
104 QObject::connect(ui->modifyB, SIGNAL(clicked()), this, SLOT(modifyINDIHost()));
105 QObject::connect(ui->removeB, SIGNAL(clicked()), this, SLOT(removeINDIHost()));
106
107 //QObject::connect(ksw->indiMenu(), SIGNAL(driverDisconnected(int)), this, SLOT(shutdownHost(int)));
108 QObject::connect(ui->connectHostB, SIGNAL(clicked()), this, SLOT(activateHostConnection()));
109 QObject::connect(ui->disconnectHostB, SIGNAL(clicked()), this, SLOT(activateHostDisconnection()));
110 QObject::connect(ui->runServiceB, SIGNAL(clicked()), this, SLOT(activateRunService()));
111 QObject::connect(ui->stopServiceB, SIGNAL(clicked()), this, SLOT(activateStopService()));
112 QObject::connect(ui->localTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(updateLocalTab()));
113 QObject::connect(ui->clientTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(updateClientTab()));
114 QObject::connect(ui->localTreeWidget, SIGNAL(expanded(QModelIndex)), this, SLOT(resizeDeviceColumn()));
115
116 readXMLDrivers();
117}
118
119/*
120void INDIDriver::enableDevice(INDI_D *indi_device)
121{
122 if (indi_device == nullptr)
123 return;
124
125 if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT)
126 {
127 foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
128 {
129
130 if (host->deviceManager == indi_device->deviceManager && host->isConnected == false)
131 {
132 foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly, 1))
133 item->setIcon(HOST_STATUS_COLUMN, ui->connected);
134
135 host->isConnected = true;
136 updateClientTab();
137 updateMenuActions();
138 ksw->indiMenu()->show();
139 return;
140 }
141 }
142 }
143 else
144 {
145 foreach (IDevice *device, devices)
146 {
147 if (device->unique_label == indi_device->label)
148 {
149 foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly | Qt::MatchRecursive))
150 {
151 item->setIcon(LOCAL_STATUS_COLUMN, ui->runningPix);
152 item->setText(LOCAL_PORT_COLUMN, QString::number(indi_device->deviceManager->port));
153 }
154
155 updateLocalTab();
156 updateMenuActions();
157 ksw->indiMenu()->show();
158 return;
159 }
160 }
161 }
162
163}
164
165void INDIDriver::disableDevice(INDI_D *indi_device)
166{
167 if (indi_device == nullptr)
168 return;
169
170 if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT)
171 {
172 foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
173 {
174 if (host->deviceManager == indi_device->deviceManager && host->isConnected == true)
175 {
176 foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly,1))
177 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
178
179 host->deviceManager = nullptr;
180 host->isConnected = false;
181
182 updateClientTab();
183 return;
184 }
185 }
186 }
187 else
188 {
189 foreach (IDevice *device, devices)
190 {
191 if (device->unique_label == indi_device->label)
192 {
193 foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly | Qt::MatchRecursive))
194 {
195 item->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
196 item->setIcon(LOCAL_MODE_COLUMN, QIcon());
197 item->setText(LOCAL_PORT_COLUMN, device->port);
198 }
199
200 device->clear();
201 updateLocalTab();
202
203 return;
204 }
205 }
206 }
207 }
208 */
209void INDIDriver::activateRunService()
210{
211 processLocalTree(IDevice::DEV_START);
212}
213
214void INDIDriver::activateStopService()
215{
216 processLocalTree(IDevice::DEV_TERMINATE);
217}
218
219void INDIDriver::activateHostConnection()
220{
221 processRemoteTree(IDevice::DEV_START);
222}
223
224void INDIDriver::activateHostDisconnection()
225{
226 processRemoteTree(IDevice::DEV_TERMINATE);
227}
228
229void INDIDriver::updateLocalTab()
230{
231 if (ui->localTreeWidget->currentItem() == nullptr)
232 return;
233
234 foreach (IDevice *device, devices)
235 {
236 if (ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN) == device->tree_label)
237 {
238 ui->runServiceB->setEnabled(device->state == IDevice::DEV_TERMINATE);
239 ui->stopServiceB->setEnabled(device->state == IDevice::DEV_START);
240
241 ui->serverLogText->clear();
242 ui->serverLogText->append(device->getServerBuffer());
243
244 return;
245 }
246 }
247}
248
249void INDIDriver::updateClientTab()
250{
251 if (ui->clientTreeWidget->currentItem() == nullptr)
252 return;
253
254 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
255 {
256 if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == host->name &&
257 ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) == host->portnumber)
258 {
259 ui->connectHostB->setEnabled(!host->isConnected);
260 ui->disconnectHostB->setEnabled(host->isConnected);
261 break;
262 }
263 }
264}
265
266void INDIDriver::processLocalTree(IDevice::DeviceStatus dev_request)
267{
268 QList<IDevice *> processed_devices;
269 DeviceManager *deviceManager = nullptr;
270 int port = 0;
271 bool portOK = false;
272
273 foreach (QTreeWidgetItem *item, ui->localTreeWidget->selectedItems())
274 {
275 foreach (IDevice *device, devices)
276 {
277 //device->state = (dev_request == IDevice::DEV_TERMINATE) ? IDevice::DEV_START : IDevice::DEV_TERMINATE;
278 if (item->text(LOCAL_NAME_COLUMN) == device->tree_label && device->state != dev_request)
279 {
280 processed_devices.append(device);
281
282 // N.B. If multiple devices are selected to run under one device manager
283 // then we select the port for the first device that has a valid port
284 // entry, the rest are ignored.
285 if (port == 0 && item->text(LOCAL_PORT_COLUMN).isEmpty() == false)
286 {
287 port = item->text(LOCAL_PORT_COLUMN).toInt(&portOK);
288 // If we encounter conversion error, we abort
289 if (portOK == false)
290 {
291 KMessageBox::error(0, xi18n("Invalid port entry: %1", item->text(LOCAL_PORT_COLUMN)));
292 return;
293 }
294 }
295 }
296 }
297 }
298
299 if (processed_devices.empty())
300 return;
301
302 // Select random port within range is none specified.
303 port = getINDIPort(port);
304
305 if (dev_request == IDevice::DEV_START)
306 {
307 if (port <= 0)
308 {
309 KMessageBox::error(0, xi18n("Cannot start INDI server: port error."));
310 return;
311 }
312
313 //deviceManager = ksw->indiMenu()->startDeviceManager(processed_devices, ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER, ((uint) port));
314 deviceManager = ksw->indiMenu()->initDeviceManager(
315 "localhost", ((uint)port), ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER);
316
317 if (deviceManager == nullptr)
318 {
319 kWarning() << "Warning: device manager has not been established properly";
320 return;
321 }
322
323 deviceManager->appendManagedDevices(processed_devices);
324 deviceManager->startServer();
325 connect(deviceManager, SIGNAL(newServerInput()), this, SLOT(updateLocalTab()));
326 }
327 else
328 ksw->indiMenu()->stopDeviceManager(processed_devices);
329}
330
331void INDIDriver::processRemoteTree(IDevice::DeviceStatus dev_request)
332{
333 DeviceManager *deviceManager = nullptr;
334 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
335 if (!currentItem)
336 return;
337 bool toConnect = (dev_request == IDevice::DEV_START);
338
339 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
340 {
341 if (currentItem->text(HOST_NAME_COLUMN) == host->name &&
342 currentItem->text(HOST_PORT_COLUMN) == host->portnumber)
343 {
344 // Nothing changed, return
345 if (host->isConnected == toConnect)
346 return;
347
348 // connect to host
349 if (toConnect)
350 {
351 deviceManager = ksw->indiMenu()->initDeviceManager(host->hostname, host->portnumber.toUInt(),
352 DeviceManager::M_CLIENT);
353
354 if (deviceManager == nullptr)
355 {
356 // msgbox after freeze
357 kWarning() << "Warning: device manager has not been established properly";
358 return;
359 }
360
361 host->deviceManager = deviceManager;
362 deviceManager->connectToServer();
363 }
364 else
365 ksw->indiMenu()->removeDeviceManager(host->deviceManager);
366
367 return;
368 }
369 }
370}
371
372void INDIDriver::newTelescopeDiscovered()
373{
374 emit newTelescope();
375}
376
377void INDIDriver::newCCDDiscovered()
378{
379 emit newCCD();
380}
381
382void INDIDriver::resizeDeviceColumn()
383{
384 ui->localTreeWidget->resizeColumnToContents(0);
385}
386
387void INDIDriver::updateMenuActions()
388{
389 // We iterate over devices, we enable INDI Control Panel if we have any active device
390 // We enable capture image sequence if we have any imaging device
391
392 QAction *tmpAction = nullptr;
393 INDIMenu *devMenu = ksw->indiMenu();
394 bool activeDevice = false;
395 bool activeImaging = false;
396 INDI_P *imgProp = nullptr;
397
398 if (devMenu == nullptr)
399 return;
400
401 if (devMenu->managers.count() > 0)
402 activeDevice = true;
403
404 foreach (DeviceManager *dev_managers, devMenu->managers)
405 {
406 foreach (INDI_D *device, dev_managers->indi_dev)
407 {
408 imgProp = device->findProp("CCD_EXPOSURE");
409 if (imgProp && device->isOn())
410 {
411 activeImaging = true;
412 break;
413 }
414 }
415 }
416
417 tmpAction = ksw->actionCollection()->action("capture_sequence");
418
419 if (tmpAction != nullptr)
420 tmpAction->setEnabled(activeImaging);
421
422 tmpAction = ksw->actionCollection()->action("indi_cpl");
423 if (tmpAction != nullptr)
424 tmpAction->setEnabled(activeDevice);
425}
426
427bool INDIDriver::isDeviceRunning(const QString &deviceLabel)
428{
429 foreach (IDevice *dev, devices)
430 {
431 if (deviceLabel == dev->tree_label)
432 return dev->state == IDevice::DEV_START;
433 }
434 return false;
435}
436
437int INDIDriver::getINDIPort(int customPort)
438{
439 int lastPort = Options::serverPortEnd().toInt();
440 ;
441 bool success = false;
442 currentPort++;
443
444 // recycle
445 if (currentPort > lastPort)
446 currentPort = Options::serverPortStart().toInt();
447
448 QTcpServer temp_server;
449
450 if (customPort != 0)
451 {
452 success = temp_server.listen(QHostAddress::LocalHost, customPort);
453 if (success)
454 {
455 temp_server.close();
456 return customPort;
457 }
458 else
459 return -1;
460 }
461
462 for (; currentPort <= lastPort; currentPort++)
463 {
464 success = temp_server.listen(QHostAddress::LocalHost, currentPort);
465 if (success)
466 {
467 temp_server.close();
468 return currentPort;
469 }
470 }
471 return -1;
472}
473
474bool INDIDriver::readXMLDrivers()
475{
476 QDir indiDir;
477 QString driverName;
478
479 QString driversDir = Options::indiDriversDir();
480#ifdef Q_OS_MACOS
481 if (Options::indiDriversAreInternal())
482 QCoreApplication::applicationDirPath() + "/../Resources/DriverSupport";
483#endif
484
485 if (indiDir.cd(driversDir) == false)
486 {
487 KMessageBox::error(0, xi18n("Unable to find INDI drivers directory: %1\nPlease make sure to set the correct "
488 "path in KStars configuration",
489 driversDir));
490 return false;
491 }
492
493 indiDir.setNameFilters(QStringList("*.xml"));
495 QFileInfoList list = indiDir.entryInfoList();
496
497 foreach (QFileInfo fileInfo, list)
498 {
499 // libindi 0.7.1: Skip skeleton files
500 if (fileInfo.fileName().endsWith(QLatin1String("_sk.xml")))
501 continue;
502
503 if (fileInfo.fileName() == "drivers.xml")
504 {
505 // Let first attempt to load the local version of drivers.xml
506 driverName = QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("drivers.xml");
507
508 // If found, we continue, otherwise, we load the system file
509 if (driverName.isEmpty() == false && QFile(driverName).exists())
510 {
511 processXMLDriver(driverName);
512 continue;
513 }
514 }
515
516 driverName = QString("%1/%2").arg(driversDir).arg(fileInfo.fileName());
517 processXMLDriver(driverName);
518 }
519
520 return true;
521}
522
523void INDIDriver::processXMLDriver(QString &driverName)
524{
525 QFile file(driverName);
526 if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
527 {
528 KMessageBox::error(0, xi18n("Failed to open INDI Driver file: %1", driverName));
529 return;
530 }
531
532 char errmsg[ERRMSG_SIZE];
533 char c;
534 LilXML *xmlParser = newLilXML();
535 XMLEle *root = nullptr;
536
537 if (driverName.endsWith(QLatin1String("drivers.xml")))
538 xmlSource = IDevice::PRIMARY_XML;
539 else
540 xmlSource = IDevice::THIRD_PARTY_XML;
541
542 while (file.getChar(&c))
543 {
544 root = readXMLEle(xmlParser, c, errmsg);
545
546 if (root)
547 {
548 if (!buildDeviceGroup(root, errmsg))
549 prXMLEle(stderr, root, 0);
550
551 delXMLEle(root);
552 }
553 else if (errmsg[0])
554 {
555 kDebug() << QString(errmsg) << endl;
556 delLilXML(xmlParser);
557 return;
558 }
559 }
560
561 delLilXML(xmlParser);
562}
563
564bool INDIDriver::buildDeviceGroup(XMLEle *root, char errmsg[])
565{
566 XMLAtt *ap;
567 XMLEle *ep;
568 QString groupName;
569 QTreeWidgetItem *group;
570 int groupType = KSTARS_TELESCOPE;
571
572 // avoid overflow
573 if (strlen(tagXMLEle(root)) > 1024)
574 return false;
575
576 // Get device grouping name
577 ap = findXMLAtt(root, "group");
578
579 if (!ap)
580 {
581 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute", tagXMLEle(root));
582 return false;
583 }
584
585 groupName = valuXMLAtt(ap);
586
587 if (groupName.indexOf("Telescopes") != -1)
588 groupType = KSTARS_TELESCOPE;
589 else if (groupName.indexOf("CCDs") != -1)
590 groupType = KSTARS_CCD;
591 else if (groupName.indexOf("Filter") != -1)
592 groupType = KSTARS_FILTER;
593 else if (groupName.indexOf("Video") != -1)
594 groupType = KSTARS_VIDEO;
595 else if (groupName.indexOf("Focusers") != -1)
596 groupType = KSTARS_FOCUSER;
597 else if (groupName.indexOf("Domes") != -1)
598 groupType = KSTARS_DOME;
599 else if (groupName.indexOf("Receivers") != -1)
600 groupType = KSTARS_RECEIVERS;
601 else if (groupName.indexOf("GPS") != -1)
602 groupType = KSTARS_GPS;
603
604#ifndef HAVE_CFITSIO_H
605 // We do not create these groups if we don't have CFITSIO support
606 if (groupType == KSTARS_CCD || groupType == KSTARS_VIDEO)
607 return true;
608#endif
609
610 // Find if the group already exists
611 QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems(groupName, Qt::MatchExactly);
612 if (!treeList.isEmpty())
613 group = treeList[0];
614 else
615 group = new QTreeWidgetItem(ui->localTreeWidget, lastGroup);
616
617 group->setText(0, groupName);
618 lastGroup = group;
619
620 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
621 if (!buildDriverElement(ep, group, groupType, errmsg))
622 return false;
623
624 return true;
625}
626
627bool INDIDriver::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup, int groupType, char errmsg[])
628{
629 XMLAtt *ap;
630 XMLEle *el;
631 IDevice *dv;
633 QString driver;
636 QString port;
637 double focal_length(-1), aperture(-1);
638
639 ap = findXMLAtt(root, "label");
640 if (!ap)
641 {
642 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute", tagXMLEle(root));
643 return false;
644 }
645
646 label = valuXMLAtt(ap);
647
648 // Search for optional port attribute
649 ap = findXMLAtt(root, "port");
650 if (ap)
651 port = valuXMLAtt(ap);
652
653 // Let's look for telescope-specific attributes: focal length and aperture
654 ap = findXMLAtt(root, "focal_length");
655 if (ap)
656 focal_length = QString(valuXMLAtt(ap)).toDouble();
657
658 ap = findXMLAtt(root, "aperture");
659 if (ap)
660 aperture = QString(valuXMLAtt(ap)).toDouble();
661
662 el = findXMLEle(root, "driver");
663
664 if (!el)
665 return false;
666
667 driver = pcdataXMLEle(el);
668
669 ap = findXMLAtt(el, "name");
670 if (!ap)
671 {
672 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a name attribute", tagXMLEle(el));
673 return false;
674 }
675
676 name = valuXMLAtt(ap);
677
678 el = findXMLEle(root, "version");
679
680 if (!el)
681 return false;
682
683 version = pcdataXMLEle(el);
684
685 QTreeWidgetItem *device = new QTreeWidgetItem(DGroup, lastDevice);
686
687 device->setText(LOCAL_NAME_COLUMN, label);
688 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
689 device->setText(LOCAL_VERSION_COLUMN, version);
690 device->setText(LOCAL_PORT_COLUMN, port);
691
692 lastDevice = device;
693
694 if ((xmlSource == IDevice::PRIMARY_XML) && driversList.contains(driver) == false)
695 driversList.insert(driver, name);
696
697 dv = new IDevice(name, label, driver, version);
698 dv->type = groupType;
699 dv->xmlSource = xmlSource;
700 //connect(dv, SIGNAL(newServerInput()), this, SLOT(updateLocalTab()));
701 if (focal_length > 0)
702 dv->focal_length = focal_length;
703 if (aperture > 0)
704 dv->aperture = aperture;
705 dv->port = port;
706
707 devices.append(dv);
708
709 // SLOTS/SIGNAL, pop menu, indi server logic
710 return true;
711}
712
713void INDIDriver::updateCustomDrivers()
714{
716 QString driver;
719 IDevice *dv;
720 QTreeWidgetItem *group, *widgetDev;
721 double focal_length(-1), aperture(-1);
722
723 // Find if the group already exists
724 QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems("Telescopes", Qt::MatchExactly);
725 if (!treeList.isEmpty())
726 group = treeList[0];
727 else
728 return;
729
730 // Find custom telescope to ADD
731 foreach (OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList()))
732 {
733 label = s->vendor() + ' ' + s->model();
734
735 if (s->driver() == xi18n("None") || findDeviceByLabel(label))
736 continue;
737
738 QHash<QString, QString>::const_iterator i = driversList.constFind(s->driver());
739 if (i != driversList.constEnd() && i.key() == s->driver())
740 name = i.value();
741 else
742 return;
743 focal_length = s->focalLength();
744 aperture = s->aperture();
745 driver = s->driver();
746 version = QString("1.0");
747
748 QTreeWidgetItem *device = new QTreeWidgetItem(group, lastDevice);
749 device->setText(LOCAL_NAME_COLUMN, QString(label));
750 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
751 device->setText(LOCAL_VERSION_COLUMN, QString(version));
752
753 lastDevice = device;
754
755 dv = new IDevice(name, label, driver, version);
756 dv->type = KSTARS_TELESCOPE;
757 dv->xmlSource = IDevice::EM_XML;
758 dv->focal_length = focal_length;
759 dv->aperture = aperture;
760 dv->id = s->id();
761 devices.append(dv);
762 }
763
764 // Find custom telescope to REMOVE
765 foreach (IDevice *dev, devices)
766 {
767 // If it's from primary xml file or it is in a running state, continue.
768 if (dev->xmlSource != IDevice::EM_XML || dev->state == IDevice::DEV_START)
769 continue;
770
771 // See if log object has the device, if not delete it
772 if (KStarsData::Instance()->logObject()->findScopeById(dev->id))
773 continue;
774
775 // Find if the group already exists
777 ui->localTreeWidget->findItems(dev->tree_label, Qt::MatchExactly | Qt::MatchRecursive);
778 if (!devList.isEmpty())
779 {
780 widgetDev = devList[0];
781 group->removeChild(widgetDev);
782 }
783 else
784 return;
785
786 devices.removeOne(dev);
787 delete (dev);
788 }
789}
790
791void INDIDriver::addINDIHost()
792{
793 QDialog hostConfDialog;
794 Ui::INDIHostConf hostConf;
795 hostConf.setupUi(&hostConfDialog);
796 hostConfDialog.setWindowTitle(xi18n("Add Host"));
797 bool portOk = false;
798
799 if (hostConfDialog.exec() == QDialog::Accepted)
800 {
801 INDIHostsInfo *hostItem = new INDIHostsInfo;
802 hostItem->name = hostConf.nameIN->text();
803 hostItem->hostname = hostConf.hostname->text();
804 hostItem->portnumber = hostConf.portnumber->text();
805 hostItem->isConnected = false;
806 hostItem->deviceManager = nullptr;
807
808 hostItem->portnumber.toInt(&portOk);
809
810 if (portOk == false)
811 {
812 KMessageBox::error(0, xi18n("Error: the port number is invalid."));
813 delete hostItem;
814 return;
815 }
816
817 //search for duplicates
818 //for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++)
819 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
820 if (hostItem->name == host->name && hostItem->portnumber == host->portnumber)
821 {
822 KMessageBox::error(0, xi18n("Host: %1 Port: %2 already exists.", hostItem->name, hostItem->portnumber));
823 delete hostItem;
824 return;
825 }
826
827 ksw->data()->INDIHostsList.append(hostItem);
828
829 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget);
830 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
831 item->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
832 item->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
833 }
834
835 saveHosts();
836}
837
838void INDIDriver::modifyINDIHost()
839{
840 QDialog hostConfDialog;
841 Ui::INDIHostConf hostConf;
842 hostConf.setupUi(&hostConfDialog);
843 hostConfDialog.setWindowTitle(xi18n("Modify Host"));
844
845 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
846
847 if (currentItem == nullptr)
848 return;
849
850 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
851 {
852 if (currentItem->text(HOST_NAME_COLUMN) == host->name &&
853 currentItem->text(HOST_PORT_COLUMN) == host->portnumber)
854 {
855 hostConf.nameIN->setText(host->name);
856 hostConf.hostname->setText(host->hostname);
857 hostConf.portnumber->setText(host->portnumber);
858
859 if (hostConfDialog.exec() == QDialog::Accepted)
860 {
861 //INDIHostsInfo *hostItem = new INDIHostsInfo;
862 host->name = hostConf.nameIN->text();
863 host->hostname = hostConf.hostname->text();
864 host->portnumber = hostConf.portnumber->text();
865
866 currentItem->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
867 currentItem->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
868
869 //ksw->data()->INDIHostsList.replace(i, hostItem);
870
871 saveHosts();
872 return;
873 }
874 }
875 }
876}
877
878void INDIDriver::removeINDIHost()
879{
880 if (ui->clientTreeWidget->currentItem() == nullptr)
881 return;
882
883 for (int i = 0; i < ksw->data()->INDIHostsList.count(); i++)
884 {
885 if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == ksw->data()->INDIHostsList[i]->name &&
886 ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) == ksw->data()->INDIHostsList[i]->portnumber)
887 {
888 if (ksw->data()->INDIHostsList[i]->isConnected)
889 {
890 KMessageBox::error(0, xi18n("You need to disconnect the client before removing it."));
891 return;
892 }
893
895 xi18n("Are you sure you want to remove the %1 client?",
896 ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN)),
897 xi18n("Delete Confirmation"),
899 return;
900
901 delete ksw->data()->INDIHostsList.takeAt(i);
902 delete (ui->clientTreeWidget->currentItem());
903 break;
904 }
905 }
906 saveHosts();
907}
908
909void INDIDriver::saveHosts()
910{
911 QFile file;
912 QString hostData;
913
914 //determine filename in local user KDE directory tree.
915 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("indihosts.xml"));
916
917 if (!file.open(QIODevice::WriteOnly))
918 {
919 QString message = xi18n(
920 "Unable to write to file 'indihosts.xml'\nAny changes to INDI hosts configurations will not be saved.");
921 KMessageBox::sorry(0, message, xi18n("Could Not Open File"));
922 return;
923 }
924
925 QTextStream outstream(&file);
926
927 //for (uint i= 0; i < ksw->data()->INDIHostsList.count(); i++)
928 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
929 {
930 hostData = "<INDIHost name='";
931 hostData += host->name;
932 hostData += "' hostname='";
933 hostData += host->hostname;
934 hostData += "' port='";
935 hostData += host->portnumber;
936 hostData += "' />\n";
937
938 outstream << hostData;
939 }
940
941 file.close();
942}
943
944IDevice *INDIDriver::findDeviceByLabel(const QString &label)
945{
946 foreach (IDevice *dev, devices)
947 {
948 if (dev->tree_label == label)
949 return dev;
950 }
951 return nullptr;
952}
953
954INDIDriver::~INDIDriver()
955{
956 //for (uint i=0; i < devices.size(); i++)
957 //delete (devices[i]);
958 while (!devices.isEmpty())
959 delete devices.takeFirst();
960}
961
962IDevice::IDevice(const QString &inName, const QString &inLabel, const QString &inDriver, const QString &inVersion)
963{
964 tree_label = inLabel;
965 unique_label.clear();
966 name = inName;
967 driver = inDriver;
968 version = inVersion;
969
970 xmlSource = PRIMARY_XML;
971
972 // Initially off
973 state = IDevice::DEV_TERMINATE;
974
975 // No port initially
976 //indiPort = -1;
977
978 // not yet managed by DeviceManager
979 //managed = false;
980
981 deviceManager = nullptr;
982
983 focal_length = -1;
984 aperture = -1;
985
986 //proc = nullptr;
987}
988
989IDevice::~IDevice()
990{
991}
992
993void IDevice::clear()
994{
995 //managed = false;
996 state = IDevice::DEV_TERMINATE;
997 deviceManager = nullptr;
998
999 unique_label.clear();
1000}
1001
1002QString IDevice::getServerBuffer()
1003{
1004 if (deviceManager != nullptr)
1005 return deviceManager->getServerBuffer();
1006
1007 return QString();
1008}
1009
1010#include "indidriver.moc"
INDI_D represents an INDI GUI Device.
Definition indidevice.h:35
INDI_P represents a single INDI property (Switch, Text, Number, Light, or BLOB).
Q_INVOKABLE QAction * action(const QString &name) const
This is the main window for KStars.
Definition kstars.h:89
KStarsData * data() const
Definition kstars.h:133
virtual KActionCollection * actionCollection() const
Information on telescope used in observation.
Definition scope.h:18
QString xi18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT unsigned int version()
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString name(StandardAction id)
KGuiItem del()
QString label(StandardShortcut id)
void setEnabled(bool)
QString applicationDirPath()
virtual int exec()
bool cd(const QString &dirName)
QFileInfoList entryInfoList(Filters filters, SortFlags sort) const const
QString filePath(const QString &fileName) const const
void setFilter(Filters filters)
void setNameFilters(const QStringList &nameFilters)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QString fileName() const const
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
bool empty() const const
bool isEmpty() const const
bool removeOne(const AT &t)
value_type takeFirst()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString arg(Args &&... args) const const
void clear()
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
double toDouble(bool *ok) const const
int toInt(bool *ok, int base) const const
uint toUInt(bool *ok, int base) const const
ItemIsSelectable
MatchExactly
QTextStream & endl(QTextStream &stream)
void close()
bool listen(const QHostAddress &address, quint16 port)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void removeChild(QTreeWidgetItem *child)
void setFlags(Qt::ItemFlags flags)
void setIcon(int column, const QIcon &icon)
void setText(int column, const QString &text)
QString text(int column) const const
void setEnabled(bool)
void setupUi(QWidget *widget)
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.