File indexing completed on 2024-03-24 03:46:52
0001 /* 0002 SPDX-FileCopyrightText: 2001 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "indidriver.h" 0008 0009 #include "config-kstars.h" 0010 0011 #include "devicemanager.h" 0012 #include "indidevice.h" 0013 #include "indimenu.h" 0014 #include "kstars.h" 0015 #include "kstarsdata.h" 0016 #include "ksutils.h" 0017 #include "Options.h" 0018 #include "ui_indihostconf.h" 0019 #include "oal/log.h" 0020 #include "oal/scope.h" 0021 0022 #include <KMessageBox> 0023 #include <KProcess> 0024 #include <KActionCollection> 0025 0026 #include <QRadioButton> 0027 #include <QFile> 0028 #include <QDir> 0029 #include <QTextStream> 0030 #include <QTreeWidget> 0031 #include <QIcon> 0032 #include <QDialog> 0033 #include <QTcpServer> 0034 #include <QMenu> 0035 #include <QPushButton> 0036 #include <QLineEdit> 0037 #include <QAction> 0038 #include <QStandardPaths> 0039 0040 #include <unistd.h> 0041 #include <sys/socket.h> 0042 #include <netinet/in.h> 0043 #include <netdb.h> 0044 0045 #define MAX_RETRIES 3 0046 #define ERRMSG_SIZE 1024 0047 0048 DeviceManagerUI::DeviceManagerUI(QWidget *parent) : QFrame(parent) 0049 { 0050 #ifdef Q_OS_OSX 0051 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0052 #endif 0053 setupUi(this); 0054 0055 localTreeWidget->setSortingEnabled(false); 0056 localTreeWidget->setRootIsDecorated(true); 0057 0058 clientTreeWidget->setSortingEnabled(false); 0059 0060 runningPix = QIcon::fromTheme("system-run"); 0061 stopPix = QIcon::fromTheme("dialog-cancel"); 0062 localMode = QIcon::fromTheme("computer"); 0063 serverMode = QIcon::fromTheme("network-server"); 0064 0065 connected = QIcon::fromTheme("network-connect"); 0066 disconnected = QIcon::fromTheme("network-disconnect"); 0067 0068 connect(localTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, 0069 SLOT(makePortEditable(QTreeWidgetItem*,int))); 0070 } 0071 0072 void DeviceManagerUI::makePortEditable(QTreeWidgetItem *selectedItem, int column) 0073 { 0074 // If it's the port column, then make it user-editable 0075 if (column == INDIDriver::LOCAL_PORT_COLUMN) 0076 selectedItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled); 0077 0078 localTreeWidget->editItem(selectedItem, INDIDriver::LOCAL_PORT_COLUMN); 0079 } 0080 0081 INDIDriver::INDIDriver(KStars *_ks) : QDialog(_ks), ksw(_ks) 0082 { 0083 currentPort = Options::serverPortStart().toInt() - 1; 0084 lastGroup = nullptr; 0085 lastDevice = nullptr; 0086 0087 ui = new DeviceManagerUI(this); 0088 setMainWidget(ui); 0089 setCaption(xi18n("Device Manager")); 0090 setButtons(QDialog::Close); 0091 0092 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList) 0093 { 0094 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget, lastGroup); 0095 lastGroup = item; 0096 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected); 0097 item->setText(HOST_NAME_COLUMN, host->name); 0098 item->setText(HOST_PORT_COLUMN, host->portnumber); 0099 } 0100 0101 lastGroup = nullptr; 0102 0103 QObject::connect(ui->addB, SIGNAL(clicked()), this, SLOT(addINDIHost())); 0104 QObject::connect(ui->modifyB, SIGNAL(clicked()), this, SLOT(modifyINDIHost())); 0105 QObject::connect(ui->removeB, SIGNAL(clicked()), this, SLOT(removeINDIHost())); 0106 0107 //QObject::connect(ksw->indiMenu(), SIGNAL(driverDisconnected(int)), this, SLOT(shutdownHost(int))); 0108 QObject::connect(ui->connectHostB, SIGNAL(clicked()), this, SLOT(activateHostConnection())); 0109 QObject::connect(ui->disconnectHostB, SIGNAL(clicked()), this, SLOT(activateHostDisconnection())); 0110 QObject::connect(ui->runServiceB, SIGNAL(clicked()), this, SLOT(activateRunService())); 0111 QObject::connect(ui->stopServiceB, SIGNAL(clicked()), this, SLOT(activateStopService())); 0112 QObject::connect(ui->localTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(updateLocalTab())); 0113 QObject::connect(ui->clientTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(updateClientTab())); 0114 QObject::connect(ui->localTreeWidget, SIGNAL(expanded(QModelIndex)), this, SLOT(resizeDeviceColumn())); 0115 0116 readXMLDrivers(); 0117 } 0118 0119 /* 0120 void INDIDriver::enableDevice(INDI_D *indi_device) 0121 { 0122 if (indi_device == nullptr) 0123 return; 0124 0125 if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT) 0126 { 0127 foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList) 0128 { 0129 0130 if (host->deviceManager == indi_device->deviceManager && host->isConnected == false) 0131 { 0132 foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly, 1)) 0133 item->setIcon(HOST_STATUS_COLUMN, ui->connected); 0134 0135 host->isConnected = true; 0136 updateClientTab(); 0137 updateMenuActions(); 0138 ksw->indiMenu()->show(); 0139 return; 0140 } 0141 } 0142 } 0143 else 0144 { 0145 foreach (IDevice *device, devices) 0146 { 0147 if (device->unique_label == indi_device->label) 0148 { 0149 foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly | Qt::MatchRecursive)) 0150 { 0151 item->setIcon(LOCAL_STATUS_COLUMN, ui->runningPix); 0152 item->setText(LOCAL_PORT_COLUMN, QString::number(indi_device->deviceManager->port)); 0153 } 0154 0155 updateLocalTab(); 0156 updateMenuActions(); 0157 ksw->indiMenu()->show(); 0158 return; 0159 } 0160 } 0161 } 0162 0163 } 0164 0165 void INDIDriver::disableDevice(INDI_D *indi_device) 0166 { 0167 if (indi_device == nullptr) 0168 return; 0169 0170 if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT) 0171 { 0172 foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList) 0173 { 0174 if (host->deviceManager == indi_device->deviceManager && host->isConnected == true) 0175 { 0176 foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly,1)) 0177 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected); 0178 0179 host->deviceManager = nullptr; 0180 host->isConnected = false; 0181 0182 updateClientTab(); 0183 return; 0184 } 0185 } 0186 } 0187 else 0188 { 0189 foreach (IDevice *device, devices) 0190 { 0191 if (device->unique_label == indi_device->label) 0192 { 0193 foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly | Qt::MatchRecursive)) 0194 { 0195 item->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix); 0196 item->setIcon(LOCAL_MODE_COLUMN, QIcon()); 0197 item->setText(LOCAL_PORT_COLUMN, device->port); 0198 } 0199 0200 device->clear(); 0201 updateLocalTab(); 0202 0203 return; 0204 } 0205 } 0206 } 0207 } 0208 */ 0209 void INDIDriver::activateRunService() 0210 { 0211 processLocalTree(IDevice::DEV_START); 0212 } 0213 0214 void INDIDriver::activateStopService() 0215 { 0216 processLocalTree(IDevice::DEV_TERMINATE); 0217 } 0218 0219 void INDIDriver::activateHostConnection() 0220 { 0221 processRemoteTree(IDevice::DEV_START); 0222 } 0223 0224 void INDIDriver::activateHostDisconnection() 0225 { 0226 processRemoteTree(IDevice::DEV_TERMINATE); 0227 } 0228 0229 void INDIDriver::updateLocalTab() 0230 { 0231 if (ui->localTreeWidget->currentItem() == nullptr) 0232 return; 0233 0234 foreach (IDevice *device, devices) 0235 { 0236 if (ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN) == device->tree_label) 0237 { 0238 ui->runServiceB->setEnabled(device->state == IDevice::DEV_TERMINATE); 0239 ui->stopServiceB->setEnabled(device->state == IDevice::DEV_START); 0240 0241 ui->serverLogText->clear(); 0242 ui->serverLogText->append(device->getServerBuffer()); 0243 0244 return; 0245 } 0246 } 0247 } 0248 0249 void INDIDriver::updateClientTab() 0250 { 0251 if (ui->clientTreeWidget->currentItem() == nullptr) 0252 return; 0253 0254 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList) 0255 { 0256 if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == host->name && 0257 ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) == host->portnumber) 0258 { 0259 ui->connectHostB->setEnabled(!host->isConnected); 0260 ui->disconnectHostB->setEnabled(host->isConnected); 0261 break; 0262 } 0263 } 0264 } 0265 0266 void INDIDriver::processLocalTree(IDevice::DeviceStatus dev_request) 0267 { 0268 QList<IDevice *> processed_devices; 0269 DeviceManager *deviceManager = nullptr; 0270 int port = 0; 0271 bool portOK = false; 0272 0273 foreach (QTreeWidgetItem *item, ui->localTreeWidget->selectedItems()) 0274 { 0275 foreach (IDevice *device, devices) 0276 { 0277 //device->state = (dev_request == IDevice::DEV_TERMINATE) ? IDevice::DEV_START : IDevice::DEV_TERMINATE; 0278 if (item->text(LOCAL_NAME_COLUMN) == device->tree_label && device->state != dev_request) 0279 { 0280 processed_devices.append(device); 0281 0282 // N.B. If multiple devices are selected to run under one device manager 0283 // then we select the port for the first device that has a valid port 0284 // entry, the rest are ignored. 0285 if (port == 0 && item->text(LOCAL_PORT_COLUMN).isEmpty() == false) 0286 { 0287 port = item->text(LOCAL_PORT_COLUMN).toInt(&portOK); 0288 // If we encounter conversion error, we abort 0289 if (portOK == false) 0290 { 0291 KMessageBox::error(0, xi18n("Invalid port entry: %1", item->text(LOCAL_PORT_COLUMN))); 0292 return; 0293 } 0294 } 0295 } 0296 } 0297 } 0298 0299 if (processed_devices.empty()) 0300 return; 0301 0302 // Select random port within range is none specified. 0303 port = getINDIPort(port); 0304 0305 if (dev_request == IDevice::DEV_START) 0306 { 0307 if (port <= 0) 0308 { 0309 KMessageBox::error(0, xi18n("Cannot start INDI server: port error.")); 0310 return; 0311 } 0312 0313 //deviceManager = ksw->indiMenu()->startDeviceManager(processed_devices, ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER, ((uint) port)); 0314 deviceManager = ksw->indiMenu()->initDeviceManager( 0315 "localhost", ((uint)port), ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER); 0316 0317 if (deviceManager == nullptr) 0318 { 0319 kWarning() << "Warning: device manager has not been established properly"; 0320 return; 0321 } 0322 0323 deviceManager->appendManagedDevices(processed_devices); 0324 deviceManager->startServer(); 0325 connect(deviceManager, SIGNAL(newServerInput()), this, SLOT(updateLocalTab())); 0326 } 0327 else 0328 ksw->indiMenu()->stopDeviceManager(processed_devices); 0329 } 0330 0331 void INDIDriver::processRemoteTree(IDevice::DeviceStatus dev_request) 0332 { 0333 DeviceManager *deviceManager = nullptr; 0334 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem(); 0335 if (!currentItem) 0336 return; 0337 bool toConnect = (dev_request == IDevice::DEV_START); 0338 0339 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList) 0340 { 0341 if (currentItem->text(HOST_NAME_COLUMN) == host->name && 0342 currentItem->text(HOST_PORT_COLUMN) == host->portnumber) 0343 { 0344 // Nothing changed, return 0345 if (host->isConnected == toConnect) 0346 return; 0347 0348 // connect to host 0349 if (toConnect) 0350 { 0351 deviceManager = ksw->indiMenu()->initDeviceManager(host->hostname, host->portnumber.toUInt(), 0352 DeviceManager::M_CLIENT); 0353 0354 if (deviceManager == nullptr) 0355 { 0356 // msgbox after freeze 0357 kWarning() << "Warning: device manager has not been established properly"; 0358 return; 0359 } 0360 0361 host->deviceManager = deviceManager; 0362 deviceManager->connectToServer(); 0363 } 0364 else 0365 ksw->indiMenu()->removeDeviceManager(host->deviceManager); 0366 0367 return; 0368 } 0369 } 0370 } 0371 0372 void INDIDriver::newTelescopeDiscovered() 0373 { 0374 emit newTelescope(); 0375 } 0376 0377 void INDIDriver::newCCDDiscovered() 0378 { 0379 emit newCCD(); 0380 } 0381 0382 void INDIDriver::resizeDeviceColumn() 0383 { 0384 ui->localTreeWidget->resizeColumnToContents(0); 0385 } 0386 0387 void INDIDriver::updateMenuActions() 0388 { 0389 // We iterate over devices, we enable INDI Control Panel if we have any active device 0390 // We enable capture image sequence if we have any imaging device 0391 0392 QAction *tmpAction = nullptr; 0393 INDIMenu *devMenu = ksw->indiMenu(); 0394 bool activeDevice = false; 0395 bool activeImaging = false; 0396 INDI_P *imgProp = nullptr; 0397 0398 if (devMenu == nullptr) 0399 return; 0400 0401 if (devMenu->managers.count() > 0) 0402 activeDevice = true; 0403 0404 foreach (DeviceManager *dev_managers, devMenu->managers) 0405 { 0406 foreach (INDI_D *device, dev_managers->indi_dev) 0407 { 0408 imgProp = device->findProp("CCD_EXPOSURE"); 0409 if (imgProp && device->isOn()) 0410 { 0411 activeImaging = true; 0412 break; 0413 } 0414 } 0415 } 0416 0417 tmpAction = ksw->actionCollection()->action("capture_sequence"); 0418 0419 if (tmpAction != nullptr) 0420 tmpAction->setEnabled(activeImaging); 0421 0422 tmpAction = ksw->actionCollection()->action("indi_cpl"); 0423 if (tmpAction != nullptr) 0424 tmpAction->setEnabled(activeDevice); 0425 } 0426 0427 bool INDIDriver::isDeviceRunning(const QString &deviceLabel) 0428 { 0429 foreach (IDevice *dev, devices) 0430 { 0431 if (deviceLabel == dev->tree_label) 0432 return dev->state == IDevice::DEV_START; 0433 } 0434 return false; 0435 } 0436 0437 int INDIDriver::getINDIPort(int customPort) 0438 { 0439 int lastPort = Options::serverPortEnd().toInt(); 0440 ; 0441 bool success = false; 0442 currentPort++; 0443 0444 // recycle 0445 if (currentPort > lastPort) 0446 currentPort = Options::serverPortStart().toInt(); 0447 0448 QTcpServer temp_server; 0449 0450 if (customPort != 0) 0451 { 0452 success = temp_server.listen(QHostAddress::LocalHost, customPort); 0453 if (success) 0454 { 0455 temp_server.close(); 0456 return customPort; 0457 } 0458 else 0459 return -1; 0460 } 0461 0462 for (; currentPort <= lastPort; currentPort++) 0463 { 0464 success = temp_server.listen(QHostAddress::LocalHost, currentPort); 0465 if (success) 0466 { 0467 temp_server.close(); 0468 return currentPort; 0469 } 0470 } 0471 return -1; 0472 } 0473 0474 bool INDIDriver::readXMLDrivers() 0475 { 0476 QDir indiDir; 0477 QString driverName; 0478 0479 QString driversDir = Options::indiDriversDir(); 0480 #ifdef Q_OS_OSX 0481 if (Options::indiDriversAreInternal()) 0482 QCoreApplication::applicationDirPath() + "/../Resources/DriverSupport"; 0483 #endif 0484 0485 if (indiDir.cd(driversDir) == false) 0486 { 0487 KMessageBox::error(0, xi18n("Unable to find INDI drivers directory: %1\nPlease make sure to set the correct " 0488 "path in KStars configuration", 0489 driversDir)); 0490 return false; 0491 } 0492 0493 indiDir.setNameFilters(QStringList("*.xml")); 0494 indiDir.setFilter(QDir::Files | QDir::NoSymLinks); 0495 QFileInfoList list = indiDir.entryInfoList(); 0496 0497 foreach (QFileInfo fileInfo, list) 0498 { 0499 // libindi 0.7.1: Skip skeleton files 0500 if (fileInfo.fileName().endsWith(QLatin1String("_sk.xml"))) 0501 continue; 0502 0503 if (fileInfo.fileName() == "drivers.xml") 0504 { 0505 // Let first attempt to load the local version of drivers.xml 0506 driverName = QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("drivers.xml"); 0507 0508 // If found, we continue, otherwise, we load the system file 0509 if (driverName.isEmpty() == false && QFile(driverName).exists()) 0510 { 0511 processXMLDriver(driverName); 0512 continue; 0513 } 0514 } 0515 0516 driverName = QString("%1/%2").arg(driversDir).arg(fileInfo.fileName()); 0517 processXMLDriver(driverName); 0518 } 0519 0520 return true; 0521 } 0522 0523 void INDIDriver::processXMLDriver(QString &driverName) 0524 { 0525 QFile file(driverName); 0526 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) 0527 { 0528 KMessageBox::error(0, xi18n("Failed to open INDI Driver file: %1", driverName)); 0529 return; 0530 } 0531 0532 char errmsg[ERRMSG_SIZE]; 0533 char c; 0534 LilXML *xmlParser = newLilXML(); 0535 XMLEle *root = nullptr; 0536 0537 if (driverName.endsWith(QLatin1String("drivers.xml"))) 0538 xmlSource = IDevice::PRIMARY_XML; 0539 else 0540 xmlSource = IDevice::THIRD_PARTY_XML; 0541 0542 while (file.getChar(&c)) 0543 { 0544 root = readXMLEle(xmlParser, c, errmsg); 0545 0546 if (root) 0547 { 0548 if (!buildDeviceGroup(root, errmsg)) 0549 prXMLEle(stderr, root, 0); 0550 0551 delXMLEle(root); 0552 } 0553 else if (errmsg[0]) 0554 { 0555 kDebug() << QString(errmsg) << endl; 0556 delLilXML(xmlParser); 0557 return; 0558 } 0559 } 0560 0561 delLilXML(xmlParser); 0562 } 0563 0564 bool INDIDriver::buildDeviceGroup(XMLEle *root, char errmsg[]) 0565 { 0566 XMLAtt *ap; 0567 XMLEle *ep; 0568 QString groupName; 0569 QTreeWidgetItem *group; 0570 int groupType = KSTARS_TELESCOPE; 0571 0572 // avoid overflow 0573 if (strlen(tagXMLEle(root)) > 1024) 0574 return false; 0575 0576 // Get device grouping name 0577 ap = findXMLAtt(root, "group"); 0578 0579 if (!ap) 0580 { 0581 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute", tagXMLEle(root)); 0582 return false; 0583 } 0584 0585 groupName = valuXMLAtt(ap); 0586 0587 if (groupName.indexOf("Telescopes") != -1) 0588 groupType = KSTARS_TELESCOPE; 0589 else if (groupName.indexOf("CCDs") != -1) 0590 groupType = KSTARS_CCD; 0591 else if (groupName.indexOf("Filter") != -1) 0592 groupType = KSTARS_FILTER; 0593 else if (groupName.indexOf("Video") != -1) 0594 groupType = KSTARS_VIDEO; 0595 else if (groupName.indexOf("Focusers") != -1) 0596 groupType = KSTARS_FOCUSER; 0597 else if (groupName.indexOf("Domes") != -1) 0598 groupType = KSTARS_DOME; 0599 else if (groupName.indexOf("Receivers") != -1) 0600 groupType = KSTARS_RECEIVERS; 0601 else if (groupName.indexOf("GPS") != -1) 0602 groupType = KSTARS_GPS; 0603 0604 #ifndef HAVE_CFITSIO_H 0605 // We do not create these groups if we don't have CFITSIO support 0606 if (groupType == KSTARS_CCD || groupType == KSTARS_VIDEO) 0607 return true; 0608 #endif 0609 0610 // Find if the group already exists 0611 QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems(groupName, Qt::MatchExactly); 0612 if (!treeList.isEmpty()) 0613 group = treeList[0]; 0614 else 0615 group = new QTreeWidgetItem(ui->localTreeWidget, lastGroup); 0616 0617 group->setText(0, groupName); 0618 lastGroup = group; 0619 0620 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) 0621 if (!buildDriverElement(ep, group, groupType, errmsg)) 0622 return false; 0623 0624 return true; 0625 } 0626 0627 bool INDIDriver::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup, int groupType, char errmsg[]) 0628 { 0629 XMLAtt *ap; 0630 XMLEle *el; 0631 IDevice *dv; 0632 QString label; 0633 QString driver; 0634 QString version; 0635 QString name; 0636 QString port; 0637 double focal_length(-1), aperture(-1); 0638 0639 ap = findXMLAtt(root, "label"); 0640 if (!ap) 0641 { 0642 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute", tagXMLEle(root)); 0643 return false; 0644 } 0645 0646 label = valuXMLAtt(ap); 0647 0648 // Search for optional port attribute 0649 ap = findXMLAtt(root, "port"); 0650 if (ap) 0651 port = valuXMLAtt(ap); 0652 0653 // Let's look for telescope-specific attributes: focal length and aperture 0654 ap = findXMLAtt(root, "focal_length"); 0655 if (ap) 0656 focal_length = QString(valuXMLAtt(ap)).toDouble(); 0657 0658 ap = findXMLAtt(root, "aperture"); 0659 if (ap) 0660 aperture = QString(valuXMLAtt(ap)).toDouble(); 0661 0662 el = findXMLEle(root, "driver"); 0663 0664 if (!el) 0665 return false; 0666 0667 driver = pcdataXMLEle(el); 0668 0669 ap = findXMLAtt(el, "name"); 0670 if (!ap) 0671 { 0672 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a name attribute", tagXMLEle(el)); 0673 return false; 0674 } 0675 0676 name = valuXMLAtt(ap); 0677 0678 el = findXMLEle(root, "version"); 0679 0680 if (!el) 0681 return false; 0682 0683 version = pcdataXMLEle(el); 0684 0685 QTreeWidgetItem *device = new QTreeWidgetItem(DGroup, lastDevice); 0686 0687 device->setText(LOCAL_NAME_COLUMN, label); 0688 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix); 0689 device->setText(LOCAL_VERSION_COLUMN, version); 0690 device->setText(LOCAL_PORT_COLUMN, port); 0691 0692 lastDevice = device; 0693 0694 if ((xmlSource == IDevice::PRIMARY_XML) && driversList.contains(driver) == false) 0695 driversList.insert(driver, name); 0696 0697 dv = new IDevice(name, label, driver, version); 0698 dv->type = groupType; 0699 dv->xmlSource = xmlSource; 0700 //connect(dv, SIGNAL(newServerInput()), this, SLOT(updateLocalTab())); 0701 if (focal_length > 0) 0702 dv->focal_length = focal_length; 0703 if (aperture > 0) 0704 dv->aperture = aperture; 0705 dv->port = port; 0706 0707 devices.append(dv); 0708 0709 // SLOTS/SIGNAL, pop menu, indi server logic 0710 return true; 0711 } 0712 0713 void INDIDriver::updateCustomDrivers() 0714 { 0715 QString label; 0716 QString driver; 0717 QString version; 0718 QString name; 0719 IDevice *dv; 0720 QTreeWidgetItem *group, *widgetDev; 0721 double focal_length(-1), aperture(-1); 0722 0723 // Find if the group already exists 0724 QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems("Telescopes", Qt::MatchExactly); 0725 if (!treeList.isEmpty()) 0726 group = treeList[0]; 0727 else 0728 return; 0729 0730 // Find custom telescope to ADD 0731 foreach (OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList())) 0732 { 0733 label = s->vendor() + ' ' + s->model(); 0734 0735 if (s->driver() == xi18n("None") || findDeviceByLabel(label)) 0736 continue; 0737 0738 QHash<QString, QString>::const_iterator i = driversList.constFind(s->driver()); 0739 if (i != driversList.constEnd() && i.key() == s->driver()) 0740 name = i.value(); 0741 else 0742 return; 0743 focal_length = s->focalLength(); 0744 aperture = s->aperture(); 0745 driver = s->driver(); 0746 version = QString("1.0"); 0747 0748 QTreeWidgetItem *device = new QTreeWidgetItem(group, lastDevice); 0749 device->setText(LOCAL_NAME_COLUMN, QString(label)); 0750 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix); 0751 device->setText(LOCAL_VERSION_COLUMN, QString(version)); 0752 0753 lastDevice = device; 0754 0755 dv = new IDevice(name, label, driver, version); 0756 dv->type = KSTARS_TELESCOPE; 0757 dv->xmlSource = IDevice::EM_XML; 0758 dv->focal_length = focal_length; 0759 dv->aperture = aperture; 0760 dv->id = s->id(); 0761 devices.append(dv); 0762 } 0763 0764 // Find custom telescope to REMOVE 0765 foreach (IDevice *dev, devices) 0766 { 0767 // If it's from primary xml file or it is in a running state, continue. 0768 if (dev->xmlSource != IDevice::EM_XML || dev->state == IDevice::DEV_START) 0769 continue; 0770 0771 // See if log object has the device, if not delete it 0772 if (KStarsData::Instance()->logObject()->findScopeById(dev->id)) 0773 continue; 0774 0775 // Find if the group already exists 0776 QList<QTreeWidgetItem *> devList = 0777 ui->localTreeWidget->findItems(dev->tree_label, Qt::MatchExactly | Qt::MatchRecursive); 0778 if (!devList.isEmpty()) 0779 { 0780 widgetDev = devList[0]; 0781 group->removeChild(widgetDev); 0782 } 0783 else 0784 return; 0785 0786 devices.removeOne(dev); 0787 delete (dev); 0788 } 0789 } 0790 0791 void INDIDriver::addINDIHost() 0792 { 0793 QDialog hostConfDialog; 0794 Ui::INDIHostConf hostConf; 0795 hostConf.setupUi(&hostConfDialog); 0796 hostConfDialog.setWindowTitle(xi18n("Add Host")); 0797 bool portOk = false; 0798 0799 if (hostConfDialog.exec() == QDialog::Accepted) 0800 { 0801 INDIHostsInfo *hostItem = new INDIHostsInfo; 0802 hostItem->name = hostConf.nameIN->text(); 0803 hostItem->hostname = hostConf.hostname->text(); 0804 hostItem->portnumber = hostConf.portnumber->text(); 0805 hostItem->isConnected = false; 0806 hostItem->deviceManager = nullptr; 0807 0808 hostItem->portnumber.toInt(&portOk); 0809 0810 if (portOk == false) 0811 { 0812 KMessageBox::error(0, xi18n("Error: the port number is invalid.")); 0813 delete hostItem; 0814 return; 0815 } 0816 0817 //search for duplicates 0818 //for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) 0819 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList) 0820 if (hostItem->name == host->name && hostItem->portnumber == host->portnumber) 0821 { 0822 KMessageBox::error(0, xi18n("Host: %1 Port: %2 already exists.", hostItem->name, hostItem->portnumber)); 0823 delete hostItem; 0824 return; 0825 } 0826 0827 ksw->data()->INDIHostsList.append(hostItem); 0828 0829 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget); 0830 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected); 0831 item->setText(HOST_NAME_COLUMN, hostConf.nameIN->text()); 0832 item->setText(HOST_PORT_COLUMN, hostConf.portnumber->text()); 0833 } 0834 0835 saveHosts(); 0836 } 0837 0838 void INDIDriver::modifyINDIHost() 0839 { 0840 QDialog hostConfDialog; 0841 Ui::INDIHostConf hostConf; 0842 hostConf.setupUi(&hostConfDialog); 0843 hostConfDialog.setWindowTitle(xi18n("Modify Host")); 0844 0845 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem(); 0846 0847 if (currentItem == nullptr) 0848 return; 0849 0850 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList) 0851 { 0852 if (currentItem->text(HOST_NAME_COLUMN) == host->name && 0853 currentItem->text(HOST_PORT_COLUMN) == host->portnumber) 0854 { 0855 hostConf.nameIN->setText(host->name); 0856 hostConf.hostname->setText(host->hostname); 0857 hostConf.portnumber->setText(host->portnumber); 0858 0859 if (hostConfDialog.exec() == QDialog::Accepted) 0860 { 0861 //INDIHostsInfo *hostItem = new INDIHostsInfo; 0862 host->name = hostConf.nameIN->text(); 0863 host->hostname = hostConf.hostname->text(); 0864 host->portnumber = hostConf.portnumber->text(); 0865 0866 currentItem->setText(HOST_NAME_COLUMN, hostConf.nameIN->text()); 0867 currentItem->setText(HOST_PORT_COLUMN, hostConf.portnumber->text()); 0868 0869 //ksw->data()->INDIHostsList.replace(i, hostItem); 0870 0871 saveHosts(); 0872 return; 0873 } 0874 } 0875 } 0876 } 0877 0878 void INDIDriver::removeINDIHost() 0879 { 0880 if (ui->clientTreeWidget->currentItem() == nullptr) 0881 return; 0882 0883 for (int i = 0; i < ksw->data()->INDIHostsList.count(); i++) 0884 { 0885 if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == ksw->data()->INDIHostsList[i]->name && 0886 ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) == ksw->data()->INDIHostsList[i]->portnumber) 0887 { 0888 if (ksw->data()->INDIHostsList[i]->isConnected) 0889 { 0890 KMessageBox::error(0, xi18n("You need to disconnect the client before removing it.")); 0891 return; 0892 } 0893 0894 if (KMessageBox::warningContinueCancel(0, 0895 xi18n("Are you sure you want to remove the %1 client?", 0896 ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN)), 0897 xi18n("Delete Confirmation"), 0898 KStandardGuiItem::del()) != KMessageBox::Continue) 0899 return; 0900 0901 delete ksw->data()->INDIHostsList.takeAt(i); 0902 delete (ui->clientTreeWidget->currentItem()); 0903 break; 0904 } 0905 } 0906 saveHosts(); 0907 } 0908 0909 void INDIDriver::saveHosts() 0910 { 0911 QFile file; 0912 QString hostData; 0913 0914 //determine filename in local user KDE directory tree. 0915 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("indihosts.xml")); 0916 0917 if (!file.open(QIODevice::WriteOnly)) 0918 { 0919 QString message = xi18n( 0920 "Unable to write to file 'indihosts.xml'\nAny changes to INDI hosts configurations will not be saved."); 0921 KMessageBox::sorry(0, message, xi18n("Could Not Open File")); 0922 return; 0923 } 0924 0925 QTextStream outstream(&file); 0926 0927 //for (uint i= 0; i < ksw->data()->INDIHostsList.count(); i++) 0928 foreach (INDIHostsInfo *host, ksw->data()->INDIHostsList) 0929 { 0930 hostData = "<INDIHost name='"; 0931 hostData += host->name; 0932 hostData += "' hostname='"; 0933 hostData += host->hostname; 0934 hostData += "' port='"; 0935 hostData += host->portnumber; 0936 hostData += "' />\n"; 0937 0938 outstream << hostData; 0939 } 0940 0941 file.close(); 0942 } 0943 0944 IDevice *INDIDriver::findDeviceByLabel(const QString &label) 0945 { 0946 foreach (IDevice *dev, devices) 0947 { 0948 if (dev->tree_label == label) 0949 return dev; 0950 } 0951 return nullptr; 0952 } 0953 0954 INDIDriver::~INDIDriver() 0955 { 0956 //for (uint i=0; i < devices.size(); i++) 0957 //delete (devices[i]); 0958 while (!devices.isEmpty()) 0959 delete devices.takeFirst(); 0960 } 0961 0962 IDevice::IDevice(const QString &inName, const QString &inLabel, const QString &inDriver, const QString &inVersion) 0963 { 0964 tree_label = inLabel; 0965 unique_label.clear(); 0966 name = inName; 0967 driver = inDriver; 0968 version = inVersion; 0969 0970 xmlSource = PRIMARY_XML; 0971 0972 // Initially off 0973 state = IDevice::DEV_TERMINATE; 0974 0975 // No port initially 0976 //indiPort = -1; 0977 0978 // not yet managed by DeviceManager 0979 //managed = false; 0980 0981 deviceManager = nullptr; 0982 0983 focal_length = -1; 0984 aperture = -1; 0985 0986 //proc = nullptr; 0987 } 0988 0989 IDevice::~IDevice() 0990 { 0991 } 0992 0993 void IDevice::clear() 0994 { 0995 //managed = false; 0996 state = IDevice::DEV_TERMINATE; 0997 deviceManager = nullptr; 0998 0999 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"