Kstars

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

KDE's Doxygen guidelines are available online.