File indexing completed on 2024-04-14 14:10:26
0001 /* 0002 SPDX-FileCopyrightText: 2016 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "profileeditor.h" 0008 0009 #include "geolocation.h" 0010 #include "kstarsdata.h" 0011 #include "ksnotification.h" 0012 #include "Options.h" 0013 #include "guide/guide.h" 0014 #include "indi/driverinfo.h" 0015 #include "indi/drivermanager.h" 0016 #include "profilescriptdialog.h" 0017 #include "ui_indihub.h" 0018 0019 #include "ekos_debug.h" 0020 0021 #include <QNetworkInterface> 0022 0023 ProfileEditorUI::ProfileEditorUI(QWidget *p) : QFrame(p) 0024 { 0025 setupUi(this); 0026 } 0027 0028 ProfileEditor::ProfileEditor(QWidget *w) : QDialog(w) 0029 { 0030 setObjectName("profileEditorDialog"); 0031 #ifdef Q_OS_OSX 0032 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0033 #endif 0034 ui = new ProfileEditorUI(this); 0035 0036 pi = nullptr; 0037 0038 m_MountModel = new QStandardItemModel(this); 0039 m_CameraModel = new QStandardItemModel(this); 0040 m_GuiderModel = new QStandardItemModel(this); 0041 m_FocuserModel = new QStandardItemModel(this); 0042 0043 QVBoxLayout *mainLayout = new QVBoxLayout; 0044 mainLayout->addWidget(ui); 0045 setLayout(mainLayout); 0046 0047 setWindowTitle(i18nc("@title:window", "Profile Editor")); 0048 0049 // Create button box and link it to save and reject functions 0050 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Close, this); 0051 0052 buttonBox->setObjectName("dialogButtons"); 0053 mainLayout->addWidget(buttonBox); 0054 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); 0055 connect(buttonBox, SIGNAL(accepted()), this, SLOT(saveProfile())); 0056 0057 connect(ui->scriptsB, &QPushButton::clicked, this, &ProfileEditor::executeScriptEditor); 0058 0059 connect(ui->openWebManagerB, &QPushButton::clicked, this, [this]() 0060 { 0061 QUrl url(QString("http://" + ui->remoteHost->text() + ":8624")); 0062 QDesktopServices::openUrl(url); 0063 }); 0064 0065 connect(ui->INDIWebManagerCheck, &QCheckBox::toggled, this, [&](bool enabled) 0066 { 0067 ui->openWebManagerB->setEnabled(enabled); 0068 ui->remoteDrivers->setEnabled(enabled || ui->localMode->isChecked()); 0069 ui->scriptsB->setEnabled(enabled || ui->localMode->isChecked()); 0070 }); 0071 0072 connect(ui->guideTypeCombo, SIGNAL(activated(int)), this, SLOT(updateGuiderSelection(int))); 0073 0074 connect(ui->scanB, &QPushButton::clicked, this, &ProfileEditor::scanNetwork); 0075 0076 #ifdef Q_OS_WIN 0077 ui->remoteMode->setChecked(true); 0078 ui->localMode->setEnabled(false); 0079 setRemoteMode(true); 0080 #else 0081 connect(ui->remoteMode, SIGNAL(toggled(bool)), this, SLOT(setRemoteMode(bool))); 0082 #endif 0083 0084 connect(ui->indihubB, &QPushButton::clicked, this, &ProfileEditor::showINDIHub); 0085 0086 // Load all drivers 0087 loadDrivers(); 0088 0089 // Shared tooltips 0090 ui->remoteDrivers->setToolTip(ui->remoteDriversLabel->toolTip()); 0091 ui->aux1Combo->setToolTip(ui->aux1Label->toolTip()); 0092 ui->aux2Combo->setToolTip(ui->aux2Label->toolTip()); 0093 ui->aux3Combo->setToolTip(ui->aux3Label->toolTip()); 0094 ui->aux4Combo->setToolTip(ui->aux4Label->toolTip()); 0095 ui->filterCombo->setToolTip(ui->filterLabel->toolTip()); 0096 ui->AOCombo->setToolTip(ui->AOLabel->toolTip()); 0097 ui->domeCombo->setToolTip(ui->domeLabel->toolTip()); 0098 ui->weatherCombo->setToolTip(ui->weatherLabel->toolTip()); 0099 ui->localMode->setToolTip(ui->modeLabel->toolTip()); 0100 ui->remoteMode->setToolTip(ui->modeLabel->toolTip()); 0101 ui->remoteHostLabel->setToolTip(ui->remoteHost->toolTip()); 0102 ui->remotePortLabel->setToolTip(ui->remotePort->toolTip()); 0103 ui->externalGuidePortLabel->setToolTip(ui->externalGuidePort->toolTip()); 0104 ui->INDIWebManagerPortLabel->setToolTip(ui->INDIWebManagerPort->toolTip()); 0105 ui->guideTypeCombo->setToolTip(ui->guidingTypeLabel->toolTip()); 0106 ui->externalGuideHostLabel->setToolTip(ui->externalGuideHost->toolTip()); 0107 } 0108 0109 void ProfileEditor::saveProfile() 0110 { 0111 bool newProfile = (pi.isNull()); 0112 0113 if (ui->profileIN->text().isEmpty()) 0114 { 0115 KSNotification::error(i18n("Cannot save an empty profile.")); 0116 return; 0117 } 0118 0119 if (newProfile) 0120 { 0121 QList<QSharedPointer<ProfileInfo>> existingProfiles; 0122 KStarsData::Instance()->userdb()->GetAllProfiles(existingProfiles); 0123 for (auto &profileInfo : existingProfiles) 0124 { 0125 if (ui->profileIN->text() == profileInfo->name) 0126 { 0127 KSNotification::error(i18n("Profile name already exists.")); 0128 return; 0129 } 0130 } 0131 int id = KStarsData::Instance()->userdb()->AddProfile(ui->profileIN->text()); 0132 pi.reset(new ProfileInfo(id, ui->profileIN->text())); 0133 } 0134 else 0135 pi->name = ui->profileIN->text(); 0136 0137 // Local Mode 0138 if (ui->localMode->isChecked()) 0139 { 0140 pi->host.clear(); 0141 pi->port = -1; 0142 pi->INDIWebManagerPort = -1; 0143 //pi->customDrivers = ui->customDriversIN->text(); 0144 } 0145 // Remote Mode 0146 else 0147 { 0148 pi->host = ui->remoteHost->text().trimmed(); 0149 pi->port = ui->remotePort->text().toInt(); 0150 if (ui->INDIWebManagerCheck->isChecked()) 0151 pi->INDIWebManagerPort = ui->INDIWebManagerPort->text().toInt(); 0152 else 0153 pi->INDIWebManagerPort = -1; 0154 //pi->customDrivers.clear(); 0155 } 0156 0157 // City Info 0158 if (ui->loadSiteCheck->isEnabled() && ui->loadSiteCheck->isChecked()) 0159 { 0160 pi->city = KStarsData::Instance()->geo()->name(); 0161 pi->province = KStarsData::Instance()->geo()->province(); 0162 pi->country = KStarsData::Instance()->geo()->country(); 0163 } 0164 else 0165 { 0166 pi->city.clear(); 0167 pi->province.clear(); 0168 pi->country.clear(); 0169 } 0170 0171 pi->indihub = m_INDIHub; 0172 0173 // Auto Connect 0174 pi->autoConnect = ui->autoConnectCheck->isChecked(); 0175 // Port Selector 0176 pi->portSelector = ui->portSelectorCheck->isChecked(); 0177 0178 // Guider Type 0179 pi->guidertype = ui->guideTypeCombo->currentIndex(); 0180 if (pi->guidertype != Ekos::Guide::GUIDE_INTERNAL) 0181 { 0182 pi->guiderhost = ui->externalGuideHost->text(); 0183 pi->guiderport = ui->externalGuidePort->text().toInt(); 0184 0185 if (pi->guidertype == Ekos::Guide::GUIDE_PHD2) 0186 { 0187 Options::setPHD2Host(pi->guiderhost); 0188 Options::setPHD2Port(pi->guiderport); 0189 } 0190 else if (pi->guidertype == Ekos::Guide::GUIDE_LINGUIDER) 0191 { 0192 Options::setLinGuiderHost(pi->guiderhost); 0193 Options::setLinGuiderPort(pi->guiderport); 0194 } 0195 } 0196 0197 if (ui->mountCombo->currentText().isEmpty() || ui->mountCombo->currentText() == "--") 0198 pi->drivers.remove("Mount"); 0199 else 0200 pi->drivers["Mount"] = ui->mountCombo->currentText(); 0201 0202 if (ui->ccdCombo->currentText().isEmpty() || ui->ccdCombo->currentText() == "--") 0203 pi->drivers.remove("CCD"); 0204 else 0205 pi->drivers["CCD"] = ui->ccdCombo->currentText(); 0206 0207 if (ui->guiderCombo->currentText().isEmpty() || ui->guiderCombo->currentText() == "--") 0208 pi->drivers.remove("Guider"); 0209 else 0210 pi->drivers["Guider"] = ui->guiderCombo->currentText(); 0211 0212 if (ui->focuserCombo->currentText().isEmpty() || ui->focuserCombo->currentText() == "--") 0213 pi->drivers.remove("Focuser"); 0214 else 0215 pi->drivers["Focuser"] = ui->focuserCombo->currentText(); 0216 0217 if (ui->filterCombo->currentText().isEmpty() || ui->filterCombo->currentText() == "--") 0218 pi->drivers.remove("Filter"); 0219 else 0220 pi->drivers["Filter"] = ui->filterCombo->currentText(); 0221 0222 if (ui->AOCombo->currentText().isEmpty() || ui->AOCombo->currentText() == "--") 0223 pi->drivers.remove("AO"); 0224 else 0225 pi->drivers["AO"] = ui->AOCombo->currentText(); 0226 0227 if (ui->domeCombo->currentText().isEmpty() || ui->domeCombo->currentText() == "--") 0228 pi->drivers.remove("Dome"); 0229 else 0230 pi->drivers["Dome"] = ui->domeCombo->currentText(); 0231 0232 if (ui->weatherCombo->currentText().isEmpty() || ui->weatherCombo->currentText() == "--") 0233 pi->drivers.remove("Weather"); 0234 else 0235 pi->drivers["Weather"] = ui->weatherCombo->currentText(); 0236 0237 if (ui->aux1Combo->currentText().isEmpty() || ui->aux1Combo->currentText() == "--") 0238 pi->drivers.remove("Aux1"); 0239 else 0240 pi->drivers["Aux1"] = ui->aux1Combo->currentText(); 0241 0242 if (ui->aux2Combo->currentText().isEmpty() || ui->aux2Combo->currentText() == "--") 0243 pi->drivers.remove("Aux2"); 0244 else 0245 pi->drivers["Aux2"] = ui->aux2Combo->currentText(); 0246 0247 if (ui->aux3Combo->currentText().isEmpty() || ui->aux3Combo->currentText() == "--") 0248 pi->drivers.remove("Aux3"); 0249 else 0250 pi->drivers["Aux3"] = ui->aux3Combo->currentText(); 0251 0252 if (ui->aux4Combo->currentText().isEmpty() || ui->aux4Combo->currentText() == "--") 0253 pi->drivers.remove("Aux4"); 0254 else 0255 pi->drivers["Aux4"] = ui->aux4Combo->currentText(); 0256 0257 pi->remotedrivers = ui->remoteDrivers->text(); 0258 0259 KStarsData::Instance()->userdb()->SaveProfile(pi); 0260 0261 // Ekos manager will reload and new profiles will be created 0262 if (newProfile) 0263 pi.clear(); 0264 0265 accept(); 0266 } 0267 0268 void ProfileEditor::setRemoteMode(bool enable) 0269 { 0270 loadDrivers(); //This is needed to reload the drivers because some may not be available locally 0271 0272 ui->remoteHost->setEnabled(enable); 0273 ui->remoteHostLabel->setEnabled(enable); 0274 ui->remotePort->setEnabled(enable); 0275 ui->remotePortLabel->setEnabled(enable); 0276 0277 //ui->customLabel->setEnabled(!enable); 0278 //ui->customDriversIN->setEnabled(!enable); 0279 0280 ui->mountCombo->setEditable(enable); 0281 ui->ccdCombo->setEditable(enable); 0282 ui->guiderCombo->setEditable(enable); 0283 ui->focuserCombo->setEditable(enable); 0284 ui->filterCombo->setEditable(enable); 0285 ui->AOCombo->setEditable(enable); 0286 ui->domeCombo->setEditable(enable); 0287 ui->weatherCombo->setEditable(enable); 0288 ui->aux1Combo->setEditable(enable); 0289 ui->aux2Combo->setEditable(enable); 0290 ui->aux3Combo->setEditable(enable); 0291 ui->aux4Combo->setEditable(enable); 0292 0293 ui->remoteDrivers->setEnabled(!enable); 0294 0295 ui->loadSiteCheck->setEnabled(enable); 0296 0297 ui->INDIWebManagerCheck->setEnabled(enable); 0298 if (enable == false) 0299 ui->INDIWebManagerCheck->setChecked(false); 0300 ui->INDIWebManagerPort->setEnabled(enable); 0301 0302 ui->scriptsB->setEnabled(!enable || ui->INDIWebManagerCheck->isChecked()); 0303 } 0304 0305 void ProfileEditor::setPi(const QSharedPointer<ProfileInfo> &newProfile) 0306 { 0307 pi = newProfile; 0308 0309 ui->profileIN->setText(pi->name); 0310 0311 ui->loadSiteCheck->setChecked(!pi->city.isEmpty()); 0312 ui->autoConnectCheck->setChecked(pi->autoConnect); 0313 ui->portSelectorCheck->setChecked(pi->portSelector); 0314 0315 if (pi->city.isEmpty() == false) 0316 { 0317 if (pi->province.isEmpty()) 0318 ui->loadSiteCheck->setText(ui->loadSiteCheck->text() + QString(" (%1, %2)").arg(pi->country, pi->city)); 0319 else 0320 ui->loadSiteCheck->setText(ui->loadSiteCheck->text() + 0321 QString(" (%1, %2, %3)").arg(pi->country, pi->province, pi->city)); 0322 } 0323 0324 if (pi->host.isEmpty() == false) 0325 { 0326 ui->remoteHost->setText(pi->host); 0327 ui->remotePort->setText(QString::number(pi->port)); 0328 0329 ui->remoteMode->setChecked(true); 0330 0331 if (pi->INDIWebManagerPort > 0) 0332 { 0333 ui->INDIWebManagerCheck->setChecked(true); 0334 ui->INDIWebManagerPort->setText(QString::number(pi->INDIWebManagerPort)); 0335 } 0336 else 0337 { 0338 ui->INDIWebManagerCheck->setChecked(false); 0339 ui->INDIWebManagerPort->setText("8624"); 0340 } 0341 } 0342 0343 if (pi->remotedrivers.isEmpty() == false) 0344 ui->remoteDrivers->setText(pi->remotedrivers); 0345 0346 ui->guideTypeCombo->setCurrentIndex(pi->guidertype); 0347 updateGuiderSelection(ui->guideTypeCombo->currentIndex()); 0348 if (pi->guidertype == Ekos::Guide::GUIDE_PHD2) 0349 { 0350 Options::setPHD2Host(pi->guiderhost); 0351 Options::setPHD2Port(pi->guiderport); 0352 } 0353 else if (pi->guidertype == Ekos::Guide::GUIDE_LINGUIDER) 0354 { 0355 Options::setLinGuiderHost(pi->guiderhost); 0356 Options::setLinGuiderPort(pi->guiderport); 0357 } 0358 0359 QMapIterator<QString, QString> i(pi->drivers); 0360 0361 while (i.hasNext()) 0362 { 0363 int row = 0; 0364 i.next(); 0365 0366 QString key = i.key(); 0367 QString value = i.value(); 0368 0369 if (key == "Mount") 0370 { 0371 // If driver doesn't exist, let's add it to the list 0372 if ((row = ui->mountCombo->findText(value)) == -1) 0373 { 0374 ui->mountCombo->addItem(value); 0375 row = ui->mountCombo->count() - 1; 0376 } 0377 0378 // Set index to our driver 0379 ui->mountCombo->setCurrentIndex(row); 0380 } 0381 else if (key == "CCD") 0382 { 0383 if ((row = ui->ccdCombo->findText(value)) == -1) 0384 { 0385 ui->ccdCombo->addItem(value); 0386 row = ui->ccdCombo->count() - 1; 0387 } 0388 0389 ui->ccdCombo->setCurrentIndex(row); 0390 } 0391 else if (key == "Guider") 0392 { 0393 if ((row = ui->guiderCombo->findText(value)) == -1) 0394 { 0395 ui->guiderCombo->addItem(value); 0396 row = ui->guiderCombo->count() - 1; 0397 } 0398 0399 ui->guiderCombo->setCurrentIndex(row); 0400 } 0401 else if (key == "Focuser") 0402 { 0403 if ((row = ui->focuserCombo->findText(value)) == -1) 0404 { 0405 ui->focuserCombo->addItem(value); 0406 row = ui->focuserCombo->count() - 1; 0407 } 0408 0409 ui->focuserCombo->setCurrentIndex(row); 0410 } 0411 else if (key == "Filter") 0412 { 0413 if ((row = ui->filterCombo->findText(value)) == -1) 0414 { 0415 ui->filterCombo->addItem(value); 0416 row = ui->filterCombo->count() - 1; 0417 } 0418 0419 ui->filterCombo->setCurrentIndex(row); 0420 } 0421 else if (key == "AO") 0422 { 0423 if ((row = ui->AOCombo->findText(value)) == -1) 0424 { 0425 ui->AOCombo->addItem(value); 0426 row = ui->AOCombo->count() - 1; 0427 } 0428 0429 ui->AOCombo->setCurrentIndex(row); 0430 } 0431 else if (key == "Dome") 0432 { 0433 if ((row = ui->domeCombo->findText(value)) == -1) 0434 { 0435 ui->domeCombo->addItem(value); 0436 row = ui->domeCombo->count() - 1; 0437 } 0438 0439 ui->domeCombo->setCurrentIndex(row); 0440 } 0441 else if (key == "Weather") 0442 { 0443 if ((row = ui->weatherCombo->findText(value)) == -1) 0444 { 0445 ui->weatherCombo->addItem(value); 0446 row = ui->weatherCombo->count() - 1; 0447 } 0448 0449 ui->weatherCombo->setCurrentIndex(row); 0450 } 0451 else if (key == "Aux1") 0452 { 0453 if ((row = ui->aux1Combo->findText(value)) == -1) 0454 { 0455 ui->aux1Combo->addItem(value); 0456 row = ui->aux1Combo->count() - 1; 0457 } 0458 0459 ui->aux1Combo->setCurrentIndex(row); 0460 } 0461 else if (key == "Aux2") 0462 { 0463 if ((row = ui->aux2Combo->findText(value)) == -1) 0464 { 0465 ui->aux2Combo->addItem(value); 0466 row = ui->aux2Combo->count() - 1; 0467 } 0468 0469 ui->aux2Combo->setCurrentIndex(row); 0470 } 0471 else if (key == "Aux3") 0472 { 0473 if ((row = ui->aux3Combo->findText(value)) == -1) 0474 { 0475 ui->aux3Combo->addItem(value); 0476 row = ui->aux3Combo->count() - 1; 0477 } 0478 0479 ui->aux3Combo->setCurrentIndex(row); 0480 } 0481 else if (key == "Aux4") 0482 { 0483 if ((row = ui->aux4Combo->findText(value)) == -1) 0484 { 0485 ui->aux4Combo->addItem(value); 0486 row = ui->aux4Combo->count() - 1; 0487 } 0488 0489 ui->aux4Combo->setCurrentIndex(row); 0490 } 0491 } 0492 0493 m_INDIHub = pi->indihub; 0494 } 0495 0496 QString ProfileEditor::getTooltip(const QSharedPointer<DriverInfo> &driver) 0497 { 0498 bool locallyAvailable = false; 0499 if (driver->getAuxInfo().contains("LOCALLY_AVAILABLE")) 0500 locallyAvailable = driver->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool(); 0501 QString toolTipText; 0502 if (!locallyAvailable) 0503 toolTipText = i18n( 0504 "<nobr>Available as <b>Remote</b> Driver. To use locally, install the corresponding driver.<nobr/>"); 0505 else 0506 toolTipText = i18n("<nobr><b>Label</b>: %1 ━ <b>Driver</b>: %2 ━ <b>Exec</b>: %3<nobr/>", 0507 driver->getLabel(), driver->getName(), driver->getExecutable()); 0508 0509 return toolTipText; 0510 } 0511 0512 void ProfileEditor::loadDrivers() 0513 { 0514 // We need to save this now since we have two models for the mounts 0515 QString selectedMount = ui->mountCombo->currentText(); 0516 QString selectedCamera = ui->ccdCombo->currentText(); 0517 QString selectedGuider = ui->guiderCombo->currentText(); 0518 QString selectedFocuser = ui->focuserCombo->currentText(); 0519 QString selectedAux1 = ui->aux1Combo->currentText(); 0520 QString selectedAux2 = ui->aux2Combo->currentText(); 0521 QString selectedAux3 = ui->aux3Combo->currentText(); 0522 QString selectedAux4 = ui->aux4Combo->currentText(); 0523 0524 QVector<QComboBox *> boxes; 0525 boxes.append(ui->mountCombo); 0526 boxes.append(ui->ccdCombo); 0527 boxes.append(ui->guiderCombo); 0528 boxes.append(ui->AOCombo); 0529 boxes.append(ui->focuserCombo); 0530 boxes.append(ui->filterCombo); 0531 boxes.append(ui->domeCombo); 0532 boxes.append(ui->weatherCombo); 0533 boxes.append(ui->aux1Combo); 0534 boxes.append(ui->aux2Combo); 0535 boxes.append(ui->aux3Combo); 0536 boxes.append(ui->aux4Combo); 0537 0538 QVector<QString> selectedItems; 0539 0540 for (QComboBox *box : boxes) 0541 { 0542 selectedItems.append(box->currentText()); 0543 box->clear(); 0544 box->addItem("--"); 0545 box->setMaxVisibleItems(20); 0546 } 0547 0548 QIcon remoteIcon = QIcon::fromTheme("network-modem"); 0549 0550 // Create the model 0551 delete (m_MountModel); 0552 m_MountModel = new QStandardItemModel(this); 0553 delete (m_CameraModel); 0554 m_CameraModel = new QStandardItemModel(this); 0555 delete (m_GuiderModel); 0556 m_GuiderModel = new QStandardItemModel(this); 0557 delete (m_FocuserModel); 0558 m_FocuserModel = new QStandardItemModel(this); 0559 delete (m_Aux1Model); 0560 m_Aux1Model = new QStandardItemModel(this); 0561 delete (m_Aux2Model); 0562 m_Aux2Model = new QStandardItemModel(this); 0563 delete (m_Aux3Model); 0564 m_Aux3Model = new QStandardItemModel(this); 0565 delete (m_Aux4Model); 0566 m_Aux4Model = new QStandardItemModel(this); 0567 0568 const bool isLocal = ui->localMode->isChecked(); 0569 const QList<DeviceFamily> auxFamily = QList<DeviceFamily>() 0570 << KSTARS_AUXILIARY 0571 << KSTARS_CCD 0572 << KSTARS_FOCUSER 0573 << KSTARS_FILTER 0574 << KSTARS_WEATHER 0575 << KSTARS_SPECTROGRAPHS 0576 << KSTARS_DETECTORS; 0577 0578 populateManufacturerCombo(m_MountModel, ui->mountCombo, selectedMount, isLocal, QList<DeviceFamily>() << KSTARS_TELESCOPE); 0579 populateManufacturerCombo(m_CameraModel, ui->ccdCombo, selectedCamera, isLocal, QList<DeviceFamily>() << KSTARS_CCD); 0580 populateManufacturerCombo(m_GuiderModel, ui->guiderCombo, selectedGuider, isLocal, QList<DeviceFamily>() << KSTARS_CCD); 0581 populateManufacturerCombo(m_FocuserModel, ui->focuserCombo, selectedFocuser, isLocal, 0582 QList<DeviceFamily>() << KSTARS_FOCUSER); 0583 populateManufacturerCombo(m_Aux1Model, ui->aux1Combo, selectedAux1, isLocal, auxFamily); 0584 populateManufacturerCombo(m_Aux2Model, ui->aux2Combo, selectedAux2, isLocal, auxFamily); 0585 populateManufacturerCombo(m_Aux3Model, ui->aux3Combo, selectedAux3, isLocal, auxFamily); 0586 populateManufacturerCombo(m_Aux4Model, ui->aux4Combo, selectedAux4, isLocal, auxFamily); 0587 0588 for (QSharedPointer<DriverInfo>driver : DriverManager::Instance()->getDrivers()) 0589 { 0590 bool locallyAvailable = false; 0591 QIcon icon; 0592 if (driver->getAuxInfo().contains("LOCALLY_AVAILABLE")) 0593 locallyAvailable = driver->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool(); 0594 if (!locallyAvailable) 0595 { 0596 if (ui->localMode->isChecked()) 0597 continue; 0598 else 0599 icon = remoteIcon; 0600 } 0601 0602 QString toolTipText = getTooltip(driver); 0603 0604 switch (driver->getType()) 0605 { 0606 case KSTARS_CCD: 0607 break; 0608 0609 case KSTARS_ADAPTIVE_OPTICS: 0610 { 0611 ui->AOCombo->addItem(icon, driver->getLabel()); 0612 ui->AOCombo->setItemData(ui->AOCombo->count() - 1, toolTipText, Qt::ToolTipRole); 0613 } 0614 break; 0615 0616 case KSTARS_FOCUSER: 0617 break; 0618 0619 case KSTARS_FILTER: 0620 { 0621 ui->filterCombo->addItem(icon, driver->getLabel()); 0622 ui->filterCombo->setItemData(ui->filterCombo->count() - 1, toolTipText, Qt::ToolTipRole); 0623 } 0624 break; 0625 0626 case KSTARS_DOME: 0627 { 0628 ui->domeCombo->addItem(icon, driver->getLabel()); 0629 ui->domeCombo->setItemData(ui->domeCombo->count() - 1, toolTipText, Qt::ToolTipRole); 0630 } 0631 break; 0632 0633 case KSTARS_WEATHER: 0634 { 0635 ui->weatherCombo->addItem(icon, driver->getLabel()); 0636 ui->weatherCombo->setItemData(ui->weatherCombo->count() - 1, toolTipText, Qt::ToolTipRole); 0637 } 0638 break; 0639 0640 case KSTARS_AUXILIARY: 0641 case KSTARS_SPECTROGRAPHS: 0642 case KSTARS_DETECTORS: 0643 break; 0644 0645 default: 0646 continue; 0647 } 0648 } 0649 0650 // Skip mount/ccd/guider/focuser since we handled it above 0651 for (int i = 4; i < boxes.count(); i++) 0652 { 0653 QComboBox *box = boxes.at(i); 0654 QString selectedItemText = selectedItems.at(i); 0655 int index = box->findText(selectedItemText); 0656 if (index == -1) 0657 { 0658 if (ui->localMode->isChecked()) 0659 box->setCurrentIndex(0); 0660 else 0661 box->addItem(remoteIcon, selectedItemText); 0662 } 0663 else 0664 { 0665 box->setCurrentIndex(index); 0666 } 0667 0668 box->model()->sort(0); 0669 } 0670 } 0671 0672 void ProfileEditor::setProfileName(const QString &name) 0673 { 0674 ui->profileIN->setText(name); 0675 } 0676 0677 void ProfileEditor::setAuxDrivers(const QStringList &aux) 0678 { 0679 QStringList auxList(aux); 0680 0681 if (auxList.isEmpty()) 0682 return; 0683 ui->aux1Combo->setCurrentText(auxList.first()); 0684 auxList.removeFirst(); 0685 0686 if (auxList.isEmpty()) 0687 return; 0688 ui->aux2Combo->setCurrentText(auxList.first()); 0689 auxList.removeFirst(); 0690 0691 if (auxList.isEmpty()) 0692 return; 0693 ui->aux3Combo->setCurrentText(auxList.first()); 0694 auxList.removeFirst(); 0695 0696 if (auxList.isEmpty()) 0697 return; 0698 ui->aux4Combo->setCurrentText(auxList.first()); 0699 } 0700 0701 void ProfileEditor::setHostPort(const QString &host, const QString &port) 0702 { 0703 ui->remoteMode->setChecked(true); 0704 ui->remoteHost->setText(host); 0705 ui->remotePort->setText(port); 0706 } 0707 0708 void ProfileEditor::setWebManager(bool enabled, const QString &port) 0709 { 0710 ui->INDIWebManagerCheck->setChecked(enabled); 0711 ui->INDIWebManagerPort->setText(port); 0712 } 0713 0714 void ProfileEditor::setGuiderType(int type) 0715 { 0716 ui->guideTypeCombo->setCurrentIndex(type); 0717 if (type != Ekos::Guide::GUIDE_INTERNAL) 0718 { 0719 ui->externalGuideHostLabel->setEnabled(true); 0720 ui->externalGuideHost->setEnabled(true); 0721 ui->externalGuidePortLabel->setEnabled(true); 0722 ui->externalGuidePort->setEnabled(true); 0723 } 0724 } 0725 0726 void ProfileEditor::setConnectionOptionsEnabled(bool enable) 0727 { 0728 // Enable or disable connection related options 0729 ui->modeLabel->setEnabled(enable); 0730 ui->localMode->setEnabled(enable); 0731 ui->remoteMode->setEnabled(enable); 0732 ui->remoteHostLabel->setEnabled(enable); 0733 ui->remoteHost->setEnabled(enable); 0734 ui->remotePortLabel->setEnabled(enable); 0735 ui->remotePort->setEnabled(enable); 0736 ui->INDIWebManagerCheck->setEnabled(enable); 0737 ui->INDIWebManagerPort->setEnabled(enable); 0738 ui->INDIWebManagerPortLabel->setEnabled(enable); 0739 ui->guidingTypeLabel->setEnabled(enable); 0740 ui->guideTypeCombo->setEnabled(enable); 0741 ui->remoteDrivers->setEnabled(enable); 0742 0743 updateGuiderSelection(ui->guideTypeCombo->currentIndex()); 0744 0745 if (enable == false) 0746 ui->mountCombo->setFocus(); 0747 } 0748 0749 void ProfileEditor::updateGuiderSelection(int id) 0750 { 0751 0752 if (id == Ekos::Guide::GUIDE_INTERNAL) 0753 { 0754 ui->externalGuideHost->setText("localhost"); 0755 ui->externalGuidePort->clear(); 0756 0757 ui->externalGuideHost->setEnabled(false); 0758 ui->externalGuideHostLabel->setEnabled(false); 0759 ui->externalGuidePort->setEnabled(false); 0760 ui->externalGuidePortLabel->setEnabled(false); 0761 return; 0762 } 0763 0764 QString host; 0765 int port = -1; 0766 0767 ui->externalGuideHost->setEnabled(true); 0768 ui->externalGuideHostLabel->setEnabled(true); 0769 ui->externalGuidePort->setEnabled(true); 0770 ui->externalGuidePortLabel->setEnabled(true); 0771 0772 if (pi && pi->guidertype == id) 0773 { 0774 host = pi->guiderhost; 0775 port = pi->guiderport; 0776 } 0777 0778 if (id == Ekos::Guide::GUIDE_PHD2) 0779 { 0780 if (host.isEmpty()) 0781 host = Options::pHD2Host(); 0782 if (port < 0) 0783 port = Options::pHD2Port(); 0784 } 0785 else if (id == Ekos::Guide::GUIDE_LINGUIDER) 0786 { 0787 if (host.isEmpty()) 0788 host = Options::linGuiderHost(); 0789 if (port < 0) 0790 port = Options::linGuiderPort(); 0791 } 0792 0793 ui->externalGuideHost->setText(host); 0794 ui->externalGuidePort->setText(QString::number(port)); 0795 0796 } 0797 0798 void ProfileEditor::setSettings(const QJsonObject &profile) 0799 { 0800 ui->profileIN->setText(profile["name"].toString()); 0801 ui->autoConnectCheck->setChecked(profile["auto_connect"].toBool(true)); 0802 ui->portSelectorCheck->setChecked(profile["port_selector"].toBool(false)); 0803 ui->localMode->setChecked(profile["mode"].toString() == "local"); 0804 ui->remoteMode->setChecked(profile["mode"].toString() == "remote"); 0805 ui->remoteHost->setText(profile["remote_host"].toString("localhost")); 0806 ui->remotePort->setText(profile["remote_port"].toString("7624")); 0807 ui->guideTypeCombo->setCurrentText(profile["guiding"].toString(i18n("Internal"))); 0808 ui->externalGuideHost->setText(profile["remote_guiding_host"].toString(("localhost"))); 0809 ui->externalGuideHost->setText(profile["remote_guiding_port"].toString()); 0810 ui->INDIWebManagerCheck->setChecked(profile["use_web_manager"].toBool()); 0811 ui->remoteDrivers->setText(profile["remote"].toString(ui->remoteDrivers->text())); 0812 0813 m_INDIHub = profile["indihub"].toInt(m_INDIHub); 0814 0815 // Drivers 0816 const QString mount = profile["mount"].toString("--"); 0817 if (mount == "--") 0818 ui->mountCombo->setCurrentIndex(0); 0819 else 0820 { 0821 ui->mountCombo->addItem(mount); 0822 ui->mountCombo->setCurrentIndex(ui->mountCombo->count() - 1); 0823 } 0824 0825 const QString ccd = profile["ccd"].toString("--"); 0826 if (ccd == "--") 0827 ui->ccdCombo->setCurrentIndex(0); 0828 else 0829 { 0830 ui->ccdCombo->addItem(ccd); 0831 ui->ccdCombo->setCurrentIndex(ui->ccdCombo->count() - 1); 0832 } 0833 0834 const QString guider = profile["guider"].toString("--"); 0835 if (guider == "--") 0836 ui->guiderCombo->setCurrentIndex(0); 0837 else 0838 { 0839 ui->guiderCombo->addItem(guider); 0840 ui->guiderCombo->setCurrentIndex(ui->guiderCombo->count() - 1); 0841 } 0842 0843 const QString focuser = profile["focuser"].toString("--"); 0844 if (focuser == "--") 0845 ui->focuserCombo->setCurrentIndex(0); 0846 else 0847 { 0848 ui->focuserCombo->addItem(focuser); 0849 ui->focuserCombo->setCurrentIndex(ui->focuserCombo->count() - 1); 0850 } 0851 0852 ui->filterCombo->setCurrentText(profile["filter"].toString("--")); 0853 ui->AOCombo->setCurrentText(profile["ao"].toString("--")); 0854 ui->domeCombo->setCurrentText(profile["dome"].toString("--")); 0855 ui->weatherCombo->setCurrentText(profile["weather"].toString("--")); 0856 0857 const auto aux1 = profile["aux1"].toString("--"); 0858 if (aux1.isEmpty() || aux1 == "--") 0859 ui->aux1Combo->setCurrentIndex(0); 0860 else 0861 { 0862 ui->aux1Combo->addItem(aux1); 0863 ui->aux1Combo->setCurrentIndex(ui->aux1Combo->count() - 1); 0864 } 0865 0866 const auto aux2 = profile["aux2"].toString("--"); 0867 if (aux2.isEmpty() || aux2 == "--") 0868 ui->aux2Combo->setCurrentIndex(0); 0869 else 0870 { 0871 ui->aux2Combo->addItem(aux2); 0872 ui->aux2Combo->setCurrentIndex(ui->aux2Combo->count() - 1); 0873 } 0874 0875 const auto aux3 = profile["aux3"].toString("--"); 0876 if (aux3.isEmpty() || aux3 == "--") 0877 ui->aux3Combo->setCurrentIndex(0); 0878 else 0879 { 0880 ui->aux3Combo->addItem(aux3); 0881 ui->aux3Combo->setCurrentIndex(ui->aux3Combo->count() - 1); 0882 } 0883 0884 const auto aux4 = profile["aux4"].toString("--"); 0885 if (aux4.isEmpty() || aux4 == "--") 0886 ui->aux4Combo->setCurrentIndex(0); 0887 else 0888 { 0889 ui->aux4Combo->addItem(aux4); 0890 ui->aux4Combo->setCurrentIndex(ui->aux4Combo->count() - 1); 0891 } 0892 } 0893 0894 void ProfileEditor::scanNetwork() 0895 { 0896 delete (m_ProgressDialog); 0897 m_ProgressDialog = new QProgressDialog(this); 0898 m_ProgressDialog->setWindowTitle(i18nc("@title:window", "Scanning Network")); 0899 m_ProgressDialog->setLabelText(i18n("Scanning network for INDI Web Managers...")); 0900 connect(m_ProgressDialog, &QProgressDialog::canceled, this, [this]() 0901 { 0902 m_CancelScan = true; 0903 clearAllRequests(); 0904 }); 0905 m_ProgressDialog->setMinimum(0); 0906 m_ProgressDialog->setMaximum(0); 0907 m_ProgressDialog->show(); 0908 m_ProgressDialog->raise(); 0909 0910 m_CancelScan = false; 0911 0912 QList<QHostAddress> addresses = QNetworkInterface::allAddresses(); 0913 std::sort(addresses.begin(), addresses.end(), [](const QHostAddress & a, const QHostAddress & b) -> bool 0914 { return a.toString() < b.toString();}); 0915 0916 for(QHostAddress address : addresses) 0917 { 0918 if (address.isLoopback() || address.protocol() & QAbstractSocket::IPv6Protocol) 0919 continue; 0920 0921 QString ipv4 = address.toString(); 0922 0923 if (ipv4.startsWith("10.250")) 0924 { 0925 scanIP("10.250.250.1"); 0926 } 0927 else 0928 { 0929 QString prefixIP = ipv4.remove(ipv4.lastIndexOf("."), 10); 0930 // Blind search all over subnet 0931 // TODO better subnet detection instead of assuming it finishes at 254 0932 for (int i = 1; i <= 254; i++) 0933 { 0934 scanIP(prefixIP + "." + QString::number(i)); 0935 } 0936 } 0937 } 0938 0939 } 0940 0941 void ProfileEditor::scanIP(const QString &ip) 0942 { 0943 QUrl url(QString("http://%1:8624/api/server/status").arg(ip)); 0944 0945 qCDebug(KSTARS_EKOS) << "Scanning" << url; 0946 0947 QNetworkReply *response = m_Manager.get(QNetworkRequest(url)); 0948 m_Replies.append(response); 0949 connect(response, &QNetworkReply::finished, [this, response, ip]() 0950 { 0951 m_Replies.removeOne(response); 0952 response->deleteLater(); 0953 if (m_CancelScan) 0954 return; 0955 if (response->error() == QNetworkReply::NoError) 0956 { 0957 clearAllRequests(); 0958 m_ProgressDialog->close(); 0959 ui->remoteHost->setText(ip); 0960 0961 qCDebug(KSTARS_EKOS) << "Found Web Manager server at" << ip; 0962 0963 KSNotification::info(i18n("Found INDI Web Manager at %1", ip)); 0964 } 0965 }); 0966 } 0967 0968 void ProfileEditor::clearAllRequests() 0969 { 0970 for (QNetworkReply *oneReply : m_Replies) 0971 { 0972 oneReply->abort(); 0973 oneReply->deleteLater(); 0974 } 0975 0976 m_Replies.clear(); 0977 } 0978 0979 void ProfileEditor::showINDIHub() 0980 { 0981 QDialog hub; 0982 Ui::INDIHub indihub; 0983 indihub.setupUi(&hub); 0984 0985 indihub.modeButtonGroup->setId(indihub.offR, 0); 0986 indihub.modeButtonGroup->setId(indihub.solorR, 1); 0987 indihub.modeButtonGroup->setId(indihub.shareR, 2); 0988 indihub.modeButtonGroup->setId(indihub.roboticR, 3); 0989 0990 indihub.logoLabel->setPixmap(QIcon(":/icons/indihub_logo.svg").pixmap(QSize(128, 128))); 0991 0992 indihub.modeButtonGroup->button(m_INDIHub)->setChecked(true); 0993 connect(indihub.closeB, &QPushButton::clicked, &hub, &QDialog::close); 0994 0995 hub.exec(); 0996 0997 m_INDIHub = indihub.modeButtonGroup->checkedId(); 0998 } 0999 1000 void ProfileEditor::populateManufacturerCombo(QStandardItemModel *model, QComboBox *combo, const QString &selectedDriver, 1001 bool isLocal, const QList<DeviceFamily> &families) 1002 { 1003 if (isLocal) 1004 { 1005 QStandardItem *selectedItem = nullptr; 1006 model->appendRow(new QStandardItem("--")); 1007 for (QSharedPointer<DriverInfo>driver : DriverManager::Instance()->getDrivers()) 1008 { 1009 if (!families.contains(driver->getType())) 1010 continue; 1011 1012 QString manufacturer = driver->manufacturer(); 1013 QList<QStandardItem*> manufacturers = model->findItems(manufacturer); 1014 1015 QStandardItem *parentItem = nullptr; 1016 if (model->findItems(manufacturer).empty()) 1017 { 1018 parentItem = new QStandardItem(manufacturer); 1019 parentItem->setSelectable(false); 1020 model->appendRow(parentItem); 1021 } 1022 else 1023 { 1024 parentItem = manufacturers.first(); 1025 } 1026 1027 QStandardItem *item = new QStandardItem(driver->getLabel()); 1028 item->setData(getTooltip(driver), Qt::ToolTipRole); 1029 parentItem->appendRow(item); 1030 if (selectedDriver == driver->getLabel()) 1031 selectedItem = item; 1032 } 1033 QTreeView *view = new QTreeView(this); 1034 view->setModel(model); 1035 view->sortByColumn(0, Qt::AscendingOrder); 1036 combo->setView(view); 1037 combo->setModel(model); 1038 if (selectedItem) 1039 { 1040 // JM: Only way to make it the QTreeView sets the current index 1041 // in the combo way 1042 1043 QModelIndex index = model->indexFromItem(selectedItem); 1044 1045 // First set current index to the child 1046 combo->setRootModelIndex(index.parent()); 1047 combo->setModelColumn(index.column()); 1048 combo->setCurrentIndex(index.row()); 1049 1050 // Now reset 1051 combo->setRootModelIndex(QModelIndex()); 1052 view->setCurrentIndex(index); 1053 } 1054 } 1055 else 1056 { 1057 QIcon remoteIcon = QIcon::fromTheme("network-modem"); 1058 combo->setView(new QListView(this)); 1059 model->appendRow(new QStandardItem("--")); 1060 QIcon icon; 1061 for (QSharedPointer<DriverInfo>driver : DriverManager::Instance()->getDrivers()) 1062 { 1063 if (!families.contains(driver->getType())) 1064 continue; 1065 1066 bool locallyAvailable = false; 1067 if (driver->getAuxInfo().contains("LOCALLY_AVAILABLE")) 1068 locallyAvailable = driver->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool(); 1069 icon = locallyAvailable ? QIcon() : remoteIcon; 1070 1071 QStandardItem *mount = new QStandardItem(icon, driver->getLabel()); 1072 mount->setData(getTooltip(driver), Qt::ToolTipRole); 1073 model->appendRow(mount); 1074 } 1075 combo->setModel(model); 1076 combo->setCurrentText(selectedDriver); 1077 } 1078 } 1079 1080 void ProfileEditor::executeScriptEditor() 1081 { 1082 if (pi == nullptr) 1083 return; 1084 QStringList currentDrivers; 1085 for (auto &oneCombo : ui->driversGroupBox->findChildren<QComboBox *>()) 1086 currentDrivers << oneCombo->currentText(); 1087 currentDrivers.removeAll("--"); 1088 currentDrivers.removeAll(""); 1089 currentDrivers.sort(); 1090 ProfileScriptDialog dialog(currentDrivers, pi->scripts, this); 1091 dialog.exec(); 1092 auto settings = dialog.jsonSettings(); 1093 pi->scripts = QJsonDocument(settings).toJson(QJsonDocument::Compact); 1094 }