1 /*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "drivermanager.h"
8
9 #include "config-kstars.h"
10
11 #include "clientmanager.h"
12 #include "driverinfo.h"
13 #include "guimanager.h"
14 #include "indilistener.h"
15 #include "kspaths.h"
16 #include "kstars.h"
17 #include "indidbus.h"
18 #include "kstarsdata.h"
19 #include "Options.h"
20 #include "servermanager.h"
21 #include "ui_indihostconf.h"
22 #include "auxiliary/ksnotification.h"
23
24 #include <basedevice.h>
25
26 #ifndef KSTARS_LITE
27 #include <KMessageBox>
28 #include <KActionCollection>
29 #include <KNotifications/KNotification>
30 #endif
31
32 #include <QTcpServer>
33 #include <indi_debug.h>
34
35 #define INDI_MAX_TRIES 2
36 #define ERRMSG_SIZE 1024
37
DriverManagerUI(QWidget * parent)38 DriverManagerUI::DriverManagerUI(QWidget *parent) : QFrame(parent)
39 {
40 setupUi(this);
41
42 localTreeWidget->setSortingEnabled(false);
43 localTreeWidget->setRootIsDecorated(true);
44
45 clientTreeWidget->setSortingEnabled(false);
46
47 runningPix = QIcon::fromTheme("system-run");
48 stopPix = QIcon::fromTheme("dialog-cancel");
49 localMode = QIcon::fromTheme("computer");
50 serverMode = QIcon::fromTheme("network-server");
51
52 connected = QIcon::fromTheme("network-connect");
53 disconnected = QIcon::fromTheme("network-disconnect");
54
55 connect(localTreeWidget, &QTreeWidget::itemDoubleClicked, this,
56 &DriverManagerUI::makePortEditable);
57 }
58
makePortEditable(QTreeWidgetItem * selectedItem,int column)59 void DriverManagerUI::makePortEditable(QTreeWidgetItem *selectedItem, int column)
60 {
61 // If it's the port column, then make it user-editable
62 if (column == ::DriverManager::LOCAL_PORT_COLUMN)
63 selectedItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable |
64 Qt::ItemIsEnabled);
65
66 localTreeWidget->editItem(selectedItem, ::DriverManager::LOCAL_PORT_COLUMN);
67 }
68
69 DriverManager *DriverManager::_DriverManager = nullptr;
70
Instance()71 DriverManager *DriverManager::Instance()
72 {
73 if (_DriverManager == nullptr)
74 {
75 _DriverManager = new DriverManager(KStars::Instance());
76 INDIDBus *indiDBUS = new INDIDBus(KStars::Instance());
77 Q_UNUSED(indiDBUS)
78 }
79
80 return _DriverManager;
81 }
82
DriverManager(QWidget * parent)83 DriverManager::DriverManager(QWidget *parent) : QDialog(parent)
84 {
85 #ifdef Q_OS_OSX
86 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
87 #endif
88
89 currentPort = Options::serverPortStart() - 1;
90
91 QVBoxLayout *mainLayout = new QVBoxLayout;
92 ui = new DriverManagerUI(this);
93 mainLayout->addWidget(ui);
94 setLayout(mainLayout);
95 setWindowTitle(i18nc("@title:window", "Device Manager"));
96
97 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
98 mainLayout->addWidget(buttonBox);
99
100 connect(buttonBox, &QDialogButtonBox::rejected, this, &DriverManager::close);
101
102 connect(ui->addB, &QPushButton::clicked, this, &DriverManager::addINDIHost);
103 connect(ui->modifyB, &QPushButton::clicked, this, &DriverManager::modifyINDIHost);
104 connect(ui->removeB, &QPushButton::clicked, this, &DriverManager::removeINDIHost);
105
106 connect(ui->connectHostB, &QPushButton::clicked, this, &DriverManager::activateHostConnection);
107 connect(ui->disconnectHostB, &QPushButton::clicked, this, &DriverManager::activateHostDisconnection);
108 connect(ui->runServiceB, &QPushButton::clicked, this, &DriverManager::activateRunService);
109 connect(ui->stopServiceB, &QPushButton::clicked, this, &DriverManager::activateStopService);
110 connect(ui->localTreeWidget, &QTreeWidget::itemClicked, this, &DriverManager::updateLocalTab);
111 connect(ui->clientTreeWidget, &QTreeWidget::itemClicked, this, &DriverManager::updateClientTab);
112 connect(ui->localTreeWidget, &QTreeWidget::expanded, this, &DriverManager::resizeDeviceColumn);
113
114 // Do not use KSPaths here, this is for INDI
115 if (Options::indiDriversDir().isEmpty())
116 Options::setIndiDriversDir(
117 QStandardPaths::locate(QStandardPaths::GenericDataLocation, "indi",
118 QStandardPaths::LocateDirectory));
119
120 readXMLDrivers();
121
122 readINDIHosts();
123
124 m_CustomDrivers = new CustomDrivers(this, driversList);
125
126 updateCustomDrivers();
127
128 #ifdef Q_OS_WIN
129 ui->localTreeWidget->setEnabled(false);
130 #endif
131 }
132
~DriverManager()133 DriverManager::~DriverManager()
134 {
135 clearServers();
136 qDeleteAll(driversList);
137 }
138
processDeviceStatus(DriverInfo * dv)139 void DriverManager::processDeviceStatus(DriverInfo *dv)
140 {
141 if (dv == nullptr)
142 return;
143
144 if (dv->getDriverSource() == GENERATED_SOURCE)
145 return;
146
147 QString currentDriver;
148 ServerMode mode = connectionMode;
149 ServerManager *manager = dv->getServerManager();
150 bool dState = false;
151 bool cState = false;
152
153 if (dv->getDriverSource() != HOST_SOURCE)
154 {
155 if (ui->localTreeWidget->currentItem())
156 currentDriver = ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN);
157
158 for (auto &item : ui->localTreeWidget->findItems(
159 dv->getLabel(), Qt::MatchExactly | Qt::MatchRecursive))
160 {
161 item->setText(LOCAL_VERSION_COLUMN, dv->getVersion());
162
163 if (manager)
164 mode = manager->getMode();
165
166 dState = dv->getServerState();
167 cState = dv->getClientState() && dState;
168
169 bool locallyAvailable = false;
170 if (dv->getAuxInfo().contains("LOCALLY_AVAILABLE"))
171 locallyAvailable =
172 dv->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool();
173
174 switch (mode)
175 {
176 case SERVER_ONLY:
177 if (locallyAvailable)
178 {
179 ui->runServiceB->setEnabled(!dState);
180 ui->stopServiceB->setEnabled(dState);
181 item->setIcon(LOCAL_STATUS_COLUMN,
182 dState ? ui->runningPix : ui->stopPix);
183 }
184 else
185 {
186 ui->runServiceB->setEnabled(false);
187 ui->stopServiceB->setEnabled(false);
188 }
189
190 if (dState)
191 {
192 item->setIcon(LOCAL_MODE_COLUMN, ui->serverMode);
193 if (manager)
194 item->setText(LOCAL_PORT_COLUMN, QString(manager->getPort()));
195 }
196 else
197 {
198 item->setIcon(LOCAL_MODE_COLUMN, QIcon());
199 item->setText(LOCAL_PORT_COLUMN, dv->getUserPort());
200 }
201
202 break;
203
204 case SERVER_CLIENT:
205 if (locallyAvailable)
206 {
207 ui->runServiceB->setEnabled(!cState);
208 ui->stopServiceB->setEnabled(cState);
209 item->setIcon(LOCAL_STATUS_COLUMN,
210 cState ? ui->runningPix : ui->stopPix);
211 }
212 else
213 {
214 ui->runServiceB->setEnabled(false);
215 ui->stopServiceB->setEnabled(false);
216 }
217
218 if (cState)
219 {
220 item->setIcon(LOCAL_MODE_COLUMN, ui->localMode);
221
222 if (manager)
223 item->setText(LOCAL_PORT_COLUMN, QString(manager->getPort()));
224 }
225 else
226 {
227 item->setIcon(LOCAL_MODE_COLUMN, QIcon());
228 item->setText(LOCAL_PORT_COLUMN,
229 dv->getUserPort() == "-1" ? "" : dv->getUserPort());
230 }
231
232 break;
233 }
234
235 // Only update the log if the current driver is selected
236 if (currentDriver == dv->getLabel())
237 {
238 ui->serverLogText->clear();
239 ui->serverLogText->append(dv->getServerBuffer());
240 }
241 }
242 }
243 else
244 {
245 for (auto &item : ui->clientTreeWidget->findItems(dv->getName(), Qt::MatchExactly,
246 HOST_NAME_COLUMN))
247 {
248 if (dv->getClientState())
249 {
250 item->setIcon(HOST_STATUS_COLUMN, ui->connected);
251 ui->connectHostB->setEnabled(false);
252 ui->disconnectHostB->setEnabled(true);
253 }
254 else
255 {
256 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
257 ui->connectHostB->setEnabled(true);
258 ui->disconnectHostB->setEnabled(false);
259 }
260 }
261 }
262 }
263
getUniqueHosts(QList<DriverInfo * > & dList,QList<QList<DriverInfo * >> & uHosts)264 void DriverManager::getUniqueHosts(QList<DriverInfo *> &dList,
265 QList<QList<DriverInfo *>> &uHosts)
266 {
267 bool found = false;
268
269 // Iterate over all drivers
270 for (DriverInfo *dv : dList)
271 {
272 QList<DriverInfo *> uList;
273
274 // Let's see for drivers with identical hosts and ports
275 for (DriverInfo *idv : dList)
276 {
277 // If we get a match between port and hostname, we add it to the list
278 if ((dv->getHost() == idv->getHost() && dv->getPort() == idv->getPort()))
279 {
280 // Check if running already
281 if (dv->getClientState() || dv->getServerState())
282 {
283 int ans = KMessageBox::warningContinueCancel(
284 nullptr,
285 i18n("Driver %1 is already running, do you want to restart it?",
286 dv->getLabel()));
287 if (ans == KMessageBox::Cancel)
288 continue;
289 else
290 {
291 QList<DriverInfo *> stopList;
292 stopList.append(dv);
293 stopDevices(stopList);
294 }
295 }
296
297 found = false;
298
299 // Check to see if the driver already been added elsewhere
300 for (auto &qdi : uHosts)
301 {
302 for (DriverInfo *di : qdi)
303 {
304 if (di == idv)
305 {
306 found = true;
307 break;
308 }
309 }
310 }
311
312 if (found == false)
313 uList.append(idv);
314 }
315 }
316
317 if (uList.empty() == false)
318 uHosts.append(uList);
319 }
320 }
321
startDevices(QList<DriverInfo * > & dList)322 bool DriverManager::startDevices(QList<DriverInfo *> &dList)
323 {
324 ServerManager *serverManager = nullptr;
325 ClientManager *clientManager = nullptr;
326 int port = -1;
327
328 QList<QList<DriverInfo *>> uHosts;
329
330 bool connectionToServer = false;
331
332 getUniqueHosts(dList, uHosts);
333
334 qCDebug(KSTARS_INDI) << "INDI: Starting local drivers...";
335
336 for (auto &qdv : uHosts)
337 {
338 if (qdv.empty())
339 continue;
340
341 port = qdv.at(0)->getPort().toInt();
342
343 // Select random port within range is none specified.
344 if (port == -1)
345 port = getINDIPort(port);
346
347 if (port <= 0)
348 {
349 KSNotification::error(i18n("Cannot start INDI server: port error."));
350 return false;
351 }
352
353 serverManager = new ServerManager(qdv.at(0)->getHost(), port);
354
355 if (serverManager == nullptr)
356 {
357 qWarning() << "Warning: device manager has not been established properly";
358 return false;
359 }
360
361 serverManager->setMode(connectionMode);
362
363 connect(serverManager, SIGNAL(newServerLog()), this, SLOT(updateLocalTab()));
364 connect(serverManager, &ServerManager::started, [&]()
365 {
366 emit serverStarted(qdv.at(0)->getHost(), QString::number(port));
367 });
368
369 if (serverManager->start())
370 servers.append(serverManager);
371 else
372 {
373 delete serverManager;
374 return false;
375 }
376
377 qCDebug(KSTARS_INDI) << "INDI: INDI Server started locally on port " << port;
378
379 for (DriverInfo *dv : qdv)
380 {
381 if (serverManager->startDriver(dv) == false)
382 {
383 servers.removeOne(serverManager);
384 serverManager->stop();
385 emit serverTerminated(serverManager->getHost(), serverManager->getPort());
386 delete serverManager;
387 return false;
388 }
389 }
390
391 // Nothing to do more if SERVER ONLY
392 if (connectionMode == SERVER_ONLY)
393 continue;
394
395 clientManager = new ClientManager();
396
397 for (DriverInfo *dv : qdv)
398 clientManager->appendManagedDriver(dv);
399
400 connect(clientManager, &ClientManager::connectionFailure, this,
401 &DriverManager::processClientTermination);
402
403 clientManager->setServer(qdv.at(0)->getHost().toLatin1().constData(), port);
404
405 GUIManager::Instance()->addClient(clientManager);
406 INDIListener::Instance()->addClient(clientManager);
407
408 for (int i = 0; i < INDI_MAX_TRIES; i++)
409 {
410 qCDebug(KSTARS_INDI)
411 << "INDI: Connecting to local INDI server on port " << port << " ...";
412
413 connectionToServer = clientManager->connectServer();
414
415 if (connectionToServer)
416 break;
417
418 qApp->processEvents();
419
420 //usleep(100000);
421 QThread::usleep(100000);
422 }
423
424 if (connectionToServer)
425 {
426 qCDebug(KSTARS_INDI) << "Connection to INDI server is successful";
427
428 clients.append(clientManager);
429 updateMenuActions();
430 }
431 else
432 {
433 qCDebug(KSTARS_INDI)
434 << "INDI: Connection to local INDI server on port " << port << " failed!";
435
436 KNotification::beep();
437 QPointer<QMessageBox> msgBox = new QMessageBox();
438 msgBox->setAttribute(Qt::WA_DeleteOnClose);
439 msgBox->setStandardButtons(QMessageBox::Ok);
440 msgBox->setWindowTitle(i18nc("@title:window", "Error"));
441 msgBox->setText(
442 i18n("Connection to INDI server locally on port %1 failed.", port));
443 msgBox->setModal(false);
444 msgBox->setIcon(QMessageBox::Critical);
445 msgBox->show();
446
447 for (DriverInfo *dv : qdv)
448 processDeviceStatus(dv);
449
450 GUIManager::Instance()->removeClient(clientManager);
451 INDIListener::Instance()->removeClient(clientManager);
452
453 return false;
454 }
455 }
456
457 return true;
458 }
459
stopDevices(const QList<DriverInfo * > & dList)460 void DriverManager::stopDevices(const QList<DriverInfo *> &dList)
461 {
462 qCDebug(KSTARS_INDI) << "INDI: Stopping local drivers...";
463
464 // #1 Disconnect all clients
465 for (DriverInfo *dv : dList)
466 {
467 ClientManager *cm = dv->getClientManager();
468
469 if (cm == nullptr)
470 continue;
471
472 cm->removeManagedDriver(dv);
473
474 if (cm->count() == 0)
475 {
476 GUIManager::Instance()->removeClient(cm);
477 INDIListener::Instance()->removeClient(cm);
478 cm->disconnectServer();
479 clients.removeOne(cm);
480 cm->deleteLater();
481
482 KStars::Instance()->slotSetTelescopeEnabled(false);
483 KStars::Instance()->slotSetDomeEnabled(false);
484 }
485 }
486
487 // #2 Disconnect all servers
488 for (DriverInfo *dv : dList)
489 {
490 ServerManager *sm = dv->getServerManager();
491
492 if (sm != nullptr)
493 {
494 sm->stopDriver(dv);
495
496 if (sm->size() == 0)
497 {
498 sm->stop();
499 servers.removeOne(sm);
500 sm->deleteLater();
501 }
502 }
503 }
504
505 // Reset current port
506 currentPort = Options::serverPortStart() - 1;
507
508 updateMenuActions();
509 }
510
clearServers()511 void DriverManager::clearServers()
512 {
513 foreach (ServerManager *serverManager, servers)
514 serverManager->terminate();
515
516 qDeleteAll(servers);
517 }
518
activateRunService()519 void DriverManager::activateRunService()
520 {
521 processLocalTree(true);
522 }
523
activateStopService()524 void DriverManager::activateStopService()
525 {
526 processLocalTree(false);
527 }
528
activateHostConnection()529 void DriverManager::activateHostConnection()
530 {
531 processRemoteTree(true);
532 }
533
activateHostDisconnection()534 void DriverManager::activateHostDisconnection()
535 {
536 processRemoteTree(false);
537 }
538
getClientManager(DriverInfo * dv)539 ClientManager *DriverManager::getClientManager(DriverInfo *dv)
540 {
541 return dv->getClientManager();
542 }
543
updateLocalTab()544 void DriverManager::updateLocalTab()
545 {
546 if (ui->localTreeWidget->currentItem() == nullptr)
547 return;
548
549 QString currentDriver = ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN);
550
551 foreach (DriverInfo *device, driversList)
552 {
553 if (currentDriver == device->getLabel())
554 {
555 processDeviceStatus(device);
556 break;
557 }
558 }
559 }
560
updateClientTab()561 void DriverManager::updateClientTab()
562 {
563 QTreeWidgetItem *item = ui->clientTreeWidget->currentItem();
564
565 if (item == nullptr)
566 return;
567
568 QString hostname = item->text(HOST_NAME_COLUMN);
569 QString hostport = item->text(HOST_PORT_COLUMN);
570
571 for (auto &dv : driversList)
572 {
573 if (hostname == dv->getName() && hostport == dv->getPort())
574 {
575 processDeviceStatus(dv);
576 break;
577 }
578 }
579 }
580
processLocalTree(bool dState)581 void DriverManager::processLocalTree(bool dState)
582 {
583 QList<DriverInfo *> processed_devices;
584
585 int port = -1;
586 bool portOK = false;
587
588 connectionMode = ui->localR->isChecked() ? SERVER_CLIENT : SERVER_ONLY;
589
590 foreach (QTreeWidgetItem *item, ui->localTreeWidget->selectedItems())
591 {
592 foreach (DriverInfo *device, driversList)
593 {
594 port = -1;
595
596 //device->state = (dev_request == DriverInfo::DEV_TERMINATE) ? DriverInfo::DEV_START : DriverInfo::DEV_TERMINATE;
597 if (item->text(LOCAL_NAME_COLUMN) == device->getLabel() &&
598 device->getServerState() != dState)
599 {
600 processed_devices.append(device);
601
602 // N.B. If multiple devices are selected to run under one device manager
603 // then we select the port for the first device that has a valid port
604 // entry, the rest are ignored.
605 if (port == -1 && item->text(LOCAL_PORT_COLUMN).isEmpty() == false)
606 {
607 port = item->text(LOCAL_PORT_COLUMN).toInt(&portOK);
608 // If we encounter conversion error, we abort
609 if (portOK == false)
610 {
611 KSNotification::error(i18n("Invalid port entry: %1",
612 item->text(LOCAL_PORT_COLUMN)));
613 return;
614 }
615 }
616
617 device->setHostParameters("localhost", QString("%1").arg(port));
618 }
619 }
620 }
621
622 if (processed_devices.empty())
623 return;
624
625 if (dState)
626 startDevices(processed_devices);
627 else
628 stopDevices(processed_devices);
629 }
630
processClientTermination(ClientManager * client)631 void DriverManager::processClientTermination(ClientManager *client)
632 {
633 if (client == nullptr)
634 return;
635
636 ServerManager *manager = client->getServerManager();
637
638 if (manager)
639 {
640 servers.removeOne(manager);
641 delete manager;
642 }
643
644 emit serverTerminated(client->getHost(), QString("%1").arg(client->getPort()));
645
646 GUIManager::Instance()->removeClient(client);
647 INDIListener::Instance()->removeClient(client);
648
649 KSNotification::event(
650 QLatin1String("IndiServerMessage"),
651 i18n("Connection to INDI host at %1 on port %2 lost. Server disconnected.",
652 client->getHost(), client->getPort()),
653 KSNotification::EVENT_ALERT);
654
655 clients.removeOne(client);
656 client->deleteLater();
657
658 updateMenuActions();
659 updateLocalTab();
660 }
661
processServerTermination(ServerManager * server)662 void DriverManager::processServerTermination(ServerManager *server)
663 {
664 if (server == nullptr)
665 return;
666
667 for (auto &dv : driversList)
668 if (dv->getServerManager() == server)
669 {
670 dv->reset();
671 }
672
673 if (server->getMode() == SERVER_ONLY)
674 {
675 KSNotification::error(
676 i18n("Connection to INDI host at %1 on port %2 encountered an error: %3.",
677 server->getHost(), server->getPort(), server->errorString()));
678 }
679
680 emit serverTerminated(server->getHost(), server->getPort());
681 servers.removeOne(server);
682 server->deleteLater();
683
684 updateLocalTab();
685 }
686
processRemoteTree(bool dState)687 void DriverManager::processRemoteTree(bool dState)
688 {
689 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
690 if (!currentItem)
691 return;
692
693 for (auto &dv : driversList)
694 {
695 if (dv->getDriverSource() != HOST_SOURCE)
696 continue;
697
698 //qDebug() << "Current item port " << currentItem->text(HOST_PORT_COLUMN) << " current dev " << dv->getName() << " -- port " << dv->getPort() << endl;
699 //qDebug() << "dState is : " << (dState ? "True" : "False") << endl;
700
701 if (currentItem->text(HOST_NAME_COLUMN) == dv->getName() &&
702 currentItem->text(HOST_PORT_COLUMN) == dv->getPort())
703 {
704 // Nothing changed, return
705 if (dv->getClientState() == dState)
706 return;
707
708 // connect to host
709 if (dState)
710 connectRemoteHost(dv);
711 // Disconnect form host
712 else
713 disconnectRemoteHost(dv);
714 }
715 }
716 }
717
connectRemoteHost(DriverInfo * dv)718 bool DriverManager::connectRemoteHost(DriverInfo *dv)
719 {
720 bool hostPortOk = false;
721 bool connectionToServer = false;
722 ClientManager *clientManager = nullptr;
723
724 dv->getPort().toInt(&hostPortOk);
725
726 if (hostPortOk == false)
727 {
728 KSNotification::error(i18n("Invalid host port %1", dv->getPort()));
729 return false;
730 }
731
732 clientManager = new ClientManager();
733
734 clientManager->appendManagedDriver(dv);
735
736 connect(clientManager, &ClientManager::connectionFailure, this,
737 &DriverManager::processClientTermination);
738
739 clientManager->setServer(dv->getHost().toLatin1().constData(),
740 static_cast<uint>(dv->getPort().toInt()));
741
742 GUIManager::Instance()->addClient(clientManager);
743 INDIListener::Instance()->addClient(clientManager);
744
745 for (int i = 0; i < INDI_MAX_TRIES; i++)
746 {
747 connectionToServer = clientManager->connectServer();
748
749 if (connectionToServer)
750 break;
751
752 QThread::usleep(100000);
753 }
754
755 if (connectionToServer)
756 {
757 clients.append(clientManager);
758 updateMenuActions();
759
760 KSNotification::event(QLatin1String("ConnectionSuccessful"),
761 i18n("Connected to INDI server"),
762 KSNotification::EVENT_INFO);
763 }
764 else
765 {
766 GUIManager::Instance()->removeClient(clientManager);
767 INDIListener::Instance()->removeClient(clientManager);
768
769 KSNotification::error(i18n("Connection to INDI server at host %1 with port %2 failed.",
770 dv->getHost(), dv->getPort()), i18nc("@title:window", "Error"), 30);
771 processDeviceStatus(dv);
772 return false;
773 }
774
775 return true;
776 }
777
disconnectRemoteHost(DriverInfo * dv)778 bool DriverManager::disconnectRemoteHost(DriverInfo *dv)
779 {
780 ClientManager *clientManager = dv->getClientManager();
781
782 if (clientManager)
783 {
784 clientManager->removeManagedDriver(dv);
785 clientManager->disconnectAll();
786 GUIManager::Instance()->removeClient(clientManager);
787 INDIListener::Instance()->removeClient(clientManager);
788 clients.removeOne(clientManager);
789 clientManager->deleteLater();
790 updateMenuActions();
791 return true;
792 }
793
794 return false;
795 }
796
resizeDeviceColumn()797 void DriverManager::resizeDeviceColumn()
798 {
799 ui->localTreeWidget->resizeColumnToContents(0);
800 }
801
updateMenuActions()802 void DriverManager::updateMenuActions()
803 {
804 // We iterate over devices, we enable INDI Control Panel if we have any active device
805 // We enable capture image sequence if we have any imaging device
806
807 QAction *tmpAction = nullptr;
808 bool activeDevice = false;
809
810 if (clients.size() > 0)
811 activeDevice = true;
812
813 tmpAction = KStars::Instance()->actionCollection()->action("indi_cpl");
814 if (tmpAction != nullptr)
815 {
816 //qDebug() << "indi_cpl action set to active" << endl;
817 tmpAction->setEnabled(activeDevice);
818 }
819 }
820
getINDIPort(int customPort)821 int DriverManager::getINDIPort(int customPort)
822 {
823 #ifdef Q_OS_WIN
824 qWarning() << "INDI server is currently not supported on Windows.";
825 return -1;
826 #else
827 int lastPort = Options::serverPortEnd();
828 bool success = false;
829 currentPort++;
830
831 // recycle
832 if (currentPort > lastPort)
833 currentPort = Options::serverPortStart();
834
835 QTcpServer temp_server;
836
837 if (customPort != -1)
838 {
839 success = temp_server.listen(QHostAddress::LocalHost, customPort);
840 if (success)
841 {
842 temp_server.close();
843 return customPort;
844 }
845 else
846 return -1;
847 }
848
849 for (; currentPort <= lastPort; currentPort++)
850 {
851 success = temp_server.listen(QHostAddress::LocalHost, currentPort);
852 if (success)
853 {
854 temp_server.close();
855 return currentPort;
856 }
857 }
858 return -1;
859 #endif
860 }
861
readINDIHosts()862 bool DriverManager::readINDIHosts()
863 {
864 QString indiFile("indihosts.xml");
865 //QFile localeFile;
866 QFile file;
867 char errmsg[1024];
868 char c;
869 LilXML *xmlParser = newLilXML();
870 XMLEle *root = nullptr;
871 XMLAtt *ap = nullptr;
872 QString hName, hHost, hPort;
873
874 lastGroup = nullptr;
875 file.setFileName(KSPaths::locate(QStandardPaths::AppDataLocation, indiFile));
876 if (file.fileName().isEmpty() || !file.open(QIODevice::ReadOnly))
877 {
878 delLilXML(xmlParser);
879 return false;
880 }
881
882 while (file.getChar(&c))
883 {
884 root = readXMLEle(xmlParser, c, errmsg);
885
886 if (root)
887 {
888 // Get host name
889 ap = findXMLAtt(root, "name");
890 if (!ap)
891 {
892 delLilXML(xmlParser);
893 return false;
894 }
895
896 hName = QString(valuXMLAtt(ap));
897
898 // Get host name
899 ap = findXMLAtt(root, "hostname");
900
901 if (!ap)
902 {
903 delLilXML(xmlParser);
904 return false;
905 }
906
907 hHost = QString(valuXMLAtt(ap));
908
909 ap = findXMLAtt(root, "port");
910
911 if (!ap)
912 {
913 delLilXML(xmlParser);
914 return false;
915 }
916
917 hPort = QString(valuXMLAtt(ap));
918
919 DriverInfo *dv = new DriverInfo(hName);
920 dv->setHostParameters(hHost, hPort);
921 dv->setDriverSource(HOST_SOURCE);
922
923 connect(dv, &DriverInfo::deviceStateChanged, this, &DriverManager::processDeviceStatus);
924
925 driversList.append(dv);
926
927 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget, lastGroup);
928 lastGroup = item;
929 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
930 item->setText(HOST_NAME_COLUMN, hName);
931 item->setText(HOST_PORT_COLUMN, hPort);
932
933 delXMLEle(root);
934 }
935 else if (errmsg[0])
936 {
937 qDebug() << errmsg;
938 delLilXML(xmlParser);
939 return false;
940 }
941 }
942
943 delLilXML(xmlParser);
944
945 return true;
946 }
947
readXMLDrivers()948 bool DriverManager::readXMLDrivers()
949 {
950 QDir indiDir;
951 QString driverName;
952
953 // This is the XML file shipped with KStars that contains all supported INDI drivers.
954 /*QString indiDriversXML = KSPaths::locate(QStandardPaths::AppDataLocation, "indidrivers.xml");
955 if (indiDriversXML.isEmpty() == false)
956 processXMLDriver(indiDriversXML);
957 */
958
959 processXMLDriver(QLatin1String(":/indidrivers.xml"));
960
961 QString driversDir = Options::indiDriversDir();
962 #ifdef Q_OS_OSX
963 if (Options::indiDriversAreInternal())
964 driversDir =
965 QCoreApplication::applicationDirPath() + "/../Resources/DriverSupport";
966 #endif
967
968 if (indiDir.cd(driversDir) == false)
969 {
970 KSNotification::error(i18n("Unable to find INDI drivers directory: %1\nPlease "
971 "make sure to set the correct "
972 "path in KStars configuration",
973 driversDir));
974 return false;
975 }
976
977 indiDir.setNameFilters(QStringList() << "indi_*.xml"
978 << "drivers.xml");
979 indiDir.setFilter(QDir::Files | QDir::NoSymLinks);
980 QFileInfoList list = indiDir.entryInfoList();
981
982 for (auto &fileInfo : list)
983 {
984 // libindi 0.7.1: Skip skeleton files
985 if (fileInfo.fileName().endsWith(QLatin1String("_sk.xml")))
986 continue;
987
988 // if (fileInfo.fileName() == "drivers.xml")
989 // {
990 // // Let first attempt to load the local version of drivers.xml
991 // driverName = QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("drivers.xml");
992
993 // // If found, we continue, otherwise, we load the system file
994 // if (driverName.isEmpty() == false && QFile(driverName).exists())
995 // {
996 // processXMLDriver(driverName);
997 // continue;
998 // }
999 // }
1000
1001 processXMLDriver(fileInfo.absoluteFilePath());
1002 }
1003
1004 return true;
1005 }
1006
processXMLDriver(const QString & driverName)1007 void DriverManager::processXMLDriver(const QString &driverName)
1008 {
1009 QFile file(driverName);
1010 if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
1011 {
1012 KSNotification::error(i18n("Failed to open INDI Driver file: %1", driverName));
1013 return;
1014 }
1015
1016 char errmsg[ERRMSG_SIZE];
1017 char c;
1018 LilXML *xmlParser = newLilXML();
1019 XMLEle *root = nullptr;
1020 XMLEle *ep = nullptr;
1021
1022 if (driverName.endsWith(QLatin1String("drivers.xml")))
1023 driverSource = PRIMARY_XML;
1024 else
1025 driverSource = THIRD_PARTY_XML;
1026
1027 while (file.getChar(&c))
1028 {
1029 root = readXMLEle(xmlParser, c, errmsg);
1030
1031 if (root)
1032 {
1033 // If the XML file is using the INDI Library v1.3+ format
1034 if (!strcmp(tagXMLEle(root), "driversList"))
1035 {
1036 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
1037 {
1038 if (!buildDeviceGroup(ep, errmsg))
1039 prXMLEle(stderr, ep, 0);
1040 }
1041 }
1042 // If using the older format
1043 else
1044 {
1045 if (!buildDeviceGroup(root, errmsg))
1046 prXMLEle(stderr, root, 0);
1047 }
1048
1049 delXMLEle(root);
1050 }
1051 else if (errmsg[0])
1052 {
1053 qCDebug(KSTARS_INDI) << QString(errmsg);
1054 delLilXML(xmlParser);
1055 return;
1056 }
1057 }
1058
1059 delLilXML(xmlParser);
1060 }
1061
buildDeviceGroup(XMLEle * root,char errmsg[])1062 bool DriverManager::buildDeviceGroup(XMLEle *root, char errmsg[])
1063 {
1064 XMLAtt *ap;
1065 XMLEle *ep;
1066 QString groupName;
1067 QTreeWidgetItem *group;
1068 DeviceFamily groupType = KSTARS_TELESCOPE;
1069
1070 // avoid overflow
1071 if (strlen(tagXMLEle(root)) > 1024)
1072 return false;
1073
1074 // Get device grouping name
1075 ap = findXMLAtt(root, "group");
1076
1077 if (!ap)
1078 {
1079 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute",
1080 tagXMLEle(root));
1081 return false;
1082 }
1083
1084 groupName = valuXMLAtt(ap);
1085 groupType = DeviceFamilyLabels.key(groupName);
1086
1087 #ifndef HAVE_CFITSIO
1088 // We do not create these groups if we don't have CFITSIO support
1089 if (groupType == KSTARS_CCD || groupType == KSTARS_VIDEO)
1090 return true;
1091 #endif
1092
1093 // Find if the group already exists
1094 QList<QTreeWidgetItem *> treeList =
1095 ui->localTreeWidget->findItems(groupName, Qt::MatchExactly);
1096 if (!treeList.isEmpty())
1097 group = treeList[0];
1098 else
1099 group = new QTreeWidgetItem(ui->localTreeWidget, lastGroup);
1100
1101 group->setText(0, groupName);
1102 lastGroup = group;
1103
1104 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
1105 {
1106 if (!buildDriverElement(ep, group, groupType, errmsg))
1107 return false;
1108 }
1109
1110 return true;
1111 }
1112
buildDriverElement(XMLEle * root,QTreeWidgetItem * DGroup,DeviceFamily groupType,char errmsg[])1113 bool DriverManager::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup,
1114 DeviceFamily groupType, char errmsg[])
1115 {
1116 XMLAtt *ap;
1117 XMLEle *el;
1118 DriverInfo *dv;
1119 QString label;
1120 QString driver;
1121 QString version;
1122 // N.B. NOT an i18n string.
1123 QString manufacturer("Others");
1124 QString name;
1125 QString port;
1126 QString skel;
1127 QVariantMap vMap;
1128 //double focal_length(-1), aperture(-1);
1129
1130 ap = findXMLAtt(root, "label");
1131 if (!ap)
1132 {
1133 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute",
1134 tagXMLEle(root));
1135 return false;
1136 }
1137
1138 label = valuXMLAtt(ap);
1139
1140 // Label is unique, so if we have the same label, we simply ignore
1141 if (findDriverByLabel(label) != nullptr)
1142 return true;
1143
1144 ap = findXMLAtt(root, "manufacturer");
1145 if (ap)
1146 manufacturer = valuXMLAtt(ap);
1147
1148 // Search for optional port attribute
1149 ap = findXMLAtt(root, "port");
1150 if (ap)
1151 port = valuXMLAtt(ap);
1152
1153 // Search for skel file, if any
1154 // Search for optional port attribute
1155 ap = findXMLAtt(root, "skel");
1156 if (ap)
1157 skel = valuXMLAtt(ap);
1158
1159 // Let's look for telescope-specific attributes: focal length and aperture
1160 // ap = findXMLAtt(root, "focal_length");
1161 // if (ap)
1162 // {
1163 // focal_length = QString(valuXMLAtt(ap)).toDouble();
1164 // if (focal_length > 0)
1165 // vMap.insert("TELESCOPE_FOCAL_LENGTH", focal_length);
1166 // }
1167
1168 // Find MDPD: Multiple Devices Per Driver
1169 ap = findXMLAtt(root, "mdpd");
1170 if (ap)
1171 {
1172 bool mdpd = false;
1173 mdpd = (QString(valuXMLAtt(ap)) == QString("true")) ? true : false;
1174 vMap.insert("mdpd", mdpd);
1175 }
1176
1177 // ap = findXMLAtt(root, "aperture");
1178 // if (ap)
1179 // {
1180 // aperture = QString(valuXMLAtt(ap)).toDouble();
1181 // if (aperture > 0)
1182 // vMap.insert("TELESCOPE_APERTURE", aperture);
1183 // }
1184
1185 el = findXMLEle(root, "driver");
1186
1187 if (!el)
1188 return false;
1189
1190 driver = pcdataXMLEle(el);
1191
1192 ap = findXMLAtt(el, "name");
1193 if (!ap)
1194 {
1195 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a name attribute",
1196 tagXMLEle(el));
1197 return false;
1198 }
1199
1200 name = valuXMLAtt(ap);
1201
1202 el = findXMLEle(root, "version");
1203
1204 if (!el)
1205 return false;
1206
1207 version = pcdataXMLEle(el);
1208 bool versionOK = false;
1209 version.toDouble(&versionOK);
1210 if (versionOK == false)
1211 version = "1.0";
1212
1213 bool driverIsAvailable = checkDriverAvailability(driver);
1214
1215 vMap.insert("LOCALLY_AVAILABLE", driverIsAvailable);
1216 QIcon remoteIcon = QIcon::fromTheme("network-modem");
1217
1218 QTreeWidgetItem *device = new QTreeWidgetItem(DGroup);
1219
1220 device->setText(LOCAL_NAME_COLUMN, label);
1221 if (driverIsAvailable)
1222 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
1223 else
1224 device->setIcon(LOCAL_STATUS_COLUMN, remoteIcon);
1225 device->setText(LOCAL_VERSION_COLUMN, version);
1226 device->setText(LOCAL_PORT_COLUMN, port);
1227
1228 //if ((driverSource == PRIMARY_XML) && driversStringList.contains(driver) == false)
1229 if (groupType == KSTARS_TELESCOPE && driversStringList.contains(driver) == false)
1230 driversStringList.append(driver);
1231
1232 dv = new DriverInfo(name);
1233
1234 dv->setLabel(label);
1235 dv->setVersion(version);
1236 dv->setExecutable(driver);
1237 dv->setManufacturer(manufacturer);
1238 dv->setSkeletonFile(skel);
1239 dv->setType(groupType);
1240 dv->setDriverSource(driverSource);
1241 dv->setUserPort(port);
1242 dv->setAuxInfo(vMap);
1243
1244 connect(dv, &DriverInfo::deviceStateChanged, this, &DriverManager::processDeviceStatus);
1245
1246 driversList.append(dv);
1247
1248 return true;
1249 }
1250
checkDriverAvailability(const QString & driver)1251 bool DriverManager::checkDriverAvailability(const QString &driver)
1252 {
1253 QString indiServerDir = Options::indiServer();
1254 if (Options::indiServerIsInternal())
1255 indiServerDir = QCoreApplication::applicationDirPath() + "/indi";
1256 else
1257 indiServerDir = QFileInfo(Options::indiServer()).dir().path();
1258
1259 QFile driverFile(indiServerDir + '/' + driver);
1260
1261 if (driverFile.exists() == false)
1262 return (!QStandardPaths::findExecutable(indiServerDir + '/' + driver).isEmpty());
1263
1264 return true;
1265 }
1266
updateCustomDrivers()1267 void DriverManager::updateCustomDrivers()
1268 {
1269 for (const QVariantMap &oneDriver : m_CustomDrivers->customDrivers())
1270 {
1271 DriverInfo *dv = new DriverInfo(oneDriver["Name"].toString());
1272 dv->setLabel(oneDriver["Label"].toString());
1273 dv->setUniqueLabel(dv->getLabel());
1274 dv->setExecutable(oneDriver["Exec"].toString());
1275 dv->setVersion(oneDriver["Version"].toString());
1276 dv->setManufacturer(oneDriver["Manufacturer"].toString());
1277 dv->setType(DeviceFamilyLabels.key(oneDriver["Family"].toString()));
1278 dv->setDriverSource(CUSTOM_SOURCE);
1279
1280 bool driverIsAvailable = checkDriverAvailability(oneDriver["Exec"].toString());
1281 QVariantMap vMap;
1282 vMap.insert("LOCALLY_AVAILABLE", driverIsAvailable);
1283 dv->setAuxInfo(vMap);
1284
1285 driversList.append(dv);
1286 }
1287 }
1288
1289 // JM 2018-07-23: Disabling the old custom drivers method
1290 #if 0
1291 void DriverManager::updateCustomDrivers()
1292 {
1293 QString label;
1294 QString driver;
1295 QString version;
1296 QString name;
1297 QTreeWidgetItem *group = nullptr;
1298 QTreeWidgetItem *widgetDev = nullptr;
1299 QVariantMap vMap;
1300 DriverInfo *drv = nullptr;
1301
1302 // Find if the group already exists
1303 QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems("Telescopes", Qt::MatchExactly);
1304 if (!treeList.isEmpty())
1305 group = treeList[0];
1306 else
1307 return;
1308
1309 KStarsData::Instance()->logObject()->readAll();
1310
1311 // Find custom telescope to ADD/UPDATE
1312 foreach (OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList()))
1313 {
1314 name = label = s->name();
1315
1316 if (s->driver() == i18n("None"))
1317 continue;
1318
1319 // If driver already exists, just update values
1320 if ((drv = findDriverByLabel(label)))
1321 {
1322 if (s->aperture() > 0 && s->focalLength() > 0)
1323 {
1324 vMap.insert("TELESCOPE_APERTURE", s->aperture());
1325 vMap.insert("TELESCOPE_FOCAL_LENGTH", s->focalLength());
1326 drv->setAuxInfo(vMap);
1327 }
1328
1329 drv->setExecutable(s->driver());
1330
1331 continue;
1332 }
1333
1334 driver = s->driver();
1335 version = QString("1.0");
1336
1337 QTreeWidgetItem *device = new QTreeWidgetItem(group);
1338 device->setText(LOCAL_NAME_COLUMN, QString(label));
1339 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
1340 device->setText(LOCAL_VERSION_COLUMN, QString(version));
1341
1342 DriverInfo *dv = new DriverInfo(name);
1343
1344 dv->setLabel(label);
1345 dv->setExecutable(driver);
1346 dv->setVersion(version);
1347 dv->setType(KSTARS_TELESCOPE);
1348 dv->setDriverSource(EM_XML);
1349
1350 if (s->aperture() > 0 && s->focalLength() > 0)
1351 {
1352 vMap.insert("TELESCOPE_APERTURE", s->aperture());
1353 vMap.insert("TELESCOPE_FOCAL_LENGTH", s->focalLength());
1354 dv->setAuxInfo(vMap);
1355 }
1356
1357 connect(dv, SIGNAL(deviceStateChanged(DriverInfo*)), this, SLOT(processDeviceStatus(DriverInfo*)));
1358 driversList.append(dv);
1359 }
1360
1361 // Find custom telescope to REMOVE
1362 foreach (DriverInfo *dev, driversList)
1363 {
1364 // If it's from primary xml file or it is in a running state, continue.
1365 if (dev->getDriverSource() != EM_XML || dev->getClientState())
1366 continue;
1367
1368 if (KStarsData::Instance()->logObject()->findScopeByName(dev->getName()))
1369 continue;
1370
1371 // Find if the group already exists
1372 QList<QTreeWidgetItem *> devList =
1373 ui->localTreeWidget->findItems(dev->getLabel(), Qt::MatchExactly | Qt::MatchRecursive);
1374 if (!devList.isEmpty())
1375 {
1376 widgetDev = devList[0];
1377 group->removeChild(widgetDev);
1378 }
1379 else
1380 return;
1381
1382 driversList.removeOne(dev);
1383 delete (dev);
1384 }
1385 }
1386 #endif
1387
addINDIHost()1388 void DriverManager::addINDIHost()
1389 {
1390 QDialog hostConfDialog;
1391 Ui::INDIHostConf hostConf;
1392 hostConf.setupUi(&hostConfDialog);
1393 hostConfDialog.setWindowTitle(i18nc("@title:window", "Add Host"));
1394 bool portOk = false;
1395
1396 if (hostConfDialog.exec() == QDialog::Accepted)
1397 {
1398 DriverInfo *hostItem = new DriverInfo(hostConf.nameIN->text());
1399
1400 hostConf.portnumber->text().toInt(&portOk);
1401
1402 if (portOk == false)
1403 {
1404 KSNotification::error(i18n("Error: the port number is invalid."));
1405 delete hostItem;
1406 return;
1407 }
1408
1409 hostItem->setHostParameters(hostConf.hostname->text(),
1410 hostConf.portnumber->text());
1411
1412 //search for duplicates
1413 //for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++)
1414 foreach (DriverInfo *host, driversList)
1415 if (hostItem->getName() == host->getName() &&
1416 hostItem->getPort() == host->getPort())
1417 {
1418 KSNotification::error(i18n("Host: %1 Port: %2 already exists.",
1419 hostItem->getName(), hostItem->getPort()));
1420 delete hostItem;
1421 return;
1422 }
1423
1424 hostItem->setDriverSource(HOST_SOURCE);
1425
1426 connect(hostItem, &DriverInfo::deviceStateChanged, this, &DriverManager::processDeviceStatus);
1427
1428 driversList.append(hostItem);
1429
1430 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget);
1431 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
1432 item->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
1433 item->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
1434 }
1435
1436 saveHosts();
1437 }
1438
modifyINDIHost()1439 void DriverManager::modifyINDIHost()
1440 {
1441 QDialog hostConfDialog;
1442 Ui::INDIHostConf hostConf;
1443 hostConf.setupUi(&hostConfDialog);
1444 hostConfDialog.setWindowTitle(i18nc("@title:window", "Modify Host"));
1445
1446 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
1447
1448 if (currentItem == nullptr)
1449 return;
1450
1451 foreach (DriverInfo *host, driversList)
1452 {
1453 if (currentItem->text(HOST_NAME_COLUMN) == host->getName() &&
1454 currentItem->text(HOST_PORT_COLUMN) == host->getPort())
1455 {
1456 hostConf.nameIN->setText(host->getName());
1457 hostConf.hostname->setText(host->getHost());
1458 hostConf.portnumber->setText(host->getPort());
1459
1460 if (hostConfDialog.exec() == QDialog::Accepted)
1461 {
1462 //INDIHostsInfo *hostItem = new INDIHostsInfo;
1463 host->setName(hostConf.nameIN->text());
1464 host->setHostParameters(hostConf.hostname->text(),
1465 hostConf.portnumber->text());
1466
1467 currentItem->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
1468 currentItem->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
1469
1470 //ksw->data()->INDIHostsList.replace(i, hostItem);
1471
1472 saveHosts();
1473 return;
1474 }
1475 }
1476 }
1477 }
1478
removeINDIHost()1479 void DriverManager::removeINDIHost()
1480 {
1481 if (ui->clientTreeWidget->currentItem() == nullptr)
1482 return;
1483
1484 foreach (DriverInfo *host, driversList)
1485 if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) ==
1486 host->getName() &&
1487 ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) ==
1488 host->getPort())
1489 {
1490 if (host->getClientState())
1491 {
1492 KSNotification::error(
1493 i18n("You need to disconnect the client before removing it."));
1494 return;
1495 }
1496
1497 if (KMessageBox::warningContinueCancel(
1498 nullptr,
1499 i18n("Are you sure you want to remove the %1 client?",
1500 ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN)),
1501 i18n("Delete Confirmation"),
1502 KStandardGuiItem::del()) != KMessageBox::Continue)
1503 return;
1504
1505 driversList.removeOne(host);
1506 delete host;
1507 delete (ui->clientTreeWidget->currentItem());
1508 break;
1509 }
1510
1511 saveHosts();
1512 }
1513
saveHosts()1514 void DriverManager::saveHosts()
1515 {
1516 QFile file;
1517 QString hostData;
1518
1519 //determine filename in local user KDE directory tree.
1520 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation))
1521 .filePath("indihosts.xml"));
1522
1523 if (!file.open(QIODevice::WriteOnly))
1524 {
1525 KSNotification::sorry(i18n("Unable to write to file 'indihosts.xml'\nAny changes "
1526 "to INDI hosts configurations will not be saved."),
1527 i18n("Could Not Open File"));
1528 return;
1529 }
1530
1531 QTextStream outstream(&file);
1532
1533 //for (uint i= 0; i < ksw->data()->INDIHostsList.count(); i++)
1534 foreach (DriverInfo *host, driversList)
1535 {
1536 if (host->getDriverSource() != HOST_SOURCE)
1537 continue;
1538
1539 hostData = "<INDIHost name='";
1540 hostData += host->getName();
1541 hostData += "' hostname='";
1542 hostData += host->getHost();
1543 hostData += "' port='";
1544 hostData += host->getPort();
1545 hostData += "' />\n";
1546
1547 outstream << hostData;
1548 }
1549
1550 file.close();
1551 }
1552
findDriverByName(const QString & name)1553 DriverInfo *DriverManager::findDriverByName(const QString &name)
1554 {
1555 for (auto &dv : driversList)
1556 {
1557 if (dv->getName() == name)
1558 return dv;
1559 }
1560
1561 return nullptr;
1562 }
1563
findDriverByLabel(const QString & label)1564 DriverInfo *DriverManager::findDriverByLabel(const QString &label)
1565 {
1566 for (auto &dv : driversList)
1567 {
1568 if (dv->getLabel() == label)
1569 return dv;
1570 }
1571
1572 return nullptr;
1573 }
1574
findDriverByExec(const QString & exec)1575 DriverInfo *DriverManager::findDriverByExec(const QString &exec)
1576 {
1577 for (auto &dv : driversList)
1578 {
1579 if (dv->getExecutable() == exec)
1580 return dv;
1581 }
1582
1583 return nullptr;
1584 }
1585
getUniqueDeviceLabel(const QString & label)1586 QString DriverManager::getUniqueDeviceLabel(const QString &label)
1587 {
1588 int nset = 0;
1589 QString uniqueLabel = label;
1590
1591 for (auto &cm : clients)
1592 {
1593 auto &devices = cm->getDevices();
1594
1595 for (auto &dv : devices)
1596 {
1597 if (label == QString(dv->getDeviceName()))
1598 nset++;
1599 }
1600 }
1601 if (nset > 0)
1602 uniqueLabel = QString("%1 %2").arg(label).arg(nset + 1);
1603
1604 return uniqueLabel;
1605 }
1606
getDriverList() const1607 QJsonArray DriverManager::getDriverList() const
1608 {
1609 QJsonArray driverArray;
1610
1611 for (const auto &drv : driversList)
1612 driverArray.append(drv->toJson());
1613
1614 return driverArray;
1615 }
1616
restartDriver(DriverInfo * dv)1617 bool DriverManager::restartDriver(DriverInfo *dv)
1618 {
1619 for (auto &oneServer : servers)
1620 {
1621 if (oneServer->contains(dv))
1622 {
1623 return oneServer->restartDriver(dv);
1624 }
1625 }
1626
1627 return false;
1628 }
1629