File indexing completed on 2024-04-28 07:31:45
0001 /* 0002 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "drivermanager.h" 0008 0009 #include "config-kstars.h" 0010 0011 #include "clientmanager.h" 0012 #include "driverinfo.h" 0013 #include "guimanager.h" 0014 #include "indilistener.h" 0015 #include "kspaths.h" 0016 #include "kstars.h" 0017 #include "indidbus.h" 0018 #include "kstarsdata.h" 0019 #include "Options.h" 0020 #include "servermanager.h" 0021 #include "ui_indihostconf.h" 0022 #include "auxiliary/ksnotification.h" 0023 0024 #include <basedevice.h> 0025 0026 #ifndef KSTARS_LITE 0027 #include <KMessageBox> 0028 #include <KActionCollection> 0029 #include <KNotifications/KNotification> 0030 #endif 0031 0032 #include <QTcpServer> 0033 #include <QtConcurrent> 0034 #include <indi_debug.h> 0035 0036 #define INDI_MAX_TRIES 2 0037 #define ERRMSG_SIZE 1024 0038 0039 DriverManagerUI::DriverManagerUI(QWidget *parent) : QFrame(parent) 0040 { 0041 setupUi(this); 0042 0043 localTreeWidget->setSortingEnabled(false); 0044 localTreeWidget->setRootIsDecorated(true); 0045 0046 clientTreeWidget->setSortingEnabled(false); 0047 0048 runningPix = QIcon::fromTheme("system-run"); 0049 stopPix = QIcon::fromTheme("dialog-cancel"); 0050 localMode = QIcon::fromTheme("computer"); 0051 serverMode = QIcon::fromTheme("network-server"); 0052 0053 connected = QIcon::fromTheme("network-connect"); 0054 disconnected = QIcon::fromTheme("network-disconnect"); 0055 0056 connect(localTreeWidget, &QTreeWidget::itemDoubleClicked, this, 0057 &DriverManagerUI::makePortEditable); 0058 } 0059 0060 void DriverManagerUI::makePortEditable(QTreeWidgetItem *selectedItem, int column) 0061 { 0062 // If it's the port column, then make it user-editable 0063 if (column == ::DriverManager::LOCAL_PORT_COLUMN) 0064 selectedItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | 0065 Qt::ItemIsEnabled); 0066 0067 localTreeWidget->editItem(selectedItem, ::DriverManager::LOCAL_PORT_COLUMN); 0068 } 0069 0070 DriverManager *DriverManager::_DriverManager = nullptr; 0071 INDIDBus *DriverManager::_INDIDBus = nullptr; 0072 0073 DriverManager *DriverManager::Instance() 0074 { 0075 if (_DriverManager == nullptr) 0076 { 0077 _DriverManager = new DriverManager(KStars::Instance()); 0078 _INDIDBus = new INDIDBus(KStars::Instance()); 0079 0080 qRegisterMetaType<QSharedPointer<DriverInfo>>("QSharedPointer<DriverInfo>"); 0081 } 0082 0083 return _DriverManager; 0084 } 0085 0086 void DriverManager::release() 0087 { 0088 delete (_DriverManager); 0089 delete (_INDIDBus); 0090 _DriverManager = nullptr; 0091 _INDIDBus = nullptr; 0092 } 0093 0094 DriverManager::DriverManager(QWidget *parent) : QDialog(parent) 0095 { 0096 #ifdef Q_OS_OSX 0097 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0098 #endif 0099 0100 currentPort = Options::serverPortStart() - 1; 0101 0102 QVBoxLayout *mainLayout = new QVBoxLayout; 0103 ui = new DriverManagerUI(this); 0104 mainLayout->addWidget(ui); 0105 setLayout(mainLayout); 0106 setWindowTitle(i18nc("@title:window", "Device Manager")); 0107 0108 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0109 mainLayout->addWidget(buttonBox); 0110 0111 connect(buttonBox, &QDialogButtonBox::rejected, this, &DriverManager::close); 0112 0113 connect(ui->addB, &QPushButton::clicked, this, &DriverManager::addINDIHost); 0114 connect(ui->modifyB, &QPushButton::clicked, this, &DriverManager::modifyINDIHost); 0115 connect(ui->removeB, &QPushButton::clicked, this, &DriverManager::removeINDIHost); 0116 0117 connect(ui->connectHostB, &QPushButton::clicked, this, &DriverManager::activateHostConnection); 0118 connect(ui->disconnectHostB, &QPushButton::clicked, this, &DriverManager::activateHostDisconnection); 0119 connect(ui->runServiceB, &QPushButton::clicked, this, &DriverManager::activateRunService); 0120 connect(ui->stopServiceB, &QPushButton::clicked, this, &DriverManager::activateStopService); 0121 connect(ui->localTreeWidget, &QTreeWidget::itemClicked, this, &DriverManager::updateLocalTab); 0122 connect(ui->clientTreeWidget, &QTreeWidget::itemClicked, this, &DriverManager::updateClientTab); 0123 connect(ui->localTreeWidget, &QTreeWidget::expanded, this, &DriverManager::resizeDeviceColumn); 0124 0125 // Do not use KSPaths here, this is for INDI 0126 if (Options::indiDriversDir().isEmpty()) 0127 Options::setIndiDriversDir( 0128 QStandardPaths::locate(QStandardPaths::GenericDataLocation, "indi", 0129 QStandardPaths::LocateDirectory)); 0130 0131 readXMLDrivers(); 0132 0133 readINDIHosts(); 0134 0135 m_CustomDrivers = new CustomDrivers(this, driversList); 0136 0137 updateCustomDrivers(); 0138 0139 #ifdef Q_OS_WIN 0140 ui->localTreeWidget->setEnabled(false); 0141 #endif 0142 } 0143 0144 DriverManager::~DriverManager() 0145 { 0146 clearServers(); 0147 } 0148 0149 void DriverManager::processDeviceStatus(const QSharedPointer<DriverInfo> &driver) 0150 { 0151 if (driver.isNull() || driver->getDriverSource() == GENERATED_SOURCE) 0152 return; 0153 0154 QString currentDriver; 0155 ServerMode mode = connectionMode; 0156 ServerManager *manager = driver->getServerManager(); 0157 bool dState = false; 0158 bool cState = false; 0159 0160 if (driver->getDriverSource() != HOST_SOURCE) 0161 { 0162 if (ui->localTreeWidget->currentItem()) 0163 currentDriver = ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN); 0164 0165 for (auto &item : ui->localTreeWidget->findItems( 0166 driver->getLabel(), Qt::MatchExactly | Qt::MatchRecursive)) 0167 { 0168 item->setText(LOCAL_VERSION_COLUMN, driver->getVersion()); 0169 0170 if (manager) 0171 mode = manager->getMode(); 0172 0173 dState = driver->getServerState(); 0174 cState = driver->getClientState() && dState; 0175 0176 bool locallyAvailable = false; 0177 if (driver->getAuxInfo().contains("LOCALLY_AVAILABLE")) 0178 locallyAvailable = 0179 driver->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool(); 0180 0181 switch (mode) 0182 { 0183 case SERVER_ONLY: 0184 if (locallyAvailable) 0185 { 0186 ui->runServiceB->setEnabled(!dState); 0187 ui->stopServiceB->setEnabled(dState); 0188 item->setIcon(LOCAL_STATUS_COLUMN, 0189 dState ? ui->runningPix : ui->stopPix); 0190 } 0191 else 0192 { 0193 ui->runServiceB->setEnabled(false); 0194 ui->stopServiceB->setEnabled(false); 0195 } 0196 0197 if (dState) 0198 { 0199 item->setIcon(LOCAL_MODE_COLUMN, ui->serverMode); 0200 if (manager) 0201 { 0202 item->setText(LOCAL_PORT_COLUMN, QString::number(manager->getPort())); 0203 } 0204 } 0205 else 0206 { 0207 item->setIcon(LOCAL_MODE_COLUMN, QIcon()); 0208 item->setText(LOCAL_PORT_COLUMN, QString::number(driver->getUserPort())); 0209 } 0210 0211 break; 0212 0213 case SERVER_CLIENT: 0214 if (locallyAvailable) 0215 { 0216 ui->runServiceB->setEnabled(!cState); 0217 ui->stopServiceB->setEnabled(cState); 0218 item->setIcon(LOCAL_STATUS_COLUMN, 0219 cState ? ui->runningPix : ui->stopPix); 0220 } 0221 else 0222 { 0223 ui->runServiceB->setEnabled(false); 0224 ui->stopServiceB->setEnabled(false); 0225 } 0226 0227 if (cState) 0228 { 0229 item->setIcon(LOCAL_MODE_COLUMN, ui->localMode); 0230 0231 if (manager) 0232 { 0233 item->setText(LOCAL_PORT_COLUMN, QString::number(manager->getPort())); 0234 } 0235 } 0236 else 0237 { 0238 item->setIcon(LOCAL_MODE_COLUMN, QIcon()); 0239 const auto port = driver->getUserPort() == -1 ? QString() : QString::number(driver->getUserPort()); 0240 item->setText(LOCAL_PORT_COLUMN, port); 0241 } 0242 0243 break; 0244 } 0245 0246 // Only update the log if the current driver is selected 0247 if (currentDriver == driver->getLabel()) 0248 { 0249 ui->serverLogText->clear(); 0250 ui->serverLogText->append(driver->getServerBuffer()); 0251 } 0252 } 0253 } 0254 else 0255 { 0256 for (auto &item : ui->clientTreeWidget->findItems(driver->getName(), Qt::MatchExactly, 0257 HOST_NAME_COLUMN)) 0258 { 0259 if (driver->getClientState()) 0260 { 0261 item->setIcon(HOST_STATUS_COLUMN, ui->connected); 0262 ui->connectHostB->setEnabled(false); 0263 ui->disconnectHostB->setEnabled(true); 0264 } 0265 else 0266 { 0267 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected); 0268 ui->connectHostB->setEnabled(true); 0269 ui->disconnectHostB->setEnabled(false); 0270 } 0271 } 0272 } 0273 } 0274 0275 void DriverManager::getUniqueHosts(const QList<QSharedPointer<DriverInfo>> &dList, 0276 QList<QList<QSharedPointer<DriverInfo>>> &uHosts) 0277 { 0278 bool found = false; 0279 0280 // Iterate over all drivers 0281 for (auto &dv : dList) 0282 { 0283 QList<QSharedPointer<DriverInfo>> uList; 0284 0285 // Let's see for drivers with identical hosts and ports 0286 for (auto &idv : dList) 0287 { 0288 // If we get a match between port and hostname, we add it to the list 0289 if ((dv->getHost() == idv->getHost() && dv->getPort() == idv->getPort())) 0290 { 0291 // Check if running already 0292 if (dv->getClientState() || dv->getServerState()) 0293 { 0294 int ans = KMessageBox::warningContinueCancel( 0295 nullptr, 0296 i18n("Driver %1 is already running, do you want to restart it?", 0297 dv->getLabel())); 0298 if (ans == KMessageBox::Cancel) 0299 continue; 0300 else 0301 { 0302 QList<QSharedPointer<DriverInfo>> stopList; 0303 stopList.append(dv); 0304 stopDevices(stopList); 0305 } 0306 } 0307 0308 found = false; 0309 0310 // Check to see if the driver already been added elsewhere 0311 for (auto &qdi : uHosts) 0312 { 0313 for (QSharedPointer<DriverInfo>di : qdi) 0314 { 0315 if (di == idv) 0316 { 0317 found = true; 0318 break; 0319 } 0320 } 0321 } 0322 0323 if (found == false) 0324 uList.append(idv); 0325 } 0326 } 0327 0328 if (uList.empty() == false) 0329 uHosts.append(uList); 0330 } 0331 } 0332 0333 void DriverManager::startDevices(const QList<QSharedPointer<DriverInfo>> &dList) 0334 { 0335 ServerManager *serverManager = nullptr; 0336 int port = -1; 0337 0338 QList<QList<QSharedPointer<DriverInfo>>> uHosts; 0339 getUniqueHosts(dList, uHosts); 0340 0341 qCDebug(KSTARS_INDI) << "INDI: Starting local drivers..."; 0342 0343 for (auto &qdv : uHosts) 0344 { 0345 if (qdv.empty()) 0346 continue; 0347 0348 port = qdv.at(0)->getPort(); 0349 auto host = qdv.at(0)->getHost(); 0350 0351 // Select random port within range is none specified. 0352 if (port == -1) 0353 port = getINDIPort(port); 0354 0355 if (port <= 0) 0356 { 0357 emit serverFailed(host, port, i18n("Cannot start INDI server: port error.")); 0358 return; 0359 } 0360 0361 serverManager = new ServerManager(host, port); 0362 0363 if (serverManager == nullptr) 0364 { 0365 emit serverFailed(host, port, i18n("Failed to create local INDI server")); 0366 return; 0367 } 0368 0369 servers.append(serverManager); 0370 serverManager->setPendingDrivers(qdv); 0371 serverManager->setMode(connectionMode); 0372 0373 connect(serverManager, &ServerManager::newServerLog, this, &DriverManager::updateLocalTab, Qt::UniqueConnection); 0374 connect(serverManager, &ServerManager::started, this, &DriverManager::setServerStarted, Qt::UniqueConnection); 0375 connect(serverManager, &ServerManager::failed, this, &DriverManager::setServerFailed, Qt::UniqueConnection); 0376 connect(serverManager, &ServerManager::terminated, this, &DriverManager::setServerTerminated, Qt::UniqueConnection); 0377 0378 serverManager->start(); 0379 } 0380 } 0381 0382 void DriverManager::startLocalDrivers(ServerManager *serverManager) 0383 { 0384 connect(serverManager, &ServerManager::driverStarted, this, &DriverManager::processDriverStartup, Qt::UniqueConnection); 0385 connect(serverManager, &ServerManager::driverFailed, this, &DriverManager::processDriverFailure, Qt::UniqueConnection); 0386 QtConcurrent::run(serverManager, &ServerManager::startDriver, serverManager->pendingDrivers().first()); 0387 } 0388 0389 void DriverManager::processDriverStartup(const QSharedPointer<DriverInfo> &driver) 0390 { 0391 emit driverStarted(driver); 0392 0393 auto manager = driver->getServerManager(); 0394 // Do we have more pending drivers? 0395 if (manager->pendingDrivers().count() > 0) 0396 { 0397 QtConcurrent::run(manager, &ServerManager::startDriver, manager->pendingDrivers().first()); 0398 return; 0399 } 0400 0401 // Nothing to do more if SERVER ONLY 0402 if (connectionMode == SERVER_ONLY) 0403 { 0404 return; 0405 } 0406 0407 // Otherwise proceed to start Client Manager 0408 startClientManager(manager->managedDrivers(), manager->getHost(), manager->getPort()); 0409 } 0410 0411 void DriverManager::processDriverFailure(const QSharedPointer<DriverInfo> &driver, const QString &message) 0412 { 0413 emit driverFailed(driver, message); 0414 0415 qCWarning(KSTARS_INDI) << "Driver" << driver->getName() << "failed to start. Retrying in 5 seconds..."; 0416 0417 QTimer::singleShot(5000, this, [driver]() 0418 { 0419 auto manager = driver->getServerManager(); 0420 // Do we have more pending drivers? 0421 if (manager->pendingDrivers().count() > 0) 0422 { 0423 QtConcurrent::run(manager, &ServerManager::startDriver, manager->pendingDrivers().first()); 0424 return; 0425 } 0426 }); 0427 } 0428 0429 void DriverManager::startClientManager(const QList<QSharedPointer<DriverInfo>> &qdv, const QString &host, int port) 0430 { 0431 auto clientManager = new ClientManager(); 0432 0433 for (auto &dv : qdv) 0434 clientManager->appendManagedDriver(dv); 0435 0436 connect(clientManager, &ClientManager::started, this, &DriverManager::setClientStarted, Qt::UniqueConnection); 0437 connect(clientManager, &ClientManager::failed, this, &DriverManager::setClientFailed, Qt::UniqueConnection); 0438 connect(clientManager, &ClientManager::terminated, this, &DriverManager::setClientTerminated, Qt::UniqueConnection); 0439 0440 clientManager->setServer(host.toLatin1().constData(), port); 0441 0442 GUIManager::Instance()->addClient(clientManager); 0443 INDIListener::Instance()->addClient(clientManager); 0444 0445 clientManager->establishConnection(); 0446 } 0447 0448 void DriverManager::stopDevices(const QList<QSharedPointer<DriverInfo>> &dList) 0449 { 0450 qCDebug(KSTARS_INDI) << "INDI: Stopping local drivers..."; 0451 0452 // #1 Disconnect all clients 0453 for (QSharedPointer<DriverInfo>dv : dList) 0454 { 0455 ClientManager *cm = dv->getClientManager(); 0456 0457 if (cm == nullptr) 0458 continue; 0459 0460 cm->removeManagedDriver(dv); 0461 0462 if (cm->count() == 0) 0463 { 0464 GUIManager::Instance()->removeClient(cm); 0465 INDIListener::Instance()->removeClient(cm); 0466 cm->disconnectServer(); 0467 clients.removeOne(cm); 0468 cm->deleteLater(); 0469 0470 KStars::Instance()->slotSetTelescopeEnabled(false); 0471 KStars::Instance()->slotSetDomeEnabled(false); 0472 } 0473 } 0474 0475 // #2 Disconnect all servers 0476 for (QSharedPointer<DriverInfo>dv : dList) 0477 { 0478 ServerManager *sm = dv->getServerManager(); 0479 0480 if (sm != nullptr) 0481 { 0482 sm->stopDriver(dv); 0483 0484 if (sm->size() == 0) 0485 { 0486 sm->stop(); 0487 servers.removeOne(sm); 0488 sm->deleteLater(); 0489 } 0490 } 0491 } 0492 0493 // Reset current port 0494 currentPort = Options::serverPortStart() - 1; 0495 0496 updateMenuActions(); 0497 } 0498 0499 void DriverManager::disconnectClients() 0500 { 0501 for (auto &clientManager : clients) 0502 { 0503 clientManager->disconnectAll(); 0504 clientManager->disconnect(); 0505 } 0506 } 0507 0508 void DriverManager::clearServers() 0509 { 0510 for (auto &serverManager : servers) 0511 serverManager->stop(); 0512 0513 qDeleteAll(servers); 0514 } 0515 0516 void DriverManager::activateRunService() 0517 { 0518 processLocalTree(true); 0519 } 0520 0521 void DriverManager::activateStopService() 0522 { 0523 processLocalTree(false); 0524 } 0525 0526 void DriverManager::activateHostConnection() 0527 { 0528 processRemoteTree(true); 0529 } 0530 0531 void DriverManager::activateHostDisconnection() 0532 { 0533 processRemoteTree(false); 0534 } 0535 0536 ClientManager *DriverManager::getClientManager(const QSharedPointer<DriverInfo> &driver) 0537 { 0538 auto cm = driver->getClientManager(); 0539 if (cm) 0540 return cm; 0541 // If no client manager found for the driver, return the first client manager on the same host and port number 0542 else if (!clients.isEmpty()) 0543 { 0544 auto host = driver->getHost(); 0545 auto port = driver->getPort(); 0546 auto it = std::find_if(clients.begin(), clients.end(), [host, port](const auto & oneClient) 0547 { 0548 return oneClient->getHost() == host && oneClient->getPort() == port; 0549 } 0550 ); 0551 if (it != clients.end()) 0552 return *it; 0553 } 0554 0555 return nullptr; 0556 } 0557 0558 void DriverManager::updateLocalTab() 0559 { 0560 if (ui->localTreeWidget->currentItem() == nullptr) 0561 return; 0562 0563 QString currentDriver = ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN); 0564 0565 auto device = std::find_if(driversList.begin(), driversList.end(), [currentDriver](const auto & oneDriver) 0566 { 0567 return currentDriver == oneDriver->getLabel(); 0568 }); 0569 if (device != driversList.end()) 0570 processDeviceStatus(*device); 0571 } 0572 0573 void DriverManager::updateClientTab() 0574 { 0575 QTreeWidgetItem *item = ui->clientTreeWidget->currentItem(); 0576 0577 if (item == nullptr) 0578 return; 0579 0580 auto hostname = item->text(HOST_NAME_COLUMN); 0581 int port = item->text(HOST_PORT_COLUMN).toInt(); 0582 0583 auto device = std::find_if(driversList.begin(), driversList.end(), [hostname, port](const auto & oneDriver) 0584 { 0585 return hostname == oneDriver->getName() && port == oneDriver->getPort(); 0586 }); 0587 if (device != driversList.end()) 0588 processDeviceStatus(*device); 0589 } 0590 0591 void DriverManager::processLocalTree(bool dState) 0592 { 0593 QList<QSharedPointer<DriverInfo>> processed_devices; 0594 0595 int port; 0596 bool portOK = false; 0597 0598 connectionMode = ui->localR->isChecked() ? SERVER_CLIENT : SERVER_ONLY; 0599 0600 for (auto &item : ui->localTreeWidget->selectedItems()) 0601 { 0602 for (auto &device : driversList) 0603 { 0604 port = -1; 0605 0606 //device->state = (dev_request == DriverInfo::DEV_TERMINATE) ? DriverInfo::DEV_START : DriverInfo::DEV_TERMINATE; 0607 if (item->text(LOCAL_NAME_COLUMN) == device->getLabel() && 0608 device->getServerState() != dState) 0609 { 0610 processed_devices.append(device); 0611 0612 // N.B. If multiple devices are selected to run under one device manager 0613 // then we select the port for the first device that has a valid port 0614 // entry, the rest are ignored. 0615 if (port == -1 && item->text(LOCAL_PORT_COLUMN).isEmpty() == false) 0616 { 0617 port = item->text(LOCAL_PORT_COLUMN).toInt(&portOK); 0618 // If we encounter conversion error, we abort 0619 if (portOK == false) 0620 { 0621 KSNotification::error(i18n("Invalid port entry: %1", item->text(LOCAL_PORT_COLUMN))); 0622 return; 0623 } 0624 } 0625 0626 device->setHostParameters("localhost", port); 0627 } 0628 } 0629 } 0630 0631 if (processed_devices.empty()) 0632 return; 0633 0634 if (dState) 0635 startDevices(processed_devices); 0636 else 0637 stopDevices(processed_devices); 0638 } 0639 0640 void DriverManager::setClientFailed(const QString &message) 0641 { 0642 auto client = qobject_cast<ClientManager*>(sender()); 0643 0644 if (client == nullptr) 0645 return; 0646 0647 auto host = client->getHost(); 0648 auto port = client->getPort(); 0649 0650 KNotification::beep(); 0651 0652 emit clientFailed(host, port, message); 0653 0654 GUIManager::Instance()->removeClient(client); 0655 INDIListener::Instance()->removeClient(client); 0656 0657 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::INDI); 0658 0659 clients.removeOne(client); 0660 client->deleteLater(); 0661 0662 if (connectionMode == SERVER_CLIENT) 0663 activateStopService(); 0664 0665 updateMenuActions(); 0666 updateLocalTab(); 0667 } 0668 0669 void DriverManager::setClientTerminated(const QString &message) 0670 { 0671 auto client = qobject_cast<ClientManager*>(sender()); 0672 0673 if (client == nullptr) 0674 return; 0675 0676 auto host = client->getHost(); 0677 auto port = client->getPort(); 0678 0679 KNotification::beep(); 0680 0681 emit clientTerminated(host, port, message); 0682 0683 GUIManager::Instance()->removeClient(client); 0684 INDIListener::Instance()->removeClient(client); 0685 0686 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::INDI, KSNotification::Alert); 0687 0688 clients.removeOne(client); 0689 client->deleteLater(); 0690 0691 updateMenuActions(); 0692 updateLocalTab(); 0693 updateClientTab(); 0694 } 0695 0696 void DriverManager::setServerStarted() 0697 { 0698 auto manager = qobject_cast<ServerManager*>(sender()); 0699 qCDebug(KSTARS_INDI) << "INDI: INDI Server started locally on port " << manager->getPort(); 0700 startLocalDrivers(manager); 0701 emit serverStarted(manager->getHost(), manager->getPort()); 0702 } 0703 0704 void DriverManager::setServerFailed(const QString &message) 0705 { 0706 auto server = qobject_cast<ServerManager *>(sender()); 0707 0708 if (server == nullptr) 0709 return; 0710 0711 for (auto &dv : driversList) 0712 { 0713 if (dv->getServerManager() == server) 0714 { 0715 dv->reset(); 0716 } 0717 } 0718 0719 if (server->getMode() == SERVER_ONLY) 0720 KSNotification::error(message); 0721 0722 emit serverFailed(server->getHost(), server->getPort(), message); 0723 servers.removeOne(server); 0724 server->deleteLater(); 0725 0726 updateLocalTab(); 0727 } 0728 0729 void DriverManager::setServerTerminated(const QString &message) 0730 { 0731 auto server = qobject_cast<ServerManager *>(sender()); 0732 0733 if (server == nullptr) 0734 return; 0735 0736 for (auto &dv : driversList) 0737 { 0738 if (dv->getServerManager() == server) 0739 { 0740 dv->reset(); 0741 } 0742 } 0743 0744 if (server->getMode() == SERVER_ONLY) 0745 KSNotification::error(message); 0746 0747 emit serverTerminated(server->getHost(), server->getPort(), message); 0748 servers.removeOne(server); 0749 server->deleteLater(); 0750 0751 updateLocalTab(); 0752 } 0753 0754 void DriverManager::processRemoteTree(bool dState) 0755 { 0756 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem(); 0757 if (!currentItem) 0758 return; 0759 0760 for (auto &dv : driversList) 0761 { 0762 if (dv->getDriverSource() != HOST_SOURCE) 0763 continue; 0764 0765 //qDebug() << Q_FUNC_INFO << "Current item port " << currentItem->text(HOST_PORT_COLUMN) << " current dev " << dv->getName() << " -- port " << dv->getPort() << Qt::endl; 0766 //qDebug() << Q_FUNC_INFO << "dState is : " << (dState ? "True" : "False") << Qt::endl; 0767 0768 if (currentItem->text(HOST_NAME_COLUMN) == dv->getName() && 0769 currentItem->text(HOST_PORT_COLUMN).toInt() == dv->getPort()) 0770 { 0771 // Nothing changed, return 0772 if (dv->getClientState() == dState) 0773 return; 0774 0775 // connect to host 0776 if (dState) 0777 connectRemoteHost(dv); 0778 // Disconnect form host 0779 else 0780 disconnectRemoteHost(dv); 0781 } 0782 } 0783 } 0784 0785 void DriverManager::connectRemoteHost(const QSharedPointer<DriverInfo> &driver) 0786 { 0787 auto host = driver->getHost(); 0788 auto port = driver->getPort(); 0789 0790 auto clientManager = new ClientManager(); 0791 0792 clientManager->appendManagedDriver(driver); 0793 0794 connect(clientManager, &ClientManager::started, this, &DriverManager::setClientStarted, Qt::UniqueConnection); 0795 connect(clientManager, &ClientManager::failed, this, &DriverManager::setClientFailed, Qt::UniqueConnection); 0796 connect(clientManager, &ClientManager::terminated, this, &DriverManager::setClientTerminated, Qt::UniqueConnection); 0797 0798 clientManager->setServer(host.toLatin1().constData(), port); 0799 0800 GUIManager::Instance()->addClient(clientManager); 0801 INDIListener::Instance()->addClient(clientManager); 0802 0803 clientManager->establishConnection(); 0804 } 0805 0806 void DriverManager::setClientStarted() 0807 { 0808 auto clientManager = qobject_cast<ClientManager *>(sender()); 0809 if (clientManager == nullptr) 0810 return; 0811 0812 clients.append(clientManager); 0813 updateLocalTab(); 0814 updateMenuActions(); 0815 0816 KSNotification::event(QLatin1String("ConnectionSuccessful"), 0817 i18n("Connected to INDI server")); 0818 0819 emit clientStarted(clientManager->getHost(), clientManager->getPort()); 0820 } 0821 0822 bool DriverManager::disconnectRemoteHost(const QSharedPointer<DriverInfo> &driver) 0823 { 0824 ClientManager *clientManager = driver->getClientManager(); 0825 0826 if (clientManager) 0827 { 0828 clientManager->removeManagedDriver(driver); 0829 clientManager->disconnectAll(); 0830 GUIManager::Instance()->removeClient(clientManager); 0831 INDIListener::Instance()->removeClient(clientManager); 0832 clients.removeOne(clientManager); 0833 clientManager->deleteLater(); 0834 updateMenuActions(); 0835 return true; 0836 } 0837 0838 return false; 0839 } 0840 0841 void DriverManager::resizeDeviceColumn() 0842 { 0843 ui->localTreeWidget->resizeColumnToContents(0); 0844 } 0845 0846 void DriverManager::updateMenuActions() 0847 { 0848 // We iterate over devices, we enable INDI Control Panel if we have any active device 0849 // We enable capture image sequence if we have any imaging device 0850 0851 QAction *tmpAction = nullptr; 0852 bool activeDevice = false; 0853 0854 if (clients.size() > 0) 0855 activeDevice = true; 0856 0857 tmpAction = KStars::Instance()->actionCollection()->action("indi_cpl"); 0858 if (tmpAction != nullptr) 0859 { 0860 //qDebug() << Q_FUNC_INFO << "indi_cpl action set to active" << Qt::endl; 0861 tmpAction->setEnabled(activeDevice); 0862 } 0863 } 0864 0865 int DriverManager::getINDIPort(int customPort) 0866 { 0867 #ifdef Q_OS_WIN 0868 qWarning() << "INDI server is currently not supported on Windows."; 0869 return -1; 0870 #else 0871 int lastPort = Options::serverPortEnd(); 0872 bool success = false; 0873 currentPort++; 0874 0875 // recycle 0876 if (currentPort > lastPort) 0877 currentPort = Options::serverPortStart(); 0878 0879 QTcpServer temp_server; 0880 0881 if (customPort != -1) 0882 { 0883 success = temp_server.listen(QHostAddress::LocalHost, customPort); 0884 if (success) 0885 { 0886 temp_server.close(); 0887 return customPort; 0888 } 0889 else 0890 return -1; 0891 } 0892 0893 for (; currentPort <= lastPort; currentPort++) 0894 { 0895 success = temp_server.listen(QHostAddress::LocalHost, currentPort); 0896 if (success) 0897 { 0898 temp_server.close(); 0899 return currentPort; 0900 } 0901 } 0902 return -1; 0903 #endif 0904 } 0905 0906 bool DriverManager::readINDIHosts() 0907 { 0908 QString indiFile("indihosts.xml"); 0909 //QFile localeFile; 0910 QFile file; 0911 char errmsg[1024]; 0912 char c; 0913 LilXML *xmlParser = newLilXML(); 0914 XMLEle *root = nullptr; 0915 XMLAtt *ap = nullptr; 0916 QString hName, hHost, hPort; 0917 0918 lastGroup = nullptr; 0919 file.setFileName(KSPaths::locate(QStandardPaths::AppLocalDataLocation, indiFile)); 0920 if (file.fileName().isEmpty() || !file.open(QIODevice::ReadOnly)) 0921 { 0922 delLilXML(xmlParser); 0923 return false; 0924 } 0925 0926 while (file.getChar(&c)) 0927 { 0928 root = readXMLEle(xmlParser, c, errmsg); 0929 0930 if (root) 0931 { 0932 // Get host name 0933 ap = findXMLAtt(root, "name"); 0934 if (!ap) 0935 { 0936 delLilXML(xmlParser); 0937 return false; 0938 } 0939 0940 hName = QString(valuXMLAtt(ap)); 0941 0942 // Get host name 0943 ap = findXMLAtt(root, "hostname"); 0944 0945 if (!ap) 0946 { 0947 delLilXML(xmlParser); 0948 return false; 0949 } 0950 0951 hHost = QString(valuXMLAtt(ap)); 0952 0953 ap = findXMLAtt(root, "port"); 0954 0955 if (!ap) 0956 { 0957 delLilXML(xmlParser); 0958 return false; 0959 } 0960 0961 hPort = QString(valuXMLAtt(ap)); 0962 0963 QSharedPointer<DriverInfo> dv(new DriverInfo(hName)); 0964 dv->setHostParameters(hHost, hPort.toInt()); 0965 dv->setDriverSource(HOST_SOURCE); 0966 0967 connect(dv.get(), &DriverInfo::deviceStateChanged, this, [dv, this]() 0968 { 0969 processDeviceStatus(dv); 0970 }); 0971 0972 driversList.append(dv); 0973 0974 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget, lastGroup); 0975 lastGroup = item; 0976 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected); 0977 item->setText(HOST_NAME_COLUMN, hName); 0978 item->setText(HOST_PORT_COLUMN, hPort); 0979 0980 delXMLEle(root); 0981 } 0982 else if (errmsg[0]) 0983 { 0984 qDebug() << Q_FUNC_INFO << errmsg; 0985 delLilXML(xmlParser); 0986 return false; 0987 } 0988 } 0989 0990 delLilXML(xmlParser); 0991 0992 return true; 0993 } 0994 0995 bool DriverManager::readXMLDrivers() 0996 { 0997 QDir indiDir; 0998 QString driverName; 0999 1000 QString driversDir = Options::indiDriversDir(); 1001 #ifdef Q_OS_OSX 1002 if (Options::indiDriversAreInternal()) 1003 driversDir = 1004 QCoreApplication::applicationDirPath() + "/../Resources/DriverSupport"; 1005 #endif 1006 1007 if (indiDir.cd(driversDir) == false) 1008 { 1009 KSNotification::error(i18n("Unable to find INDI drivers directory: %1\nPlease " 1010 "make sure to set the correct " 1011 "path in KStars configuration", 1012 driversDir)); 1013 return false; 1014 } 1015 1016 indiDir.setNameFilters(QStringList() << "indi_*.xml" 1017 << "drivers.xml"); 1018 indiDir.setFilter(QDir::Files | QDir::NoSymLinks); 1019 QFileInfoList list = indiDir.entryInfoList(); 1020 1021 for (auto &fileInfo : list) 1022 { 1023 if (fileInfo.fileName().endsWith(QLatin1String("_sk.xml"))) 1024 continue; 1025 1026 processXMLDriver(fileInfo.absoluteFilePath()); 1027 } 1028 1029 // JM 2022.08.24: Process local source last as INDI sources should have higher priority than KStars own database. 1030 processXMLDriver(QLatin1String(":/indidrivers.xml")); 1031 1032 return true; 1033 } 1034 1035 void DriverManager::processXMLDriver(const QString &driverName) 1036 { 1037 QFile file(driverName); 1038 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) 1039 { 1040 KSNotification::error(i18n("Failed to open INDI Driver file: %1", driverName)); 1041 return; 1042 } 1043 1044 char errmsg[ERRMSG_SIZE]; 1045 char c; 1046 LilXML *xmlParser = newLilXML(); 1047 XMLEle *root = nullptr; 1048 XMLEle *ep = nullptr; 1049 1050 if (driverName.endsWith(QLatin1String("drivers.xml"))) 1051 driverSource = PRIMARY_XML; 1052 else 1053 driverSource = THIRD_PARTY_XML; 1054 1055 while (file.getChar(&c)) 1056 { 1057 root = readXMLEle(xmlParser, c, errmsg); 1058 1059 if (root) 1060 { 1061 // If the XML file is using the INDI Library v1.3+ format 1062 if (!strcmp(tagXMLEle(root), "driversList")) 1063 { 1064 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) 1065 { 1066 if (!buildDeviceGroup(ep, errmsg)) 1067 prXMLEle(stderr, ep, 0); 1068 } 1069 } 1070 // If using the older format 1071 else 1072 { 1073 if (!buildDeviceGroup(root, errmsg)) 1074 prXMLEle(stderr, root, 0); 1075 } 1076 1077 delXMLEle(root); 1078 } 1079 else if (errmsg[0]) 1080 { 1081 qCDebug(KSTARS_INDI) << QString(errmsg); 1082 delLilXML(xmlParser); 1083 return; 1084 } 1085 } 1086 1087 delLilXML(xmlParser); 1088 } 1089 1090 bool DriverManager::buildDeviceGroup(XMLEle *root, char errmsg[]) 1091 { 1092 XMLAtt *ap; 1093 XMLEle *ep; 1094 QString groupName; 1095 QTreeWidgetItem *group; 1096 DeviceFamily groupType = KSTARS_TELESCOPE; 1097 1098 // avoid overflow 1099 if (strlen(tagXMLEle(root)) > 1024) 1100 return false; 1101 1102 // Get device grouping name 1103 ap = findXMLAtt(root, "group"); 1104 1105 if (!ap) 1106 { 1107 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute", 1108 tagXMLEle(root)); 1109 return false; 1110 } 1111 1112 groupName = valuXMLAtt(ap); 1113 groupType = DeviceFamilyLabels.key(groupName); 1114 1115 #ifndef HAVE_CFITSIO 1116 // We do not create these groups if we don't have CFITSIO support 1117 if (groupType == KSTARS_CCD || groupType == KSTARS_VIDEO) 1118 return true; 1119 #endif 1120 1121 // Find if the group already exists 1122 QList<QTreeWidgetItem *> treeList = 1123 ui->localTreeWidget->findItems(groupName, Qt::MatchExactly); 1124 if (!treeList.isEmpty()) 1125 group = treeList[0]; 1126 else 1127 group = new QTreeWidgetItem(ui->localTreeWidget, lastGroup); 1128 1129 group->setText(0, groupName); 1130 lastGroup = group; 1131 1132 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) 1133 { 1134 if (!buildDriverElement(ep, group, groupType, errmsg)) 1135 return false; 1136 } 1137 1138 return true; 1139 } 1140 1141 bool DriverManager::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup, 1142 DeviceFamily groupType, char errmsg[]) 1143 { 1144 XMLAtt *ap; 1145 XMLEle *el; 1146 QString label; 1147 QString driver; 1148 QString version; 1149 // N.B. NOT an i18n string. 1150 QString manufacturer("Others"); 1151 QString name; 1152 QString port; 1153 QString skel; 1154 QVariantMap vMap; 1155 //double focal_length(-1), aperture(-1); 1156 1157 ap = findXMLAtt(root, "label"); 1158 if (!ap) 1159 { 1160 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute", 1161 tagXMLEle(root)); 1162 return false; 1163 } 1164 1165 label = valuXMLAtt(ap); 1166 1167 // Label is unique, so if we have the same label, we simply ignore 1168 if (findDriverByLabel(label) != nullptr) 1169 return true; 1170 1171 ap = findXMLAtt(root, "manufacturer"); 1172 if (ap) 1173 manufacturer = valuXMLAtt(ap); 1174 1175 // Search for optional port attribute 1176 ap = findXMLAtt(root, "port"); 1177 if (ap) 1178 port = valuXMLAtt(ap); 1179 1180 // Search for skel file, if any 1181 // Search for optional port attribute 1182 ap = findXMLAtt(root, "skel"); 1183 if (ap) 1184 skel = valuXMLAtt(ap); 1185 1186 // Find MDPD: Multiple Devices Per Driver 1187 ap = findXMLAtt(root, "mdpd"); 1188 if (ap) 1189 { 1190 bool mdpd = false; 1191 mdpd = (QString(valuXMLAtt(ap)) == QString("true")) ? true : false; 1192 vMap.insert("mdpd", mdpd); 1193 } 1194 1195 el = findXMLEle(root, "driver"); 1196 1197 if (!el) 1198 return false; 1199 1200 driver = pcdataXMLEle(el); 1201 1202 ap = findXMLAtt(el, "name"); 1203 if (!ap) 1204 { 1205 snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a name attribute", 1206 tagXMLEle(el)); 1207 return false; 1208 } 1209 1210 name = valuXMLAtt(ap); 1211 1212 el = findXMLEle(root, "version"); 1213 1214 if (!el) 1215 return false; 1216 1217 version = pcdataXMLEle(el); 1218 bool versionOK = false; 1219 version.toDouble(&versionOK); 1220 if (versionOK == false) 1221 version = "1.0"; 1222 1223 bool driverIsAvailable = checkDriverAvailability(driver); 1224 1225 vMap.insert("LOCALLY_AVAILABLE", driverIsAvailable); 1226 QIcon remoteIcon = QIcon::fromTheme("network-modem"); 1227 1228 QTreeWidgetItem *device = new QTreeWidgetItem(DGroup); 1229 1230 device->setText(LOCAL_NAME_COLUMN, label); 1231 if (driverIsAvailable) 1232 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix); 1233 else 1234 device->setIcon(LOCAL_STATUS_COLUMN, remoteIcon); 1235 device->setText(LOCAL_VERSION_COLUMN, version); 1236 device->setText(LOCAL_PORT_COLUMN, port); 1237 1238 //if ((driverSource == PRIMARY_XML) && driversStringList.contains(driver) == false) 1239 if (groupType == KSTARS_TELESCOPE && driversStringList.contains(driver) == false) 1240 driversStringList.append(driver); 1241 1242 QSharedPointer<DriverInfo> dv(new DriverInfo(name)); 1243 1244 dv->setLabel(label); 1245 dv->setVersion(version); 1246 dv->setExecutable(driver); 1247 dv->setManufacturer(manufacturer); 1248 dv->setSkeletonFile(skel); 1249 dv->setType(groupType); 1250 dv->setDriverSource(driverSource); 1251 dv->setUserPort(port.isEmpty() ? 7624 : port.toInt()); 1252 dv->setAuxInfo(vMap); 1253 1254 connect(dv.get(), &DriverInfo::deviceStateChanged, this, [dv, this]() 1255 { 1256 processDeviceStatus(dv); 1257 }); 1258 1259 driversList.append(dv); 1260 1261 return true; 1262 } 1263 1264 bool DriverManager::checkDriverAvailability(const QString &driver) 1265 { 1266 QString indiServerDir = Options::indiServer(); 1267 if (Options::indiServerIsInternal()) 1268 indiServerDir = QCoreApplication::applicationDirPath(); 1269 else 1270 indiServerDir = QFileInfo(Options::indiServer()).dir().path(); 1271 1272 QFile driverFile(indiServerDir + '/' + driver); 1273 1274 if (driverFile.exists() == false) 1275 return (!QStandardPaths::findExecutable(indiServerDir + '/' + driver).isEmpty()); 1276 1277 return true; 1278 } 1279 1280 void DriverManager::updateCustomDrivers() 1281 { 1282 for (const QVariantMap &oneDriver : m_CustomDrivers->customDrivers()) 1283 { 1284 QSharedPointer<DriverInfo> dv(new DriverInfo(oneDriver["Name"].toString())); 1285 dv->setLabel(oneDriver["Label"].toString()); 1286 dv->setUniqueLabel(dv->getLabel()); 1287 dv->setExecutable(oneDriver["Exec"].toString()); 1288 dv->setVersion(oneDriver["Version"].toString()); 1289 dv->setManufacturer(oneDriver["Manufacturer"].toString()); 1290 dv->setType(DeviceFamilyLabels.key(oneDriver["Family"].toString())); 1291 dv->setDriverSource(CUSTOM_SOURCE); 1292 1293 bool driverIsAvailable = checkDriverAvailability(oneDriver["Exec"].toString()); 1294 QVariantMap vMap; 1295 vMap.insert("LOCALLY_AVAILABLE", driverIsAvailable); 1296 dv->setAuxInfo(vMap); 1297 1298 driversList.append(dv); 1299 } 1300 } 1301 1302 // JM 2018-07-23: Disabling the old custom drivers method 1303 #if 0 1304 void DriverManager::updateCustomDrivers() 1305 { 1306 QString label; 1307 QString driver; 1308 QString version; 1309 QString name; 1310 QTreeWidgetItem *group = nullptr; 1311 QTreeWidgetItem *widgetDev = nullptr; 1312 QVariantMap vMap; 1313 QSharedPointer<DriverInfo>drv = nullptr; 1314 1315 // Find if the group already exists 1316 QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems("Telescopes", Qt::MatchExactly); 1317 if (!treeList.isEmpty()) 1318 group = treeList[0]; 1319 else 1320 return; 1321 1322 KStarsData::Instance()->logObject()->readAll(); 1323 1324 // Find custom telescope to ADD/UPDATE 1325 foreach (OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList())) 1326 { 1327 name = label = s->name(); 1328 1329 if (s->driver() == i18n("None")) 1330 continue; 1331 1332 // If driver already exists, just update values 1333 if ((drv = findDriverByLabel(label))) 1334 { 1335 if (s->aperture() > 0 && s->focalLength() > 0) 1336 { 1337 vMap.insert("TELESCOPE_APERTURE", s->aperture()); 1338 vMap.insert("TELESCOPE_FOCAL_LENGTH", s->focalLength()); 1339 drv->setAuxInfo(vMap); 1340 } 1341 1342 drv->setExecutable(s->driver()); 1343 1344 continue; 1345 } 1346 1347 driver = s->driver(); 1348 version = QString("1.0"); 1349 1350 QTreeWidgetItem *device = new QTreeWidgetItem(group); 1351 device->setText(LOCAL_NAME_COLUMN, QString(label)); 1352 device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix); 1353 device->setText(LOCAL_VERSION_COLUMN, QString(version)); 1354 1355 QSharedPointer<DriverInfo>dv = new DriverInfo(name); 1356 1357 dv->setLabel(label); 1358 dv->setExecutable(driver); 1359 dv->setVersion(version); 1360 dv->setType(KSTARS_TELESCOPE); 1361 dv->setDriverSource(EM_XML); 1362 1363 if (s->aperture() > 0 && s->focalLength() > 0) 1364 { 1365 vMap.insert("TELESCOPE_APERTURE", s->aperture()); 1366 vMap.insert("TELESCOPE_FOCAL_LENGTH", s->focalLength()); 1367 dv->setAuxInfo(vMap); 1368 } 1369 1370 connect(dv, SIGNAL(deviceStateChanged(DriverInfo*)), this, SLOT(processDeviceStatus(DriverInfo*))); 1371 driversList.append(dv); 1372 } 1373 1374 // Find custom telescope to REMOVE 1375 foreach (QSharedPointer<DriverInfo>dev, driversList) 1376 { 1377 // If it's from primary xml file or it is in a running state, continue. 1378 if (dev->getDriverSource() != EM_XML || dev->getClientState()) 1379 continue; 1380 1381 if (KStarsData::Instance()->logObject()->findScopeByName(dev->getName())) 1382 continue; 1383 1384 // Find if the group already exists 1385 QList<QTreeWidgetItem *> devList = 1386 ui->localTreeWidget->findItems(dev->getLabel(), Qt::MatchExactly | Qt::MatchRecursive); 1387 if (!devList.isEmpty()) 1388 { 1389 widgetDev = devList[0]; 1390 group->removeChild(widgetDev); 1391 } 1392 else 1393 return; 1394 1395 driversList.removeOne(dev); 1396 delete (dev); 1397 } 1398 } 1399 #endif 1400 1401 void DriverManager::addINDIHost() 1402 { 1403 QDialog hostConfDialog; 1404 Ui::INDIHostConf hostConf; 1405 hostConf.setupUi(&hostConfDialog); 1406 hostConfDialog.setWindowTitle(i18nc("@title:window", "Add Host")); 1407 bool portOk = false; 1408 1409 if (hostConfDialog.exec() == QDialog::Accepted) 1410 { 1411 QSharedPointer<DriverInfo> hostItem(new DriverInfo(hostConf.nameIN->text())); 1412 1413 hostConf.portnumber->text().toInt(&portOk); 1414 1415 if (portOk == false) 1416 { 1417 KSNotification::error(i18n("Error: the port number is invalid.")); 1418 return; 1419 } 1420 1421 hostItem->setHostParameters(hostConf.hostname->text(), 1422 hostConf.portnumber->text().toInt()); 1423 1424 //search for duplicates 1425 //for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) 1426 foreach (QSharedPointer<DriverInfo>host, driversList) 1427 if (hostItem->getName() == host->getName() && 1428 hostItem->getPort() == host->getPort()) 1429 { 1430 KSNotification::error(i18n("Host: %1 Port: %2 already exists.", 1431 hostItem->getName(), hostItem->getPort())); 1432 return; 1433 } 1434 1435 hostItem->setDriverSource(HOST_SOURCE); 1436 1437 connect(hostItem.get(), &DriverInfo::deviceStateChanged, this, [hostItem, this]() 1438 { 1439 processDeviceStatus(hostItem); 1440 }); 1441 1442 driversList.append(hostItem); 1443 1444 QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget); 1445 item->setIcon(HOST_STATUS_COLUMN, ui->disconnected); 1446 item->setText(HOST_NAME_COLUMN, hostConf.nameIN->text()); 1447 item->setText(HOST_PORT_COLUMN, hostConf.portnumber->text()); 1448 } 1449 1450 saveHosts(); 1451 } 1452 1453 void DriverManager::modifyINDIHost() 1454 { 1455 QDialog hostConfDialog; 1456 Ui::INDIHostConf hostConf; 1457 hostConf.setupUi(&hostConfDialog); 1458 hostConfDialog.setWindowTitle(i18nc("@title:window", "Modify Host")); 1459 1460 QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem(); 1461 1462 if (currentItem == nullptr) 1463 return; 1464 1465 for (auto &host : driversList) 1466 { 1467 if (currentItem->text(HOST_NAME_COLUMN) == host->getName() && 1468 currentItem->text(HOST_PORT_COLUMN).toInt() == host->getPort()) 1469 { 1470 hostConf.nameIN->setText(host->getName()); 1471 hostConf.hostname->setText(host->getHost()); 1472 hostConf.portnumber->setText(QString::number(host->getPort())); 1473 1474 if (hostConfDialog.exec() == QDialog::Accepted) 1475 { 1476 host->setName(hostConf.nameIN->text()); 1477 host->setHostParameters(hostConf.hostname->text(), 1478 hostConf.portnumber->text().toInt()); 1479 1480 currentItem->setText(HOST_NAME_COLUMN, hostConf.nameIN->text()); 1481 currentItem->setText(HOST_PORT_COLUMN, hostConf.portnumber->text()); 1482 1483 saveHosts(); 1484 return; 1485 } 1486 } 1487 } 1488 } 1489 1490 void DriverManager::removeINDIHost() 1491 { 1492 if (ui->clientTreeWidget->currentItem() == nullptr) 1493 return; 1494 1495 foreach (QSharedPointer<DriverInfo>host, driversList) 1496 if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == 1497 host->getName() && 1498 ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN).toInt() == 1499 host->getPort()) 1500 { 1501 if (host->getClientState()) 1502 { 1503 KSNotification::error( 1504 i18n("You need to disconnect the client before removing it.")); 1505 return; 1506 } 1507 1508 if (KMessageBox::warningContinueCancel( 1509 nullptr, 1510 i18n("Are you sure you want to remove the %1 client?", 1511 ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN)), 1512 i18n("Delete Confirmation"), 1513 KStandardGuiItem::del()) != KMessageBox::Continue) 1514 return; 1515 1516 driversList.removeOne(host); 1517 delete (ui->clientTreeWidget->currentItem()); 1518 break; 1519 } 1520 1521 saveHosts(); 1522 } 1523 1524 void DriverManager::saveHosts() 1525 { 1526 QFile file; 1527 QString hostData; 1528 1529 //determine filename in local user KDE directory tree. 1530 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 1531 .filePath("indihosts.xml")); 1532 1533 if (!file.open(QIODevice::WriteOnly)) 1534 { 1535 KSNotification::sorry(i18n("Unable to write to file 'indihosts.xml'\nAny changes " 1536 "to INDI hosts configurations will not be saved."), 1537 i18n("Could Not Open File")); 1538 return; 1539 } 1540 1541 QTextStream outstream(&file); 1542 1543 //for (uint i= 0; i < ksw->data()->INDIHostsList.count(); i++) 1544 foreach (QSharedPointer<DriverInfo>host, driversList) 1545 { 1546 if (host->getDriverSource() != HOST_SOURCE) 1547 continue; 1548 1549 hostData = "<INDIHost name='"; 1550 hostData += host->getName(); 1551 hostData += "' hostname='"; 1552 hostData += host->getHost(); 1553 hostData += "' port='"; 1554 hostData += QString::number(host->getPort()); 1555 hostData += "' />\n"; 1556 1557 outstream << hostData; 1558 } 1559 1560 file.close(); 1561 } 1562 1563 QSharedPointer<DriverInfo> DriverManager::findDriverByName(const QString &name) 1564 { 1565 auto driver = std::find_if(driversList.begin(), driversList.end(), [name](auto & oneDriver) 1566 { 1567 return oneDriver->getName() == name; 1568 }); 1569 1570 if (driver != driversList.end()) 1571 return *driver; 1572 else 1573 return QSharedPointer<DriverInfo>(); 1574 } 1575 1576 QSharedPointer<DriverInfo> DriverManager::findDriverByLabel(const QString &label) 1577 { 1578 auto driver = std::find_if(driversList.begin(), driversList.end(), [label](auto & oneDriver) 1579 { 1580 return oneDriver->getLabel() == label; 1581 }); 1582 1583 if (driver != driversList.end()) 1584 return *driver; 1585 else 1586 return QSharedPointer<DriverInfo>(); 1587 } 1588 1589 QSharedPointer<DriverInfo> DriverManager::findDriverByExec(const QString &exec) 1590 { 1591 auto driver = std::find_if(driversList.begin(), driversList.end(), [exec](auto & oneDriver) 1592 { 1593 return oneDriver->getLabel() == exec; 1594 }); 1595 1596 if (driver != driversList.end()) 1597 return *driver; 1598 else 1599 return QSharedPointer<DriverInfo>(); 1600 } 1601 1602 QString DriverManager::getUniqueDeviceLabel(const QString &label) 1603 { 1604 int nset = 0; 1605 QString uniqueLabel = label; 1606 1607 for (auto &cm : clients) 1608 { 1609 auto devices = cm->getDevices(); 1610 1611 for (auto &dv : devices) 1612 { 1613 if (label == QString(dv.getDeviceName())) 1614 nset++; 1615 } 1616 } 1617 if (nset > 0) 1618 uniqueLabel = QString("%1 %2").arg(label).arg(nset + 1); 1619 1620 return uniqueLabel; 1621 } 1622 1623 QJsonArray DriverManager::getDriverList() const 1624 { 1625 QJsonArray driverArray; 1626 1627 for (const auto &drv : driversList) 1628 driverArray.append(drv->toJson()); 1629 1630 return driverArray; 1631 } 1632 1633 bool DriverManager::restartDriver(const QSharedPointer<DriverInfo> &driver) 1634 { 1635 for (auto &oneServer : servers) 1636 { 1637 if (oneServer->contains(driver)) 1638 { 1639 return oneServer->restartDriver(driver); 1640 } 1641 } 1642 1643 return false; 1644 }