Kstars

drivermanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2012 Jasem Mutlaq <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "drivermanager.h"
8 
9 #include "config-kstars.h"
10 
11 #include "clientmanager.h"
12 #include "driverinfo.h"
13 #include "guimanager.h"
14 #include "indilistener.h"
15 #include "kspaths.h"
16 #include "kstars.h"
17 #include "indidbus.h"
18 #include "kstarsdata.h"
19 #include "Options.h"
20 #include "servermanager.h"
21 #include "ui_indihostconf.h"
22 #include "auxiliary/ksnotification.h"
23 
24 #include <basedevice.h>
25 
26 #ifndef KSTARS_LITE
27 #include <KMessageBox>
28 #include <KActionCollection>
29 #include <KNotifications/KNotification>
30 #endif
31 
32 #include <QTcpServer>
33 #include <QtConcurrent>
34 #include <indi_debug.h>
35 
36 #define INDI_MAX_TRIES 2
37 #define ERRMSG_SIZE 1024
38 
39 DriverManagerUI::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 
60 void 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 
70 DriverManager *DriverManager::_DriverManager = nullptr;
71 INDIDBus *DriverManager::_INDIDBus = nullptr;
72 
73 DriverManager *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 
86 void DriverManager::release()
87 {
88  delete (_DriverManager);
89  delete (_INDIDBus);
90  _DriverManager = nullptr;
91  _INDIDBus = nullptr;
92 }
93 
94 DriverManager::DriverManager(QWidget *parent) : QDialog(parent)
95 {
96 #ifdef Q_OS_OSX
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 
144 DriverManager::~DriverManager()
145 {
146  clearServers();
147 }
148 
149 void 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 
333 void 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 
382 void 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  QtConcurrent::run(serverManager, &ServerManager::startDriver, serverManager->pendingDrivers().first());
387 }
388 
389 void DriverManager::processDriverStartup(const QSharedPointer<DriverInfo> &driver)
390 {
391  emit driverStarted(driver);
392 
393  auto manager = driver->getServerManager();
394  // Do we have more pending drivers?
395  if (manager->pendingDrivers().count() > 0)
396  {
397  QtConcurrent::run(manager, &ServerManager::startDriver, manager->pendingDrivers().first());
398  return;
399  }
400 
401  // Nothing to do more if SERVER ONLY
402  if (connectionMode == SERVER_ONLY)
403  {
404  return;
405  }
406 
407  // Otherwise proceed to start Client Manager
408  startClientManager(manager->managedDrivers(), manager->getHost(), manager->getPort());
409 }
410 
411 void DriverManager::processDriverFailure(const QSharedPointer<DriverInfo> &driver, const QString &message)
412 {
413  emit driverFailed(driver, message);
414 
415  qCWarning(KSTARS_INDI) << "Driver" << driver->getName() << "failed to start. Retrying in 5 seconds...";
416 
417  QTimer::singleShot(5000, this, [driver]()
418  {
419  auto manager = driver->getServerManager();
420  // Do we have more pending drivers?
421  if (manager->pendingDrivers().count() > 0)
422  {
423  QtConcurrent::run(manager, &ServerManager::startDriver, manager->pendingDrivers().first());
424  return;
425  }
426  });
427 }
428 
429 void DriverManager::startClientManager(const QList<QSharedPointer<DriverInfo>> &qdv, const QString &host, int port)
430 {
431  auto clientManager = new ClientManager();
432 
433  for (auto &dv : qdv)
434  clientManager->appendManagedDriver(dv);
435 
436  connect(clientManager, &ClientManager::started, this, &DriverManager::setClientStarted, Qt::UniqueConnection);
437  connect(clientManager, &ClientManager::failed, this, &DriverManager::setClientFailed, Qt::UniqueConnection);
438  connect(clientManager, &ClientManager::terminated, this, &DriverManager::setClientTerminated, Qt::UniqueConnection);
439 
440  clientManager->setServer(host.toLatin1().constData(), port);
441 
442  GUIManager::Instance()->addClient(clientManager);
443  INDIListener::Instance()->addClient(clientManager);
444 
445  clientManager->establishConnection();
446 }
447 
448 void DriverManager::stopDevices(const QList<QSharedPointer<DriverInfo>> &dList)
449 {
450  qCDebug(KSTARS_INDI) << "INDI: Stopping local drivers...";
451 
452  // #1 Disconnect all clients
453  for (QSharedPointer<DriverInfo>dv : dList)
454  {
455  ClientManager *cm = dv->getClientManager();
456 
457  if (cm == nullptr)
458  continue;
459 
460  cm->removeManagedDriver(dv);
461 
462  if (cm->count() == 0)
463  {
464  GUIManager::Instance()->removeClient(cm);
465  INDIListener::Instance()->removeClient(cm);
466  cm->disconnectServer();
467  clients.removeOne(cm);
468  cm->deleteLater();
469 
472  }
473  }
474 
475  // #2 Disconnect all servers
476  for (QSharedPointer<DriverInfo>dv : dList)
477  {
478  ServerManager *sm = dv->getServerManager();
479 
480  if (sm != nullptr)
481  {
482  sm->stopDriver(dv);
483 
484  if (sm->size() == 0)
485  {
486  sm->stop();
487  servers.removeOne(sm);
488  sm->deleteLater();
489  }
490  }
491  }
492 
493  // Reset current port
494  currentPort = Options::serverPortStart() - 1;
495 
496  updateMenuActions();
497 }
498 
499 void DriverManager::disconnectClients()
500 {
501  for (auto &clientManager : clients)
502  {
503  clientManager->disconnectAll();
504  clientManager->disconnect();
505  }
506 }
507 
508 void DriverManager::clearServers()
509 {
510  for (auto &serverManager : servers)
511  serverManager->stop();
512 
513  qDeleteAll(servers);
514 }
515 
516 void DriverManager::activateRunService()
517 {
518  processLocalTree(true);
519 }
520 
521 void DriverManager::activateStopService()
522 {
523  processLocalTree(false);
524 }
525 
526 void DriverManager::activateHostConnection()
527 {
528  processRemoteTree(true);
529 }
530 
531 void DriverManager::activateHostDisconnection()
532 {
533  processRemoteTree(false);
534 }
535 
536 ClientManager *DriverManager::getClientManager(const QSharedPointer<DriverInfo> &driver)
537 {
538  return driver->getClientManager();
539 }
540 
541 void DriverManager::updateLocalTab()
542 {
543  if (ui->localTreeWidget->currentItem() == nullptr)
544  return;
545 
546  QString currentDriver = ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN);
547 
548  auto device = std::find_if(driversList.begin(), driversList.end(), [currentDriver](const auto & oneDriver)
549  {
550  return currentDriver == oneDriver->getLabel();
551  });
552  if (device != driversList.end())
553  processDeviceStatus(*device);
554 }
555 
556 void DriverManager::updateClientTab()
557 {
558  QTreeWidgetItem *item = ui->clientTreeWidget->currentItem();
559 
560  if (item == nullptr)
561  return;
562 
563  auto hostname = item->text(HOST_NAME_COLUMN);
564  int port = item->text(HOST_PORT_COLUMN).toInt();
565 
566  auto device = std::find_if(driversList.begin(), driversList.end(), [hostname, port](const auto & oneDriver)
567  {
568  return hostname == oneDriver->getName() && port == oneDriver->getPort();
569  });
570  if (device != driversList.end())
571  processDeviceStatus(*device);
572 }
573 
574 void DriverManager::processLocalTree(bool dState)
575 {
576  QList<QSharedPointer<DriverInfo>> processed_devices;
577 
578  int port;
579  bool portOK = false;
580 
581  connectionMode = ui->localR->isChecked() ? SERVER_CLIENT : SERVER_ONLY;
582 
583  for (auto &item : ui->localTreeWidget->selectedItems())
584  {
585  for (auto &device : driversList)
586  {
587  port = -1;
588 
589  //device->state = (dev_request == DriverInfo::DEV_TERMINATE) ? DriverInfo::DEV_START : DriverInfo::DEV_TERMINATE;
590  if (item->text(LOCAL_NAME_COLUMN) == device->getLabel() &&
591  device->getServerState() != dState)
592  {
593  processed_devices.append(device);
594 
595  // N.B. If multiple devices are selected to run under one device manager
596  // then we select the port for the first device that has a valid port
597  // entry, the rest are ignored.
598  if (port == -1 && item->text(LOCAL_PORT_COLUMN).isEmpty() == false)
599  {
600  port = item->text(LOCAL_PORT_COLUMN).toInt(&portOK);
601  // If we encounter conversion error, we abort
602  if (portOK == false)
603  {
604  KSNotification::error(i18n("Invalid port entry: %1", item->text(LOCAL_PORT_COLUMN)));
605  return;
606  }
607  }
608 
609  device->setHostParameters("localhost", port);
610  }
611  }
612  }
613 
614  if (processed_devices.empty())
615  return;
616 
617  if (dState)
618  startDevices(processed_devices);
619  else
620  stopDevices(processed_devices);
621 }
622 
623 void DriverManager::setClientFailed(const QString &message)
624 {
625  auto client = qobject_cast<ClientManager*>(sender());
626 
627  if (client == nullptr)
628  return;
629 
630  auto host = client->getHost();
631  auto port = client->getPort();
632 
634 
635  emit clientFailed(host, port, message);
636 
637  GUIManager::Instance()->removeClient(client);
638  INDIListener::Instance()->removeClient(client);
639 
640  KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::INDI);
641 
642  clients.removeOne(client);
643  client->deleteLater();
644 
645  if (connectionMode == SERVER_CLIENT)
646  activateStopService();
647 
648  updateMenuActions();
649  updateLocalTab();
650 }
651 
652 void DriverManager::setClientTerminated(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 clientTerminated(host, port, message);
665 
666  GUIManager::Instance()->removeClient(client);
667  INDIListener::Instance()->removeClient(client);
668 
669  KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::INDI, KSNotification::Alert);
670 
671  clients.removeOne(client);
672  client->deleteLater();
673 
674  updateMenuActions();
675  updateLocalTab();
676  updateClientTab();
677 }
678 
679 void DriverManager::setServerStarted()
680 {
681  auto manager = qobject_cast<ServerManager*>(sender());
682  qCDebug(KSTARS_INDI) << "INDI: INDI Server started locally on port " << manager->getPort();
683  startLocalDrivers(manager);
684  emit serverStarted(manager->getHost(), manager->getPort());
685 }
686 
687 void DriverManager::setServerFailed(const QString &message)
688 {
689  auto server = qobject_cast<ServerManager *>(sender());
690 
691  if (server == nullptr)
692  return;
693 
694  for (auto &dv : driversList)
695  {
696  if (dv->getServerManager() == server)
697  {
698  dv->reset();
699  }
700  }
701 
702  if (server->getMode() == SERVER_ONLY)
703  KSNotification::error(message);
704 
705  emit serverFailed(server->getHost(), server->getPort(), message);
706  servers.removeOne(server);
707  server->deleteLater();
708 
709  updateLocalTab();
710 }
711 
712 void DriverManager::setServerTerminated(const QString &message)
713 {
714  auto server = qobject_cast<ServerManager *>(sender());
715 
716  if (server == nullptr)
717  return;
718 
719  for (auto &dv : driversList)
720  {
721  if (dv->getServerManager() == server)
722  {
723  dv->reset();
724  }
725  }
726 
727  if (server->getMode() == SERVER_ONLY)
728  KSNotification::error(message);
729 
730  emit serverTerminated(server->getHost(), server->getPort(), message);
731  servers.removeOne(server);
732  server->deleteLater();
733 
734  updateLocalTab();
735 }
736 
737 void DriverManager::processRemoteTree(bool dState)
738 {
739  QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
740  if (!currentItem)
741  return;
742 
743  for (auto &dv : driversList)
744  {
745  if (dv->getDriverSource() != HOST_SOURCE)
746  continue;
747 
748  //qDebug() << Q_FUNC_INFO << "Current item port " << currentItem->text(HOST_PORT_COLUMN) << " current dev " << dv->getName() << " -- port " << dv->getPort() << Qt::endl;
749  //qDebug() << Q_FUNC_INFO << "dState is : " << (dState ? "True" : "False") << Qt::endl;
750 
751  if (currentItem->text(HOST_NAME_COLUMN) == dv->getName() &&
752  currentItem->text(HOST_PORT_COLUMN).toInt() == dv->getPort())
753  {
754  // Nothing changed, return
755  if (dv->getClientState() == dState)
756  return;
757 
758  // connect to host
759  if (dState)
760  connectRemoteHost(dv);
761  // Disconnect form host
762  else
763  disconnectRemoteHost(dv);
764  }
765  }
766 }
767 
768 void DriverManager::connectRemoteHost(const QSharedPointer<DriverInfo> &driver)
769 {
770  auto host = driver->getHost();
771  auto port = driver->getPort();
772 
773  auto clientManager = new ClientManager();
774 
775  clientManager->appendManagedDriver(driver);
776 
777  connect(clientManager, &ClientManager::started, this, &DriverManager::setClientStarted, Qt::UniqueConnection);
778  connect(clientManager, &ClientManager::failed, this, &DriverManager::setClientFailed, Qt::UniqueConnection);
779  connect(clientManager, &ClientManager::terminated, this, &DriverManager::setClientTerminated, Qt::UniqueConnection);
780 
781  clientManager->setServer(host.toLatin1().constData(), port);
782 
783  GUIManager::Instance()->addClient(clientManager);
784  INDIListener::Instance()->addClient(clientManager);
785 
786  clientManager->establishConnection();
787 }
788 
789 void DriverManager::setClientStarted()
790 {
791  auto clientManager = qobject_cast<ClientManager *>(sender());
792  if (clientManager == nullptr)
793  return;
794 
795  clients.append(clientManager);
796  updateLocalTab();
797  updateMenuActions();
798 
799  KSNotification::event(QLatin1String("ConnectionSuccessful"),
800  i18n("Connected to INDI server"));
801 
802  emit clientStarted(clientManager->getHost(), clientManager->getPort());
803 }
804 
805 bool DriverManager::disconnectRemoteHost(const QSharedPointer<DriverInfo> &driver)
806 {
807  ClientManager *clientManager = driver->getClientManager();
808 
809  if (clientManager)
810  {
811  clientManager->removeManagedDriver(driver);
812  clientManager->disconnectAll();
813  GUIManager::Instance()->removeClient(clientManager);
814  INDIListener::Instance()->removeClient(clientManager);
815  clients.removeOne(clientManager);
816  clientManager->deleteLater();
817  updateMenuActions();
818  return true;
819  }
820 
821  return false;
822 }
823 
824 void DriverManager::resizeDeviceColumn()
825 {
826  ui->localTreeWidget->resizeColumnToContents(0);
827 }
828 
829 void DriverManager::updateMenuActions()
830 {
831  // We iterate over devices, we enable INDI Control Panel if we have any active device
832  // We enable capture image sequence if we have any imaging device
833 
834  QAction *tmpAction = nullptr;
835  bool activeDevice = false;
836 
837  if (clients.size() > 0)
838  activeDevice = true;
839 
840  tmpAction = KStars::Instance()->actionCollection()->action("indi_cpl");
841  if (tmpAction != nullptr)
842  {
843  //qDebug() << Q_FUNC_INFO << "indi_cpl action set to active" << Qt::endl;
844  tmpAction->setEnabled(activeDevice);
845  }
846 }
847 
848 int DriverManager::getINDIPort(int customPort)
849 {
850 #ifdef Q_OS_WIN
851  qWarning() << "INDI server is currently not supported on Windows.";
852  return -1;
853 #else
854  int lastPort = Options::serverPortEnd();
855  bool success = false;
856  currentPort++;
857 
858  // recycle
859  if (currentPort > lastPort)
860  currentPort = Options::serverPortStart();
861 
862  QTcpServer temp_server;
863 
864  if (customPort != -1)
865  {
866  success = temp_server.listen(QHostAddress::LocalHost, customPort);
867  if (success)
868  {
869  temp_server.close();
870  return customPort;
871  }
872  else
873  return -1;
874  }
875 
876  for (; currentPort <= lastPort; currentPort++)
877  {
878  success = temp_server.listen(QHostAddress::LocalHost, currentPort);
879  if (success)
880  {
881  temp_server.close();
882  return currentPort;
883  }
884  }
885  return -1;
886 #endif
887 }
888 
889 bool DriverManager::readINDIHosts()
890 {
891  QString indiFile("indihosts.xml");
892  //QFile localeFile;
893  QFile file;
894  char errmsg[1024];
895  char c;
896  LilXML *xmlParser = newLilXML();
897  XMLEle *root = nullptr;
898  XMLAtt *ap = nullptr;
899  QString hName, hHost, hPort;
900 
901  lastGroup = nullptr;
902  file.setFileName(KSPaths::locate(QStandardPaths::AppLocalDataLocation, indiFile));
903  if (file.fileName().isEmpty() || !file.open(QIODevice::ReadOnly))
904  {
905  delLilXML(xmlParser);
906  return false;
907  }
908 
909  while (file.getChar(&c))
910  {
911  root = readXMLEle(xmlParser, c, errmsg);
912 
913  if (root)
914  {
915  // Get host name
916  ap = findXMLAtt(root, "name");
917  if (!ap)
918  {
919  delLilXML(xmlParser);
920  return false;
921  }
922 
923  hName = QString(valuXMLAtt(ap));
924 
925  // Get host name
926  ap = findXMLAtt(root, "hostname");
927 
928  if (!ap)
929  {
930  delLilXML(xmlParser);
931  return false;
932  }
933 
934  hHost = QString(valuXMLAtt(ap));
935 
936  ap = findXMLAtt(root, "port");
937 
938  if (!ap)
939  {
940  delLilXML(xmlParser);
941  return false;
942  }
943 
944  hPort = QString(valuXMLAtt(ap));
945 
946  QSharedPointer<DriverInfo> dv(new DriverInfo(hName));
947  dv->setHostParameters(hHost, hPort.toInt());
948  dv->setDriverSource(HOST_SOURCE);
949 
950  connect(dv.get(), &DriverInfo::deviceStateChanged, this, [dv, this]()
951  {
952  processDeviceStatus(dv);
953  });
954 
955  driversList.append(dv);
956 
957  QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget, lastGroup);
958  lastGroup = item;
959  item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
960  item->setText(HOST_NAME_COLUMN, hName);
961  item->setText(HOST_PORT_COLUMN, hPort);
962 
963  delXMLEle(root);
964  }
965  else if (errmsg[0])
966  {
967  qDebug() << Q_FUNC_INFO << errmsg;
968  delLilXML(xmlParser);
969  return false;
970  }
971  }
972 
973  delLilXML(xmlParser);
974 
975  return true;
976 }
977 
978 bool DriverManager::readXMLDrivers()
979 {
980  QDir indiDir;
981  QString driverName;
982 
983  QString driversDir = Options::indiDriversDir();
984 #ifdef Q_OS_OSX
985  if (Options::indiDriversAreInternal())
986  driversDir =
987  QCoreApplication::applicationDirPath() + "/../Resources/DriverSupport";
988 #endif
989 
990  if (indiDir.cd(driversDir) == false)
991  {
992  KSNotification::error(i18n("Unable to find INDI drivers directory: %1\nPlease "
993  "make sure to set the correct "
994  "path in KStars configuration",
995  driversDir));
996  return false;
997  }
998 
999  indiDir.setNameFilters(QStringList() << "indi_*.xml"
1000  << "drivers.xml");
1002  QFileInfoList list = indiDir.entryInfoList();
1003 
1004  for (auto &fileInfo : list)
1005  {
1006  if (fileInfo.fileName().endsWith(QLatin1String("_sk.xml")))
1007  continue;
1008 
1009  processXMLDriver(fileInfo.absoluteFilePath());
1010  }
1011 
1012  // JM 2022.08.24: Process local source last as INDI sources should have higher priority than KStars own database.
1013  processXMLDriver(QLatin1String(":/indidrivers.xml"));
1014 
1015  return true;
1016 }
1017 
1018 void DriverManager::processXMLDriver(const QString &driverName)
1019 {
1020  QFile file(driverName);
1022  {
1023  KSNotification::error(i18n("Failed to open INDI Driver file: %1", driverName));
1024  return;
1025  }
1026 
1027  char errmsg[ERRMSG_SIZE];
1028  char c;
1029  LilXML *xmlParser = newLilXML();
1030  XMLEle *root = nullptr;
1031  XMLEle *ep = nullptr;
1032 
1033  if (driverName.endsWith(QLatin1String("drivers.xml")))
1034  driverSource = PRIMARY_XML;
1035  else
1036  driverSource = THIRD_PARTY_XML;
1037 
1038  while (file.getChar(&c))
1039  {
1040  root = readXMLEle(xmlParser, c, errmsg);
1041 
1042  if (root)
1043  {
1044  // If the XML file is using the INDI Library v1.3+ format
1045  if (!strcmp(tagXMLEle(root), "driversList"))
1046  {
1047  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
1048  {
1049  if (!buildDeviceGroup(ep, errmsg))
1050  prXMLEle(stderr, ep, 0);
1051  }
1052  }
1053  // If using the older format
1054  else
1055  {
1056  if (!buildDeviceGroup(root, errmsg))
1057  prXMLEle(stderr, root, 0);
1058  }
1059 
1060  delXMLEle(root);
1061  }
1062  else if (errmsg[0])
1063  {
1064  qCDebug(KSTARS_INDI) << QString(errmsg);
1065  delLilXML(xmlParser);
1066  return;
1067  }
1068  }
1069 
1070  delLilXML(xmlParser);
1071 }
1072 
1073 bool DriverManager::buildDeviceGroup(XMLEle *root, char errmsg[])
1074 {
1075  XMLAtt *ap;
1076  XMLEle *ep;
1077  QString groupName;
1078  QTreeWidgetItem *group;
1079  DeviceFamily groupType = KSTARS_TELESCOPE;
1080 
1081  // avoid overflow
1082  if (strlen(tagXMLEle(root)) > 1024)
1083  return false;
1084 
1085  // Get device grouping name
1086  ap = findXMLAtt(root, "group");
1087 
1088  if (!ap)
1089  {
1090  snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute",
1091  tagXMLEle(root));
1092  return false;
1093  }
1094 
1095  groupName = valuXMLAtt(ap);
1096  groupType = DeviceFamilyLabels.key(groupName);
1097 
1098 #ifndef HAVE_CFITSIO
1099  // We do not create these groups if we don't have CFITSIO support
1100  if (groupType == KSTARS_CCD || groupType == KSTARS_VIDEO)
1101  return true;
1102 #endif
1103 
1104  // Find if the group already exists
1105  QList<QTreeWidgetItem *> treeList =
1106  ui->localTreeWidget->findItems(groupName, Qt::MatchExactly);
1107  if (!treeList.isEmpty())
1108  group = treeList[0];
1109  else
1110  group = new QTreeWidgetItem(ui->localTreeWidget, lastGroup);
1111 
1112  group->setText(0, groupName);
1113  lastGroup = group;
1114 
1115  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
1116  {
1117  if (!buildDriverElement(ep, group, groupType, errmsg))
1118  return false;
1119  }
1120 
1121  return true;
1122 }
1123 
1124 bool DriverManager::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup,
1125  DeviceFamily groupType, char errmsg[])
1126 {
1127  XMLAtt *ap;
1128  XMLEle *el;
1129  QString label;
1130  QString driver;
1131  QString version;
1132  // N.B. NOT an i18n string.
1133  QString manufacturer("Others");
1134  QString name;
1135  QString port;
1136  QString skel;
1137  QVariantMap vMap;
1138  //double focal_length(-1), aperture(-1);
1139 
1140  ap = findXMLAtt(root, "label");
1141  if (!ap)
1142  {
1143  snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute",
1144  tagXMLEle(root));
1145  return false;
1146  }
1147 
1148  label = valuXMLAtt(ap);
1149 
1150  // Label is unique, so if we have the same label, we simply ignore
1151  if (findDriverByLabel(label) != nullptr)
1152  return true;
1153 
1154  ap = findXMLAtt(root, "manufacturer");
1155  if (ap)
1156  manufacturer = valuXMLAtt(ap);
1157 
1158  // Search for optional port attribute
1159  ap = findXMLAtt(root, "port");
1160  if (ap)
1161  port = valuXMLAtt(ap);
1162 
1163  // Search for skel file, if any
1164  // Search for optional port attribute
1165  ap = findXMLAtt(root, "skel");
1166  if (ap)
1167  skel = valuXMLAtt(ap);
1168 
1169  // Find MDPD: Multiple Devices Per Driver
1170  ap = findXMLAtt(root, "mdpd");
1171  if (ap)
1172  {
1173  bool mdpd = false;
1174  mdpd = (QString(valuXMLAtt(ap)) == QString("true")) ? true : false;
1175  vMap.insert("mdpd", mdpd);
1176  }
1177 
1178  el = findXMLEle(root, "driver");
1179 
1180  if (!el)
1181  return false;
1182 
1183  driver = pcdataXMLEle(el);
1184 
1185  ap = findXMLAtt(el, "name");
1186  if (!ap)
1187  {
1188  snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a name attribute",
1189  tagXMLEle(el));
1190  return false;
1191  }
1192 
1193  name = valuXMLAtt(ap);
1194 
1195  el = findXMLEle(root, "version");
1196 
1197  if (!el)
1198  return false;
1199 
1200  version = pcdataXMLEle(el);
1201  bool versionOK = false;
1202  version.toDouble(&versionOK);
1203  if (versionOK == false)
1204  version = "1.0";
1205 
1206  bool driverIsAvailable = checkDriverAvailability(driver);
1207 
1208  vMap.insert("LOCALLY_AVAILABLE", driverIsAvailable);
1209  QIcon remoteIcon = QIcon::fromTheme("network-modem");
1210 
1211  QTreeWidgetItem *device = new QTreeWidgetItem(DGroup);
1212 
1213  device->setText(LOCAL_NAME_COLUMN, label);
1214  if (driverIsAvailable)
1215  device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
1216  else
1217  device->setIcon(LOCAL_STATUS_COLUMN, remoteIcon);
1218  device->setText(LOCAL_VERSION_COLUMN, version);
1219  device->setText(LOCAL_PORT_COLUMN, port);
1220 
1221  //if ((driverSource == PRIMARY_XML) && driversStringList.contains(driver) == false)
1222  if (groupType == KSTARS_TELESCOPE && driversStringList.contains(driver) == false)
1223  driversStringList.append(driver);
1224 
1225  QSharedPointer<DriverInfo> dv(new DriverInfo(name));
1226 
1227  dv->setLabel(label);
1228  dv->setVersion(version);
1229  dv->setExecutable(driver);
1230  dv->setManufacturer(manufacturer);
1231  dv->setSkeletonFile(skel);
1232  dv->setType(groupType);
1233  dv->setDriverSource(driverSource);
1234  dv->setUserPort(port.isEmpty() ? 7624 : port.toInt());
1235  dv->setAuxInfo(vMap);
1236 
1237  connect(dv.get(), &DriverInfo::deviceStateChanged, this, [dv, this]()
1238  {
1239  processDeviceStatus(dv);
1240  });
1241 
1242  driversList.append(dv);
1243 
1244  return true;
1245 }
1246 
1247 bool DriverManager::checkDriverAvailability(const QString &driver)
1248 {
1249  QString indiServerDir = Options::indiServer();
1250  if (Options::indiServerIsInternal())
1251  indiServerDir = QCoreApplication::applicationDirPath();
1252  else
1253  indiServerDir = QFileInfo(Options::indiServer()).dir().path();
1254 
1255  QFile driverFile(indiServerDir + '/' + driver);
1256 
1257  if (driverFile.exists() == false)
1258  return (!QStandardPaths::findExecutable(indiServerDir + '/' + driver).isEmpty());
1259 
1260  return true;
1261 }
1262 
1263 void DriverManager::updateCustomDrivers()
1264 {
1265  for (const QVariantMap &oneDriver : m_CustomDrivers->customDrivers())
1266  {
1267  QSharedPointer<DriverInfo> dv(new DriverInfo(oneDriver["Name"].toString()));
1268  dv->setLabel(oneDriver["Label"].toString());
1269  dv->setUniqueLabel(dv->getLabel());
1270  dv->setExecutable(oneDriver["Exec"].toString());
1271  dv->setVersion(oneDriver["Version"].toString());
1272  dv->setManufacturer(oneDriver["Manufacturer"].toString());
1273  dv->setType(DeviceFamilyLabels.key(oneDriver["Family"].toString()));
1274  dv->setDriverSource(CUSTOM_SOURCE);
1275 
1276  bool driverIsAvailable = checkDriverAvailability(oneDriver["Exec"].toString());
1277  QVariantMap vMap;
1278  vMap.insert("LOCALLY_AVAILABLE", driverIsAvailable);
1279  dv->setAuxInfo(vMap);
1280 
1281  driversList.append(dv);
1282  }
1283 }
1284 
1285 // JM 2018-07-23: Disabling the old custom drivers method
1286 #if 0
1287 void DriverManager::updateCustomDrivers()
1288 {
1289  QString label;
1290  QString driver;
1291  QString version;
1292  QString name;
1293  QTreeWidgetItem *group = nullptr;
1294  QTreeWidgetItem *widgetDev = nullptr;
1295  QVariantMap vMap;
1296  QSharedPointer<DriverInfo>drv = nullptr;
1297 
1298  // Find if the group already exists
1299  QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems("Telescopes", Qt::MatchExactly);
1300  if (!treeList.isEmpty())
1301  group = treeList[0];
1302  else
1303  return;
1304 
1305  KStarsData::Instance()->logObject()->readAll();
1306 
1307  // Find custom telescope to ADD/UPDATE
1308  foreach (OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList()))
1309  {
1310  name = label = s->name();
1311 
1312  if (s->driver() == i18n("None"))
1313  continue;
1314 
1315  // If driver already exists, just update values
1316  if ((drv = findDriverByLabel(label)))
1317  {
1318  if (s->aperture() > 0 && s->focalLength() > 0)
1319  {
1320  vMap.insert("TELESCOPE_APERTURE", s->aperture());
1321  vMap.insert("TELESCOPE_FOCAL_LENGTH", s->focalLength());
1322  drv->setAuxInfo(vMap);
1323  }
1324 
1325  drv->setExecutable(s->driver());
1326 
1327  continue;
1328  }
1329 
1330  driver = s->driver();
1331  version = QString("1.0");
1332 
1333  QTreeWidgetItem *device = new QTreeWidgetItem(group);
1334  device->setText(LOCAL_NAME_COLUMN, QString(label));
1335  device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
1336  device->setText(LOCAL_VERSION_COLUMN, QString(version));
1337 
1338  QSharedPointer<DriverInfo>dv = new DriverInfo(name);
1339 
1340  dv->setLabel(label);
1341  dv->setExecutable(driver);
1342  dv->setVersion(version);
1343  dv->setType(KSTARS_TELESCOPE);
1344  dv->setDriverSource(EM_XML);
1345 
1346  if (s->aperture() > 0 && s->focalLength() > 0)
1347  {
1348  vMap.insert("TELESCOPE_APERTURE", s->aperture());
1349  vMap.insert("TELESCOPE_FOCAL_LENGTH", s->focalLength());
1350  dv->setAuxInfo(vMap);
1351  }
1352 
1353  connect(dv, SIGNAL(deviceStateChanged(DriverInfo*)), this, SLOT(processDeviceStatus(DriverInfo*)));
1354  driversList.append(dv);
1355  }
1356 
1357  // Find custom telescope to REMOVE
1358  foreach (QSharedPointer<DriverInfo>dev, driversList)
1359  {
1360  // If it's from primary xml file or it is in a running state, continue.
1361  if (dev->getDriverSource() != EM_XML || dev->getClientState())
1362  continue;
1363 
1364  if (KStarsData::Instance()->logObject()->findScopeByName(dev->getName()))
1365  continue;
1366 
1367  // Find if the group already exists
1368  QList<QTreeWidgetItem *> devList =
1369  ui->localTreeWidget->findItems(dev->getLabel(), Qt::MatchExactly | Qt::MatchRecursive);
1370  if (!devList.isEmpty())
1371  {
1372  widgetDev = devList[0];
1373  group->removeChild(widgetDev);
1374  }
1375  else
1376  return;
1377 
1378  driversList.removeOne(dev);
1379  delete (dev);
1380  }
1381 }
1382 #endif
1383 
1384 void DriverManager::addINDIHost()
1385 {
1386  QDialog hostConfDialog;
1387  Ui::INDIHostConf hostConf;
1388  hostConf.setupUi(&hostConfDialog);
1389  hostConfDialog.setWindowTitle(i18nc("@title:window", "Add Host"));
1390  bool portOk = false;
1391 
1392  if (hostConfDialog.exec() == QDialog::Accepted)
1393  {
1394  QSharedPointer<DriverInfo> hostItem(new DriverInfo(hostConf.nameIN->text()));
1395 
1396  hostConf.portnumber->text().toInt(&portOk);
1397 
1398  if (portOk == false)
1399  {
1400  KSNotification::error(i18n("Error: the port number is invalid."));
1401  return;
1402  }
1403 
1404  hostItem->setHostParameters(hostConf.hostname->text(),
1405  hostConf.portnumber->text().toInt());
1406 
1407  //search for duplicates
1408  //for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++)
1409  foreach (QSharedPointer<DriverInfo>host, driversList)
1410  if (hostItem->getName() == host->getName() &&
1411  hostItem->getPort() == host->getPort())
1412  {
1413  KSNotification::error(i18n("Host: %1 Port: %2 already exists.",
1414  hostItem->getName(), hostItem->getPort()));
1415  return;
1416  }
1417 
1418  hostItem->setDriverSource(HOST_SOURCE);
1419 
1420  connect(hostItem.get(), &DriverInfo::deviceStateChanged, this, [hostItem, this]()
1421  {
1422  processDeviceStatus(hostItem);
1423  });
1424 
1425  driversList.append(hostItem);
1426 
1427  QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget);
1428  item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
1429  item->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
1430  item->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
1431  }
1432 
1433  saveHosts();
1434 }
1435 
1436 void DriverManager::modifyINDIHost()
1437 {
1438  QDialog hostConfDialog;
1439  Ui::INDIHostConf hostConf;
1440  hostConf.setupUi(&hostConfDialog);
1441  hostConfDialog.setWindowTitle(i18nc("@title:window", "Modify Host"));
1442 
1443  QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
1444 
1445  if (currentItem == nullptr)
1446  return;
1447 
1448  for (auto &host : driversList)
1449  {
1450  if (currentItem->text(HOST_NAME_COLUMN) == host->getName() &&
1451  currentItem->text(HOST_PORT_COLUMN).toInt() == host->getPort())
1452  {
1453  hostConf.nameIN->setText(host->getName());
1454  hostConf.hostname->setText(host->getHost());
1455  hostConf.portnumber->setText(QString::number(host->getPort()));
1456 
1457  if (hostConfDialog.exec() == QDialog::Accepted)
1458  {
1459  host->setName(hostConf.nameIN->text());
1460  host->setHostParameters(hostConf.hostname->text(),
1461  hostConf.portnumber->text().toInt());
1462 
1463  currentItem->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
1464  currentItem->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
1465 
1466  saveHosts();
1467  return;
1468  }
1469  }
1470  }
1471 }
1472 
1473 void DriverManager::removeINDIHost()
1474 {
1475  if (ui->clientTreeWidget->currentItem() == nullptr)
1476  return;
1477 
1478  foreach (QSharedPointer<DriverInfo>host, driversList)
1479  if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) ==
1480  host->getName() &&
1481  ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN).toInt() ==
1482  host->getPort())
1483  {
1484  if (host->getClientState())
1485  {
1486  KSNotification::error(
1487  i18n("You need to disconnect the client before removing it."));
1488  return;
1489  }
1490 
1492  nullptr,
1493  i18n("Are you sure you want to remove the %1 client?",
1494  ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN)),
1495  i18n("Delete Confirmation"),
1496  KStandardGuiItem::del()) != KMessageBox::Continue)
1497  return;
1498 
1499  driversList.removeOne(host);
1500  delete (ui->clientTreeWidget->currentItem());
1501  break;
1502  }
1503 
1504  saveHosts();
1505 }
1506 
1507 void DriverManager::saveHosts()
1508 {
1509  QFile file;
1510  QString hostData;
1511 
1512  //determine filename in local user KDE directory tree.
1513  file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation))
1514  .filePath("indihosts.xml"));
1515 
1516  if (!file.open(QIODevice::WriteOnly))
1517  {
1518  KSNotification::sorry(i18n("Unable to write to file 'indihosts.xml'\nAny changes "
1519  "to INDI hosts configurations will not be saved."),
1520  i18n("Could Not Open File"));
1521  return;
1522  }
1523 
1524  QTextStream outstream(&file);
1525 
1526  //for (uint i= 0; i < ksw->data()->INDIHostsList.count(); i++)
1527  foreach (QSharedPointer<DriverInfo>host, driversList)
1528  {
1529  if (host->getDriverSource() != HOST_SOURCE)
1530  continue;
1531 
1532  hostData = "<INDIHost name='";
1533  hostData += host->getName();
1534  hostData += "' hostname='";
1535  hostData += host->getHost();
1536  hostData += "' port='";
1537  hostData += QString::number(host->getPort());
1538  hostData += "' />\n";
1539 
1540  outstream << hostData;
1541  }
1542 
1543  file.close();
1544 }
1545 
1546 QSharedPointer<DriverInfo> DriverManager::findDriverByName(const QString &name)
1547 {
1548  auto driver = std::find_if(driversList.begin(), driversList.end(), [name](auto & oneDriver)
1549  {
1550  return oneDriver->getName() == name;
1551  });
1552 
1553  if (driver != driversList.end())
1554  return *driver;
1555  else
1556  return QSharedPointer<DriverInfo>();
1557 }
1558 
1559 QSharedPointer<DriverInfo> DriverManager::findDriverByLabel(const QString &label)
1560 {
1561  auto driver = std::find_if(driversList.begin(), driversList.end(), [label](auto & oneDriver)
1562  {
1563  return oneDriver->getLabel() == label;
1564  });
1565 
1566  if (driver != driversList.end())
1567  return *driver;
1568  else
1569  return QSharedPointer<DriverInfo>();
1570 }
1571 
1572 QSharedPointer<DriverInfo> DriverManager::findDriverByExec(const QString &exec)
1573 {
1574  auto driver = std::find_if(driversList.begin(), driversList.end(), [exec](auto & oneDriver)
1575  {
1576  return oneDriver->getLabel() == exec;
1577  });
1578 
1579  if (driver != driversList.end())
1580  return *driver;
1581  else
1582  return QSharedPointer<DriverInfo>();
1583 }
1584 
1585 QString DriverManager::getUniqueDeviceLabel(const QString &label)
1586 {
1587  int nset = 0;
1588  QString uniqueLabel = label;
1589 
1590  for (auto &cm : clients)
1591  {
1592  auto devices = cm->getDevices();
1593 
1594  for (auto &dv : devices)
1595  {
1596  if (label == QString(dv.getDeviceName()))
1597  nset++;
1598  }
1599  }
1600  if (nset > 0)
1601  uniqueLabel = QString("%1 %2").arg(label).arg(nset + 1);
1602 
1603  return uniqueLabel;
1604 }
1605 
1606 QJsonArray DriverManager::getDriverList() const
1607 {
1608  QJsonArray driverArray;
1609 
1610  for (const auto &drv : driversList)
1611  driverArray.append(drv->toJson());
1612 
1613  return driverArray;
1614 }
1615 
1616 bool DriverManager::restartDriver(const QSharedPointer<DriverInfo> &driver)
1617 {
1618  for (auto &oneServer : servers)
1619  {
1620  if (oneServer->contains(driver))
1621  {
1622  return oneServer->restartDriver(driver);
1623  }
1624  }
1625 
1626  return false;
1627 }
void append(const T &value)
T & first()
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
void setFlags(Qt::ItemFlags flags)
QAction * action(const QString &name) const
QFuture< T > run(Function function,...)
QString number(int n, int base)
void slotSetDomeEnabled(bool enable)
slotSetDomeEnabled call when dome comes online or goes offline.
void close()
bool isNull() const const
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)
virtual bool open(QIODevice::OpenMode mode) override
void slotSetTelescopeEnabled(bool enable)
slotSetTelescopeEnabled call when telescope comes online or goes offline.
int count(const T &value) const const
void clear()
void clicked(bool checked)
QIcon fromTheme(const QString &name)
void setFilter(QDir::Filters filters)
QString applicationDirPath()
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
bool close()
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void removeManagedDriver(const QSharedPointer< DriverInfo > &driver)
removeManagedDriver Remove managed driver from pool of drivers managed by this client manager.
virtual QString fileName() const const override
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
static KStars * Instance()
Definition: kstars.h:123
QString findExecutable(const QString &executableName, const QStringList &paths)
bool empty() const const
void deleteLater()
NETWORKMANAGERQT_EXPORT QString hostname()
ItemIsSelectable
QString i18n(const char *text, const TYPE &arg...)
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
bool isEmpty() const const
void setWindowTitle(const QString &)
void setNameFilters(const QStringList &nameFilters)
int toInt(bool *ok, int base) const const
void setFileName(const QString &name)
UniqueConnection
void setText(int column, const QString &text)
bool isEmpty() const const
virtual int exec()
virtual void close() override
void setupUi(QWidget *widget)
static void beep(const QString &reason=QString(), QWidget *widget=nullptr)
bool listen(const QHostAddress &address, quint16 port)
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...
void itemClicked(QTreeWidgetItem *item, int column)
QString path() const const
QString label(StandardShortcut id)
ScriptableExtension * host() const
KGuiItem del()
void expanded(const QModelIndex &index)
OAL::Log * logObject()
Return log object.
Definition: kstarsdata.h:341
virtual KActionCollection * actionCollection() const
KDB_EXPORT KDbVersionInfo version()
void setEnabled(bool)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString & insert(int position, QChar ch)
const char * name(StandardAction id)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void removeChild(QTreeWidgetItem *child)
void itemDoubleClicked(QTreeWidgetItem *item, int column)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
void append(const QJsonValue &value)
MatchExactly
void setIcon(int column, const QIcon &icon)
QString text(int column) const const
bool cd(const QString &dirName)
QString message
void disconnectAll()
disconnectAll Disconnect from server and disconnect all BLOB Managers.
char * toString(const EngineQuery &query)
bool getChar(char *c)
QDir dir() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Oct 3 2023 04:02:49 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.