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

KDE's Doxygen guidelines are available online.