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
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;
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
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;
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
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
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
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
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.
DriverInfo holds all metadata associated with a particular INDI driver.
Definition driverinfo.h:46
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:343
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
Information on telescope used in observation.
Definition scope.h:18
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)
KDB_EXPORT KDbVersionInfo version()
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()
void clicked(bool checked)
void setEnabled(bool)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
const char * constData() const const
QString applicationDirPath()
virtual int exec()
bool cd(const QString &dirName)
QFileInfoList entryInfoList(Filters filters, SortFlags sort) const const
QString path() 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
QDir dir() const const
QIcon fromTheme(const QString &name)
bool getChar(char *c)
void append(const QJsonValue &value)
void append(QList< T > &&value)
iterator begin()
qsizetype count() const const
bool empty() const const
iterator end()
T & first()
bool isEmpty() const const
bool removeOne(const AT &t)
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)
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) 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 setEnabled(bool)
void setupUi(QWidget *widget)
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:41 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.