Kstars

indidriver.cpp
1 /*
2  SPDX-FileCopyrightText: 2001 Jasem Mutlaq <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "indidriver.h"
8 
9 #include "config-kstars.h"
10 
11 #include "devicemanager.h"
12 #include "indidevice.h"
13 #include "indimenu.h"
14 #include "kstars.h"
15 #include "kstarsdata.h"
16 #include "ksutils.h"
17 #include "Options.h"
18 #include "ui_indihostconf.h"
19 #include "oal/log.h"
20 #include "oal/scope.h"
21 
22 #include <KMessageBox>
23 #include <KProcess>
24 #include <KActionCollection>
25 
26 #include <QRadioButton>
27 #include <QFile>
28 #include <QDir>
29 #include <QTextStream>
30 #include <QTreeWidget>
31 #include <QIcon>
32 #include <QDialog>
33 #include <QTcpServer>
34 #include <QMenu>
35 #include <QPushButton>
36 #include <QLineEdit>
37 #include <QAction>
38 #include <QStandardPaths>
39 
40 #include <unistd.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <netdb.h>
44 
45 #define MAX_RETRIES 3
46 #define ERRMSG_SIZE 1024
47 
48 DeviceManagerUI::DeviceManagerUI(QWidget *parent) : QFrame(parent)
49 {
50 #ifdef Q_OS_OSX
51  setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
52 #endif
53  setupUi(this);
54 
55  localTreeWidget->setSortingEnabled(false);
56  localTreeWidget->setRootIsDecorated(true);
57 
58  clientTreeWidget->setSortingEnabled(false);
59 
60  runningPix = QIcon::fromTheme("system-run");
61  stopPix = QIcon::fromTheme("dialog-cancel");
62  localMode = QIcon::fromTheme("computer");
63  serverMode = QIcon::fromTheme("network-server");
64 
65  connected = QIcon::fromTheme("network-connect");
66  disconnected = QIcon::fromTheme("network-disconnect");
67 
68  connect(localTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this,
69  SLOT(makePortEditable(QTreeWidgetItem*,int)));
70 }
71 
72 void DeviceManagerUI::makePortEditable(QTreeWidgetItem *selectedItem, int column)
73 {
74  // If it's the port column, then make it user-editable
75  if (column == INDIDriver::LOCAL_PORT_COLUMN)
77 
78  localTreeWidget->editItem(selectedItem, INDIDriver::LOCAL_PORT_COLUMN);
79 }
80 
81 INDIDriver::INDIDriver(KStars *_ks) : QDialog(_ks), ksw(_ks)
82 {
83  currentPort = Options::serverPortStart().toInt() - 1;
84  lastGroup = nullptr;
85  lastDevice = nullptr;
86 
87  ui = new DeviceManagerUI(this);
88  setMainWidget(ui);
89  setCaption(xi18n("Device Manager"));
90  setButtons(QDialog::Close);
91 
92  foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
93  {
94  QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget, lastGroup);
95  lastGroup = item;
96  item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
97  item->setText(HOST_NAME_COLUMN, host->name);
98  item->setText(HOST_PORT_COLUMN, host->portnumber);
99  }
100 
101  lastGroup = nullptr;
102 
103  QObject::connect(ui->addB, SIGNAL(clicked()), this, SLOT(addINDIHost()));
104  QObject::connect(ui->modifyB, SIGNAL(clicked()), this, SLOT(modifyINDIHost()));
105  QObject::connect(ui->removeB, SIGNAL(clicked()), this, SLOT(removeINDIHost()));
106 
107  //QObject::connect(ksw->indiMenu(), SIGNAL(driverDisconnected(int)), this, SLOT(shutdownHost(int)));
108  QObject::connect(ui->connectHostB, SIGNAL(clicked()), this, SLOT(activateHostConnection()));
109  QObject::connect(ui->disconnectHostB, SIGNAL(clicked()), this, SLOT(activateHostDisconnection()));
110  QObject::connect(ui->runServiceB, SIGNAL(clicked()), this, SLOT(activateRunService()));
111  QObject::connect(ui->stopServiceB, SIGNAL(clicked()), this, SLOT(activateStopService()));
112  QObject::connect(ui->localTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(updateLocalTab()));
113  QObject::connect(ui->clientTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(updateClientTab()));
114  QObject::connect(ui->localTreeWidget, SIGNAL(expanded(QModelIndex)), this, SLOT(resizeDeviceColumn()));
115 
116  readXMLDrivers();
117 }
118 
119 /*
120 void INDIDriver::enableDevice(INDI_D *indi_device)
121 {
122  if (indi_device == nullptr)
123  return;
124 
125  if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT)
126  {
127  foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
128  {
129 
130  if (host->deviceManager == indi_device->deviceManager && host->isConnected == false)
131  {
132  foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly, 1))
133  item->setIcon(HOST_STATUS_COLUMN, ui->connected);
134 
135  host->isConnected = true;
136  updateClientTab();
137  updateMenuActions();
138  ksw->indiMenu()->show();
139  return;
140  }
141  }
142  }
143  else
144  {
145  foreach (IDevice *device, devices)
146  {
147  if (device->unique_label == indi_device->label)
148  {
149  foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly | Qt::MatchRecursive))
150  {
151  item->setIcon(LOCAL_STATUS_COLUMN, ui->runningPix);
152  item->setText(LOCAL_PORT_COLUMN, QString::number(indi_device->deviceManager->port));
153  }
154 
155  updateLocalTab();
156  updateMenuActions();
157  ksw->indiMenu()->show();
158  return;
159  }
160  }
161  }
162 
163 }
164 
165 void INDIDriver::disableDevice(INDI_D *indi_device)
166 {
167  if (indi_device == nullptr)
168  return;
169 
170  if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT)
171  {
172  foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
173  {
174  if (host->deviceManager == indi_device->deviceManager && host->isConnected == true)
175  {
176  foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly,1))
177  item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
178 
179  host->deviceManager = nullptr;
180  host->isConnected = false;
181 
182  updateClientTab();
183  return;
184  }
185  }
186  }
187  else
188  {
189  foreach (IDevice *device, devices)
190  {
191  if (device->unique_label == indi_device->label)
192  {
193  foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly | Qt::MatchRecursive))
194  {
195  item->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
196  item->setIcon(LOCAL_MODE_COLUMN, QIcon());
197  item->setText(LOCAL_PORT_COLUMN, device->port);
198  }
199 
200  device->clear();
201  updateLocalTab();
202 
203  return;
204  }
205  }
206  }
207  }
208  */
209 void INDIDriver::activateRunService()
210 {
211  processLocalTree(IDevice::DEV_START);
212 }
213 
214 void INDIDriver::activateStopService()
215 {
216  processLocalTree(IDevice::DEV_TERMINATE);
217 }
218 
219 void INDIDriver::activateHostConnection()
220 {
221  processRemoteTree(IDevice::DEV_START);
222 }
223 
224 void INDIDriver::activateHostDisconnection()
225 {
226  processRemoteTree(IDevice::DEV_TERMINATE);
227 }
228 
229 void INDIDriver::updateLocalTab()
230 {
231  if (ui->localTreeWidget->currentItem() == nullptr)
232  return;
233 
234  foreach (IDevice *device, devices)
235  {
236  if (ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN) == device->tree_label)
237  {
238  ui->runServiceB->setEnabled(device->state == IDevice::DEV_TERMINATE);
239  ui->stopServiceB->setEnabled(device->state == IDevice::DEV_START);
240 
241  ui->serverLogText->clear();
242  ui->serverLogText->append(device->getServerBuffer());
243 
244  return;
245  }
246  }
247 }
248 
249 void INDIDriver::updateClientTab()
250 {
251  if (ui->clientTreeWidget->currentItem() == nullptr)
252  return;
253 
254  foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
255  {
256  if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == host->name &&
257  ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) == host->portnumber)
258  {
259  ui->connectHostB->setEnabled(!host->isConnected);
260  ui->disconnectHostB->setEnabled(host->isConnected);
261  break;
262  }
263  }
264 }
265 
266 void INDIDriver::processLocalTree(IDevice::DeviceStatus dev_request)
267 {
268  QList<IDevice *> processed_devices;
269  DeviceManager *deviceManager = nullptr;
270  int port = 0;
271  bool portOK = false;
272 
273  foreach (QTreeWidgetItem *item, ui->localTreeWidget->selectedItems())
274  {
275  foreach (IDevice *device, devices)
276  {
277  //device->state = (dev_request == IDevice::DEV_TERMINATE) ? IDevice::DEV_START : IDevice::DEV_TERMINATE;
278  if (item->text(LOCAL_NAME_COLUMN) == device->tree_label && device->state != dev_request)
279  {
280  processed_devices.append(device);
281 
282  // N.B. If multiple devices are selected to run under one device manager
283  // then we select the port for the first device that has a valid port
284  // entry, the rest are ignored.
285  if (port == 0 && item->text(LOCAL_PORT_COLUMN).isEmpty() == false)
286  {
287  port = item->text(LOCAL_PORT_COLUMN).toInt(&portOK);
288  // If we encounter conversion error, we abort
289  if (portOK == false)
290  {
291  KMessageBox::error(0, xi18n("Invalid port entry: %1", item->text(LOCAL_PORT_COLUMN)));
292  return;
293  }
294  }
295  }
296  }
297  }
298 
299  if (processed_devices.empty())
300  return;
301 
302  // Select random port within range is none specified.
303  port = getINDIPort(port);
304 
305  if (dev_request == IDevice::DEV_START)
306  {
307  if (port <= 0)
308  {
309  KMessageBox::error(0, xi18n("Cannot start INDI server: port error."));
310  return;
311  }
312 
313  //deviceManager = ksw->indiMenu()->startDeviceManager(processed_devices, ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER, ((uint) port));
314  deviceManager = ksw->indiMenu()->initDeviceManager(
315  "localhost", ((uint)port), ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER);
316 
317  if (deviceManager == nullptr)
318  {
319  kWarning() << "Warning: device manager has not been established properly";
320  return;
321  }
322 
323  deviceManager->appendManagedDevices(processed_devices);
324  deviceManager->startServer();
325  connect(deviceManager, SIGNAL(newServerInput()), this, SLOT(updateLocalTab()));
326  }
327  else
328  ksw->indiMenu()->stopDeviceManager(processed_devices);
329 }
330 
331 void INDIDriver::processRemoteTree(IDevice::DeviceStatus dev_request)
332 {
333  DeviceManager *deviceManager = nullptr;
334  QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
335  if (!currentItem)
336  return;
337  bool toConnect = (dev_request == IDevice::DEV_START);
338 
339  foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
340  {
341  if (currentItem->text(HOST_NAME_COLUMN) == host->name &&
342  currentItem->text(HOST_PORT_COLUMN) == host->portnumber)
343  {
344  // Nothing changed, return
345  if (host->isConnected == toConnect)
346  return;
347 
348  // connect to host
349  if (toConnect)
350  {
351  deviceManager = ksw->indiMenu()->initDeviceManager(host->hostname, host->portnumber.toUInt(),
352  DeviceManager::M_CLIENT);
353 
354  if (deviceManager == nullptr)
355  {
356  // msgbox after freeze
357  kWarning() << "Warning: device manager has not been established properly";
358  return;
359  }
360 
361  host->deviceManager = deviceManager;
362  deviceManager->connectToServer();
363  }
364  else
365  ksw->indiMenu()->removeDeviceManager(host->deviceManager);
366 
367  return;
368  }
369  }
370 }
371 
372 void INDIDriver::newTelescopeDiscovered()
373 {
374  emit newTelescope();
375 }
376 
377 void INDIDriver::newCCDDiscovered()
378 {
379  emit newCCD();
380 }
381 
382 void INDIDriver::resizeDeviceColumn()
383 {
384  ui->localTreeWidget->resizeColumnToContents(0);
385 }
386 
387 void INDIDriver::updateMenuActions()
388 {
389  // We iterate over devices, we enable INDI Control Panel if we have any active device
390  // We enable capture image sequence if we have any imaging device
391 
392  QAction *tmpAction = nullptr;
393  INDIMenu *devMenu = ksw->indiMenu();
394  bool activeDevice = false;
395  bool activeImaging = false;
396  INDI_P *imgProp = nullptr;
397 
398  if (devMenu == nullptr)
399  return;
400 
401  if (devMenu->managers.count() > 0)
402  activeDevice = true;
403 
404  foreach (DeviceManager *dev_managers, devMenu->managers)
405  {
406  foreach (INDI_D *device, dev_managers->indi_dev)
407  {
408  imgProp = device->findProp("CCD_EXPOSURE");
409  if (imgProp && device->isOn())
410  {
411  activeImaging = true;
412  break;
413  }
414  }
415  }
416 
417  tmpAction = ksw->actionCollection()->action("capture_sequence");
418 
419  if (tmpAction != nullptr)
420  tmpAction->setEnabled(activeImaging);
421 
422  tmpAction = ksw->actionCollection()->action("indi_cpl");
423  if (tmpAction != nullptr)
424  tmpAction->setEnabled(activeDevice);
425 }
426 
427 bool INDIDriver::isDeviceRunning(const QString &deviceLabel)
428 {
429  foreach (IDevice *dev, devices)
430  {
431  if (deviceLabel == dev->tree_label)
432  return dev->state == IDevice::DEV_START;
433  }
434  return false;
435 }
436 
437 int INDIDriver::getINDIPort(int customPort)
438 {
439  int lastPort = Options::serverPortEnd().toInt();
440  ;
441  bool success = false;
442  currentPort++;
443 
444  // recycle
445  if (currentPort > lastPort)
446  currentPort = Options::serverPortStart().toInt();
447 
448  QTcpServer temp_server;
449 
450  if (customPort != 0)
451  {
452  success = temp_server.listen(QHostAddress::LocalHost, customPort);
453  if (success)
454  {
455  temp_server.close();
456  return customPort;
457  }
458  else
459  return -1;
460  }
461 
462  for (; currentPort <= lastPort; currentPort++)
463  {
464  success = temp_server.listen(QHostAddress::LocalHost, currentPort);
465  if (success)
466  {
467  temp_server.close();
468  return currentPort;
469  }
470  }
471  return -1;
472 }
473 
474 bool INDIDriver::readXMLDrivers()
475 {
476  QDir indiDir;
477  QString driverName;
478 
479  QString driversDir = Options::indiDriversDir();
480 #ifdef Q_OS_OSX
481  if (Options::indiDriversAreInternal())
482  QCoreApplication::applicationDirPath() + "/../Resources/DriverSupport";
483 #endif
484 
485  if (indiDir.cd(driversDir) == false)
486  {
487  KMessageBox::error(0, xi18n("Unable to find INDI drivers directory: %1\nPlease make sure to set the correct "
488  "path in KStars configuration",
489  driversDir));
490  return false;
491  }
492 
493  indiDir.setNameFilters(QStringList("*.xml"));
495  QFileInfoList list = indiDir.entryInfoList();
496 
497  foreach (QFileInfo fileInfo, list)
498  {
499  // libindi 0.7.1: Skip skeleton files
500  if (fileInfo.fileName().endsWith(QLatin1String("_sk.xml")))
501  continue;
502 
503  if (fileInfo.fileName() == "drivers.xml")
504  {
505  // Let first attempt to load the local version of drivers.xml
506  driverName = QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("drivers.xml");
507 
508  // If found, we continue, otherwise, we load the system file
509  if (driverName.isEmpty() == false && QFile(driverName).exists())
510  {
511  processXMLDriver(driverName);
512  continue;
513  }
514  }
515 
516  driverName = QString("%1/%2").arg(driversDir).arg(fileInfo.fileName());
517  processXMLDriver(driverName);
518  }
519 
520  return true;
521 }
522 
523 void INDIDriver::processXMLDriver(QString &driverName)
524 {
525  QFile file(driverName);
526  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
527  {
528  KMessageBox::error(0, xi18n("Failed to open INDI Driver file: %1", driverName));
529  return;
530  }
531 
532  char errmsg[ERRMSG_SIZE];
533  char c;
534  LilXML *xmlParser = newLilXML();
535  XMLEle *root = nullptr;
536 
537  if (driverName.endsWith(QLatin1String("drivers.xml")))
538  xmlSource = IDevice::PRIMARY_XML;
539  else
540  xmlSource = IDevice::THIRD_PARTY_XML;
541 
542  while (file.getChar(&c))
543  {
544  root = readXMLEle(xmlParser, c, errmsg);
545 
546  if (root)
547  {
548  if (!buildDeviceGroup(root, errmsg))
549  prXMLEle(stderr, root, 0);
550 
551  delXMLEle(root);
552  }
553  else if (errmsg[0])
554  {
555  kDebug() << QString(errmsg) << endl;
556  delLilXML(xmlParser);
557  return;
558  }
559  }
560 
561  delLilXML(xmlParser);
562 }
563 
564 bool INDIDriver::buildDeviceGroup(XMLEle *root, char errmsg[])
565 {
566  XMLAtt *ap;
567  XMLEle *ep;
568  QString groupName;
569  QTreeWidgetItem *group;
570  int groupType = KSTARS_TELESCOPE;
571 
572  // avoid overflow
573  if (strlen(tagXMLEle(root)) > 1024)
574  return false;
575 
576  // Get device grouping name
577  ap = findXMLAtt(root, "group");
578 
579  if (!ap)
580  {
581  snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute", tagXMLEle(root));
582  return false;
583  }
584 
585  groupName = valuXMLAtt(ap);
586 
587  if (groupName.indexOf("Telescopes") != -1)
588  groupType = KSTARS_TELESCOPE;
589  else if (groupName.indexOf("CCDs") != -1)
590  groupType = KSTARS_CCD;
591  else if (groupName.indexOf("Filter") != -1)
592  groupType = KSTARS_FILTER;
593  else if (groupName.indexOf("Video") != -1)
594  groupType = KSTARS_VIDEO;
595  else if (groupName.indexOf("Focusers") != -1)
596  groupType = KSTARS_FOCUSER;
597  else if (groupName.indexOf("Domes") != -1)
598  groupType = KSTARS_DOME;
599  else if (groupName.indexOf("Receivers") != -1)
600  groupType = KSTARS_RECEIVERS;
601  else if (groupName.indexOf("GPS") != -1)
602  groupType = KSTARS_GPS;
603 
604 #ifndef HAVE_CFITSIO_H
605  // We do not create these groups if we don't have CFITSIO support
606  if (groupType == KSTARS_CCD || groupType == KSTARS_VIDEO)
607  return true;
608 #endif
609 
610  // Find if the group already exists
611  QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems(groupName, Qt::MatchExactly);
612  if (!treeList.isEmpty())
613  group = treeList[0];
614  else
615  group = new QTreeWidgetItem(ui->localTreeWidget, lastGroup);
616 
617  group->setText(0, groupName);
618  lastGroup = group;
619 
620  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
621  if (!buildDriverElement(ep, group, groupType, errmsg))
622  return false;
623 
624  return true;
625 }
626 
627 bool INDIDriver::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup, int groupType, char errmsg[])
628 {
629  XMLAtt *ap;
630  XMLEle *el;
631  IDevice *dv;
632  QString label;
633  QString driver;
635  QString name;
636  QString port;
637  double focal_length(-1), aperture(-1);
638 
639  ap = findXMLAtt(root, "label");
640  if (!ap)
641  {
642  snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute", tagXMLEle(root));
643  return false;
644  }
645 
646  label = valuXMLAtt(ap);
647 
648  // Search for optional port attribute
649  ap = findXMLAtt(root, "port");
650  if (ap)
651  port = valuXMLAtt(ap);
652 
653  // Let's look for telescope-specific attributes: focal length and aperture
654  ap = findXMLAtt(root, "focal_length");
655  if (ap)
656  focal_length = QString(valuXMLAtt(ap)).toDouble();
657 
658  ap = findXMLAtt(root, "aperture");
659  if (ap)
660  aperture = QString(valuXMLAtt(ap)).toDouble();
661 
662  el = findXMLEle(root, "driver");
663 
664  if (!el)
665  return false;
666 
667  driver = pcdataXMLEle(el);
668 
669  ap = findXMLAtt(el, "name");
670  if (!ap)
671  {
672  snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a name attribute", tagXMLEle(el));
673  return false;
674  }
675 
676  name = valuXMLAtt(ap);
677 
678  el = findXMLEle(root, "version");
679 
680  if (!el)
681  return false;
682 
683  version = pcdataXMLEle(el);
684 
685  QTreeWidgetItem *device = new QTreeWidgetItem(DGroup, lastDevice);
686 
687  device->setText(LOCAL_NAME_COLUMN, label);
688  device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
689  device->setText(LOCAL_VERSION_COLUMN, version);
690  device->setText(LOCAL_PORT_COLUMN, port);
691 
692  lastDevice = device;
693 
694  if ((xmlSource == IDevice::PRIMARY_XML) && driversList.contains(driver) == false)
695  driversList.insert(driver, name);
696 
697  dv = new IDevice(name, label, driver, version);
698  dv->type = groupType;
699  dv->xmlSource = xmlSource;
700  //connect(dv, SIGNAL(newServerInput()), this, SLOT(updateLocalTab()));
701  if (focal_length > 0)
702  dv->focal_length = focal_length;
703  if (aperture > 0)
704  dv->aperture = aperture;
705  dv->port = port;
706 
707  devices.append(dv);
708 
709  // SLOTS/SIGNAL, pop menu, indi server logic
710  return true;
711 }
712 
713 void INDIDriver::updateCustomDrivers()
714 {
715  QString label;
716  QString driver;
718  QString name;
719  IDevice *dv;
720  QTreeWidgetItem *group, *widgetDev;
721  double focal_length(-1), aperture(-1);
722 
723  // Find if the group already exists
724  QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems("Telescopes", Qt::MatchExactly);
725  if (!treeList.isEmpty())
726  group = treeList[0];
727  else
728  return;
729 
730  // Find custom telescope to ADD
731  foreach (OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList()))
732  {
733  label = s->vendor() + ' ' + s->model();
734 
735  if (s->driver() == xi18n("None") || findDeviceByLabel(label))
736  continue;
737 
738  QHash<QString, QString>::const_iterator i = driversList.constFind(s->driver());
739  if (i != driversList.constEnd() && i.key() == s->driver())
740  name = i.value();
741  else
742  return;
743  focal_length = s->focalLength();
744  aperture = s->aperture();
745  driver = s->driver();
746  version = QString("1.0");
747 
748  QTreeWidgetItem *device = new QTreeWidgetItem(group, lastDevice);
749  device->setText(LOCAL_NAME_COLUMN, QString(label));
750  device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
751  device->setText(LOCAL_VERSION_COLUMN, QString(version));
752 
753  lastDevice = device;
754 
755  dv = new IDevice(name, label, driver, version);
756  dv->type = KSTARS_TELESCOPE;
757  dv->xmlSource = IDevice::EM_XML;
758  dv->focal_length = focal_length;
759  dv->aperture = aperture;
760  dv->id = s->id();
761  devices.append(dv);
762  }
763 
764  // Find custom telescope to REMOVE
765  foreach (IDevice *dev, devices)
766  {
767  // If it's from primary xml file or it is in a running state, continue.
768  if (dev->xmlSource != IDevice::EM_XML || dev->state == IDevice::DEV_START)
769  continue;
770 
771  // See if log object has the device, if not delete it
772  if (KStarsData::Instance()->logObject()->findScopeById(dev->id))
773  continue;
774 
775  // Find if the group already exists
776  QList<QTreeWidgetItem *> devList =
777  ui->localTreeWidget->findItems(dev->tree_label, Qt::MatchExactly | Qt::MatchRecursive);
778  if (!devList.isEmpty())
779  {
780  widgetDev = devList[0];
781  group->removeChild(widgetDev);
782  }
783  else
784  return;
785 
786  devices.removeOne(dev);
787  delete (dev);
788  }
789 }
790 
791 void INDIDriver::addINDIHost()
792 {
793  QDialog hostConfDialog;
794  Ui::INDIHostConf hostConf;
795  hostConf.setupUi(&hostConfDialog);
796  hostConfDialog.setWindowTitle(xi18n("Add Host"));
797  bool portOk = false;
798 
799  if (hostConfDialog.exec() == QDialog::Accepted)
800  {
801  INDIHostsInfo *hostItem = new INDIHostsInfo;
802  hostItem->name = hostConf.nameIN->text();
803  hostItem->hostname = hostConf.hostname->text();
804  hostItem->portnumber = hostConf.portnumber->text();
805  hostItem->isConnected = false;
806  hostItem->deviceManager = nullptr;
807 
808  hostItem->portnumber.toInt(&portOk);
809 
810  if (portOk == false)
811  {
812  KMessageBox::error(0, xi18n("Error: the port number is invalid."));
813  delete hostItem;
814  return;
815  }
816 
817  //search for duplicates
818  //for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++)
819  foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
820  if (hostItem->name == host->name && hostItem->portnumber == host->portnumber)
821  {
822  KMessageBox::error(0, xi18n("Host: %1 Port: %2 already exists.", hostItem->name, hostItem->portnumber));
823  delete hostItem;
824  return;
825  }
826 
827  ksw->data()->INDIHostsList.append(hostItem);
828 
829  QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget);
830  item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
831  item->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
832  item->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
833  }
834 
835  saveHosts();
836 }
837 
838 void INDIDriver::modifyINDIHost()
839 {
840  QDialog hostConfDialog;
841  Ui::INDIHostConf hostConf;
842  hostConf.setupUi(&hostConfDialog);
843  hostConfDialog.setWindowTitle(xi18n("Modify Host"));
844 
845  QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
846 
847  if (currentItem == nullptr)
848  return;
849 
850  foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
851  {
852  if (currentItem->text(HOST_NAME_COLUMN) == host->name &&
853  currentItem->text(HOST_PORT_COLUMN) == host->portnumber)
854  {
855  hostConf.nameIN->setText(host->name);
856  hostConf.hostname->setText(host->hostname);
857  hostConf.portnumber->setText(host->portnumber);
858 
859  if (hostConfDialog.exec() == QDialog::Accepted)
860  {
861  //INDIHostsInfo *hostItem = new INDIHostsInfo;
862  host->name = hostConf.nameIN->text();
863  host->hostname = hostConf.hostname->text();
864  host->portnumber = hostConf.portnumber->text();
865 
866  currentItem->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
867  currentItem->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
868 
869  //ksw->data()->INDIHostsList.replace(i, hostItem);
870 
871  saveHosts();
872  return;
873  }
874  }
875  }
876 }
877 
878 void INDIDriver::removeINDIHost()
879 {
880  if (ui->clientTreeWidget->currentItem() == nullptr)
881  return;
882 
883  for (int i = 0; i < ksw->data()->INDIHostsList.count(); i++)
884  {
885  if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == ksw->data()->INDIHostsList[i]->name &&
886  ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) == ksw->data()->INDIHostsList[i]->portnumber)
887  {
888  if (ksw->data()->INDIHostsList[i]->isConnected)
889  {
890  KMessageBox::error(0, xi18n("You need to disconnect the client before removing it."));
891  return;
892  }
893 
895  xi18n("Are you sure you want to remove the %1 client?",
896  ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN)),
897  xi18n("Delete Confirmation"),
898  KStandardGuiItem::del()) != KMessageBox::Continue)
899  return;
900 
901  delete ksw->data()->INDIHostsList.takeAt(i);
902  delete (ui->clientTreeWidget->currentItem());
903  break;
904  }
905  }
906  saveHosts();
907 }
908 
909 void INDIDriver::saveHosts()
910 {
911  QFile file;
912  QString hostData;
913 
914  //determine filename in local user KDE directory tree.
915  file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("indihosts.xml"));
916 
917  if (!file.open(QIODevice::WriteOnly))
918  {
920  "Unable to write to file 'indihosts.xml'\nAny changes to INDI hosts configurations will not be saved.");
921  KMessageBox::sorry(0, message, xi18n("Could Not Open File"));
922  return;
923  }
924 
925  QTextStream outstream(&file);
926 
927  //for (uint i= 0; i < ksw->data()->INDIHostsList.count(); i++)
928  foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList)
929  {
930  hostData = "<INDIHost name='";
931  hostData += host->name;
932  hostData += "' hostname='";
933  hostData += host->hostname;
934  hostData += "' port='";
935  hostData += host->portnumber;
936  hostData += "' />\n";
937 
938  outstream << hostData;
939  }
940 
941  file.close();
942 }
943 
944 IDevice *INDIDriver::findDeviceByLabel(const QString &label)
945 {
946  foreach (IDevice *dev, devices)
947  {
948  if (dev->tree_label == label)
949  return dev;
950  }
951  return nullptr;
952 }
953 
954 INDIDriver::~INDIDriver()
955 {
956  //for (uint i=0; i < devices.size(); i++)
957  //delete (devices[i]);
958  while (!devices.isEmpty())
959  delete devices.takeFirst();
960 }
961 
962 IDevice::IDevice(const QString &inName, const QString &inLabel, const QString &inDriver, const QString &inVersion)
963 {
964  tree_label = inLabel;
965  unique_label.clear();
966  name = inName;
967  driver = inDriver;
968  version = inVersion;
969 
970  xmlSource = PRIMARY_XML;
971 
972  // Initially off
973  state = IDevice::DEV_TERMINATE;
974 
975  // No port initially
976  //indiPort = -1;
977 
978  // not yet managed by DeviceManager
979  //managed = false;
980 
981  deviceManager = nullptr;
982 
983  focal_length = -1;
984  aperture = -1;
985 
986  //proc = nullptr;
987 }
988 
989 IDevice::~IDevice()
990 {
991 }
992 
993 void IDevice::clear()
994 {
995  //managed = false;
996  state = IDevice::DEV_TERMINATE;
997  deviceManager = nullptr;
998 
999  unique_label.clear();
1000 }
1001 
1002 QString IDevice::getServerBuffer()
1003 {
1004  if (deviceManager != nullptr)
1005  return deviceManager->getServerBuffer();
1006 
1007  return QString();
1008 }
1009 
1010 #include "indidriver.moc"
void append(const T &value)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
void setFlags(Qt::ItemFlags flags)
const T value(const Key &key) const const
QTextStream & endl(QTextStream &stream)
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 clear()
QIcon fromTheme(const QString &name)
void setFilter(QDir::Filters filters)
QString applicationDirPath()
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
QString xi18n(const char *text, const TYPE &arg...)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool empty() const const
ItemIsSelectable
void sorry(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
bool isEmpty() const const
const Key key(const T &value) const const
void setWindowTitle(const QString &)
void setNameFilters(const QStringList &nameFilters)
int toInt(bool *ok, int base) const const
void setFileName(const QString &name)
void setText(int column, const QString &text)
bool isEmpty() const const
virtual int exec()
virtual void close() override
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
This is the main window for KStars. In addition to the GUI elements, the class contains the program c...
Definition: kstars.h:92
void setupUi(QWidget *widget)
QHash::const_iterator constFind(const Key &key) const const
bool listen(const QHostAddress &address, quint16 port)
QString fileName() const const
double toDouble(bool *ok) const const
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString label(StandardShortcut id)
ScriptableExtension * host() const
KGuiItem del()
void setEnabled(bool)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
unsigned int version()
QString name(StandardShortcut id)
QString filePath(const QString &fileName) const const
void removeChild(QTreeWidgetItem *child)
int count(const Key &key) const const
MatchExactly
void setIcon(int column, const QIcon &icon)
QString text(int column) const const
bool cd(const QString &dirName)
QString message
QString & append(QChar ch)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Aug 13 2022 04:01:54 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.