File indexing completed on 2024-04-28 11:24:09
0001 /* 0002 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikartech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "manager.h" 0008 0009 #include "analyze/analyze.h" 0010 #include "capture/capture.h" 0011 #include "scheduler/scheduler.h" 0012 #include "focus/focus.h" 0013 #include "align/align.h" 0014 #include "guide/guide.h" 0015 #include "mount/mount.h" 0016 #include "observatory/observatory.h" 0017 0018 #include "opsekos.h" 0019 #include "ekosadaptor.h" 0020 #include "kstars.h" 0021 #include "kstarsdata.h" 0022 #include "Options.h" 0023 #include "ekos/capture/rotatorsettings.h" 0024 #include "profileeditor.h" 0025 #include "profilewizard.h" 0026 #include "indihub.h" 0027 #include "auxiliary/darklibrary.h" 0028 #include "auxiliary/ksmessagebox.h" 0029 #include "auxiliary/profilesettings.h" 0030 #include "capture/sequencejob.h" 0031 #include "capture/captureprocess.h" 0032 #include "fitsviewer/fitsview.h" 0033 #include "fitsviewer/fitsdata.h" 0034 #include "indi/clientmanager.h" 0035 #include "indi/driverinfo.h" 0036 #include "indi/drivermanager.h" 0037 #include "indi/guimanager.h" 0038 #include "indi/indilistener.h" 0039 #include "auxiliary/opticaltrainmanager.h" 0040 #include "auxiliary/opticaltrainsettings.h" 0041 #include "indi/indiwebmanager.h" 0042 #include "indi/indigps.h" 0043 #include "indi/indiguider.h" 0044 #include "indi/indirotator.h" 0045 #include "mount/meridianflipstatuswidget.h" 0046 #include "ekos/auxiliary/rotatorutils.h" 0047 0048 #include "ekoslive/ekosliveclient.h" 0049 #include "ekoslive/message.h" 0050 #include "ekoslive/media.h" 0051 0052 #include <basedevice.h> 0053 0054 #include <KConfigDialog> 0055 #include <KMessageBox> 0056 #include <KActionCollection> 0057 #include <KNotifications/KNotification> 0058 0059 #include <QFutureWatcher> 0060 #include <QComboBox> 0061 0062 #include <ekos_debug.h> 0063 0064 #define MAX_REMOTE_INDI_TIMEOUT 15000 0065 #define MAX_LOCAL_INDI_TIMEOUT 10000 0066 0067 namespace Ekos 0068 { 0069 0070 Manager *Manager::_Manager = nullptr; 0071 0072 Manager *Manager::Instance() 0073 { 0074 if (_Manager == nullptr) 0075 _Manager = new Manager(Options::independentWindowEkos() ? nullptr : KStars::Instance()); 0076 0077 return _Manager; 0078 } 0079 0080 void Manager::release() 0081 { 0082 ProfileSettings::release(); 0083 OpticalTrainManager::release(); 0084 OpticalTrainSettings::release(); 0085 RotatorUtils::release(); 0086 delete _Manager; 0087 } 0088 0089 Manager::Manager(QWidget * parent) : QDialog(parent) 0090 { 0091 #ifdef Q_OS_OSX 0092 0093 if (Options::independentWindowEkos()) 0094 setWindowFlags(Qt::Window); 0095 else 0096 { 0097 setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); 0098 connect(QApplication::instance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, 0099 SLOT(changeAlwaysOnTop(Qt::ApplicationState))); 0100 } 0101 #else 0102 if (Options::independentWindowEkos()) 0103 //setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); 0104 setWindowFlags(Qt::Window); 0105 #endif 0106 setupUi(this); 0107 // do not show empty targets 0108 capturePreview->targetLabel->setVisible(false); 0109 capturePreview->mountTarget->setVisible(false); 0110 0111 // position the vertical splitter by 2/3 0112 deviceSplitter->setSizes(QList<int>({20000, 10000})); 0113 0114 qRegisterMetaType<Ekos::CommunicationStatus>("Ekos::CommunicationStatus"); 0115 qDBusRegisterMetaType<Ekos::CommunicationStatus>(); 0116 0117 new EkosAdaptor(this); 0118 QDBusConnection::sessionBus().registerObject("/KStars/Ekos", this); 0119 0120 setWindowIcon(QIcon::fromTheme("kstars_ekos")); 0121 0122 profileModel.reset(new QStandardItemModel(0, 4)); 0123 profileModel->setHorizontalHeaderLabels(QStringList() << "id" 0124 << "name" 0125 << "host" 0126 << "port"); 0127 0128 m_CountdownTimer.setInterval(1000); 0129 connect(&m_CountdownTimer, &QTimer::timeout, this, &Ekos::Manager::updateCaptureCountDown); 0130 0131 toolsWidget->setIconSize(QSize(48, 48)); 0132 connect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange, Qt::UniqueConnection); 0133 0134 // Enable scheduler Tab 0135 toolsWidget->setTabEnabled(1, false); 0136 0137 // Enable analyze Tab 0138 toolsWidget->setTabEnabled(2, false); 0139 0140 // Start/Stop INDI Server 0141 connect(processINDIB, &QPushButton::clicked, this, &Ekos::Manager::processINDI); 0142 processINDIB->setIcon(QIcon::fromTheme("media-playback-start")); 0143 processINDIB->setToolTip(i18n("Start")); 0144 0145 // Connect/Disconnect INDI devices 0146 connect(connectB, &QPushButton::clicked, this, &Ekos::Manager::connectDevices); 0147 connect(disconnectB, &QPushButton::clicked, this, &Ekos::Manager::disconnectDevices); 0148 0149 // Init EkosLive client 0150 ekosLiveB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0151 ekosLiveClient.reset(new EkosLive::Client(this)); 0152 connect(ekosLiveClient.get(), &EkosLive::Client::connected, this, [this]() 0153 { 0154 emit ekosLiveStatusChanged(true); 0155 }); 0156 connect(ekosLiveClient.get(), &EkosLive::Client::disconnected, this, [this]() 0157 { 0158 emit ekosLiveStatusChanged(false); 0159 }); 0160 0161 // INDI Control Panel 0162 //connect(controlPanelB, &QPushButton::clicked, GUIManager::Instance(), SLOT(show())); 0163 connect(ekosLiveB, &QPushButton::clicked, this, [&]() 0164 { 0165 ekosLiveClient.get()->show(); 0166 ekosLiveClient.get()->raise(); 0167 }); 0168 0169 connect(this, &Manager::ekosStatusChanged, ekosLiveClient.get()->message(), &EkosLive::Message::setEkosStatingStatus); 0170 connect(this, &Manager::indiStatusChanged, ekosLiveClient.get()->message(), &EkosLive::Message::setINDIStatus); 0171 connect(ekosLiveClient.get()->message(), &EkosLive::Message::connected, this, [&]() 0172 { 0173 ekosLiveB->setIcon(QIcon(":/icons/cloud-online.svg")); 0174 }); 0175 connect(ekosLiveClient.get()->message(), &EkosLive::Message::disconnected, this, [&]() 0176 { 0177 ekosLiveB->setIcon(QIcon::fromTheme("folder-cloud")); 0178 }); 0179 connect(ekosLiveClient.get()->media(), &EkosLive::Media::newBoundingRect, ekosLiveClient.get()->message(), 0180 &EkosLive::Message::setBoundingRect); 0181 connect(ekosLiveClient.get()->message(), &EkosLive::Message::resetPolarView, ekosLiveClient.get()->media(), 0182 &EkosLive::Media::resetPolarView); 0183 connect(KSMessageBox::Instance(), &KSMessageBox::newMessage, ekosLiveClient.get()->message(), 0184 &EkosLive::Message::sendDialog); 0185 0186 // Port Selector 0187 m_PortSelectorTimer.setInterval(500); 0188 m_PortSelectorTimer.setSingleShot(true); 0189 connect(&m_PortSelectorTimer, &QTimer::timeout, this, [this]() 0190 { 0191 if (m_PortSelector && m_CurrentProfile->portSelector) 0192 { 0193 if (m_PortSelector->shouldShow()) 0194 { 0195 m_PortSelector->show(); 0196 m_PortSelector->raise(); 0197 0198 ekosLiveClient.get()->message()->requestPortSelection(true); 0199 } 0200 // If port selector is enabled, but we have zero ports to work with, let's proceed to connecting if it is enabled. 0201 else if (m_CurrentProfile->autoConnect) 0202 setPortSelectionComplete(); 0203 } 0204 else if (m_CurrentProfile->autoConnect) 0205 setPortSelectionComplete(); 0206 }); 0207 connect(portSelectorB, &QPushButton::clicked, this, [&]() 0208 { 0209 if (m_PortSelector) 0210 { 0211 m_PortSelector->show(); 0212 m_PortSelector->raise(); 0213 } 0214 }); 0215 0216 connect(this, &Ekos::Manager::ekosStatusChanged, this, [&](Ekos::CommunicationStatus status) 0217 { 0218 indiControlPanelB->setEnabled(status == Ekos::Success); 0219 connectB->setEnabled(false); 0220 disconnectB->setEnabled(false); 0221 profileGroup->setEnabled(status == Ekos::Idle || status == Ekos::Error); 0222 m_isStarted = (status == Ekos::Success || status == Ekos::Pending); 0223 if (status == Ekos::Success) 0224 { 0225 processINDIB->setIcon(QIcon::fromTheme("media-playback-stop")); 0226 processINDIB->setToolTip(i18n("Stop")); 0227 setWindowTitle(i18nc("@title:window", "Ekos - %1 Profile", m_CurrentProfile->name)); 0228 } 0229 else if (status == Ekos::Error || status == Ekos::Idle) 0230 { 0231 processINDIB->setIcon(QIcon::fromTheme("media-playback-start")); 0232 processINDIB->setToolTip(i18n("Start")); 0233 } 0234 else 0235 { 0236 processINDIB->setIcon(QIcon::fromTheme("call-stop")); 0237 processINDIB->setToolTip(i18n("Connection in progress. Click to abort.")); 0238 } 0239 }); 0240 connect(indiControlPanelB, &QPushButton::clicked, this, [&]() 0241 { 0242 KStars::Instance()->actionCollection()->action("show_control_panel")->trigger(); 0243 }); 0244 connect(optionsB, &QPushButton::clicked, this, [&]() 0245 { 0246 KStars::Instance()->actionCollection()->action("configure")->trigger(); 0247 }); 0248 // Save as above, but it appears in all modules 0249 connect(ekosOptionsB, &QPushButton::clicked, this, &Ekos::Manager::showEkosOptions); 0250 0251 // Clear Ekos Log 0252 connect(clearB, &QPushButton::clicked, this, &Ekos::Manager::clearLog); 0253 0254 // Logs 0255 KConfigDialog * dialog = new KConfigDialog(this, "logssettings", Options::self()); 0256 opsLogs = new Ekos::OpsLogs(); 0257 KPageWidgetItem * page = dialog->addPage(opsLogs, i18n("Logging")); 0258 page->setIcon(QIcon::fromTheme("configure")); 0259 connect(logsB, &QPushButton::clicked, dialog, &KConfigDialog::show); 0260 connect(dialog->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &Ekos::Manager::updateDebugInterfaces); 0261 connect(dialog->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &Ekos::Manager::updateDebugInterfaces); 0262 0263 // Profiles 0264 connect(addProfileB, &QPushButton::clicked, this, &Ekos::Manager::addProfile); 0265 connect(editProfileB, &QPushButton::clicked, this, &Ekos::Manager::editProfile); 0266 connect(deleteProfileB, &QPushButton::clicked, this, &Ekos::Manager::deleteProfile); 0267 connect(profileCombo, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), this, 0268 [ = ](const QString & text) 0269 { 0270 Options::setProfile(text); 0271 if (text == "Simulators") 0272 { 0273 editProfileB->setEnabled(false); 0274 deleteProfileB->setEnabled(false); 0275 } 0276 else 0277 { 0278 editProfileB->setEnabled(true); 0279 deleteProfileB->setEnabled(true); 0280 } 0281 }); 0282 0283 // Settle timer 0284 // Debounce until property stream settles down for a second. 0285 settleTimer.setInterval(1000); 0286 connect(&settleTimer, &QTimer::timeout, this, [&]() 0287 { 0288 if (m_settleStatus != Ekos::Success) 0289 { 0290 m_settleStatus = Ekos::Success; 0291 emit settleStatusChanged(m_settleStatus); 0292 } 0293 }); 0294 0295 // Ekos Wizard 0296 connect(wizardProfileB, &QPushButton::clicked, this, &Ekos::Manager::wizardProfile); 0297 0298 addProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0299 editProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0300 deleteProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0301 0302 // Set Profile icons 0303 addProfileB->setIcon(QIcon::fromTheme("list-add")); 0304 addProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0305 editProfileB->setIcon(QIcon::fromTheme("document-edit")); 0306 editProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0307 deleteProfileB->setIcon(QIcon::fromTheme("list-remove")); 0308 deleteProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0309 wizardProfileB->setIcon(QIcon::fromTheme("tools-wizard")); 0310 wizardProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0311 customDriversB->setIcon(QIcon::fromTheme("roll")); 0312 customDriversB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0313 0314 connect(customDriversB, &QPushButton::clicked, DriverManager::Instance(), &DriverManager::showCustomDrivers); 0315 0316 // Load all drivers 0317 loadDrivers(); 0318 0319 // Load add driver profiles 0320 loadProfiles(); 0321 0322 // INDI Control Panel and Ekos Options 0323 optionsB->setIcon(QIcon::fromTheme("configure", QIcon(":/icons/ekos_setup.png"))); 0324 optionsB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0325 0326 // Setup Tab 0327 toolsWidget->tabBar()->setTabIcon(0, QIcon(":/icons/ekos_setup.png")); 0328 toolsWidget->tabBar()->setTabToolTip(0, i18n("Setup")); 0329 0330 // Initialize Ekos Scheduler Module 0331 schedulerProcess.reset(new Scheduler()); 0332 int index = addModuleTab(EkosModule::Scheduler, schedulerModule(), QIcon(":/icons/ekos_scheduler.png")); 0333 toolsWidget->tabBar()->setTabToolTip(index, i18n("Scheduler")); 0334 capturePreview->shareSchedulerModule(schedulerModule()); 0335 connect(schedulerModule(), &Scheduler::newLog, this, &Ekos::Manager::updateLog); 0336 connect(schedulerModule(), &Ekos::Scheduler::newTarget, this, &Manager::setTarget); 0337 // Scheduler <---> EkosLive connections 0338 connect(schedulerModule(), &Ekos::Scheduler::jobsUpdated, ekosLiveClient.get()->message(), 0339 &EkosLive::Message::sendSchedulerJobs, Qt::UniqueConnection); 0340 connect(schedulerModule(), &Ekos::Scheduler::settingsUpdated, ekosLiveClient.get()->message(), 0341 &EkosLive::Message::sendSchedulerSettings, Qt::UniqueConnection); 0342 connect(schedulerModule(), &Ekos::Scheduler::newLog, ekosLiveClient.get()->message(), 0343 [this]() 0344 { 0345 QJsonObject cStatus = 0346 { 0347 {"log", schedulerModule()->getLogText()} 0348 }; 0349 0350 ekosLiveClient.get()->message()->sendSchedulerStatus(cStatus); 0351 }); 0352 connect(schedulerModule(), &Ekos::Scheduler::newStatus, ekosLiveClient.get()->message(), 0353 [this](Ekos::SchedulerState state) 0354 { 0355 QJsonObject cStatus = 0356 { 0357 {"status", state} 0358 }; 0359 0360 ekosLiveClient.get()->message()->sendSchedulerStatus(cStatus); 0361 }); 0362 0363 // Initialize Ekos Analyze Module 0364 analyzeProcess.reset(new Ekos::Analyze()); 0365 connect(analyzeProcess.get(), &Ekos::Analyze::newLog, this, &Ekos::Manager::updateLog); 0366 0367 index = addModuleTab(EkosModule::Analyze, analyzeProcess.get(), QIcon(":/icons/ekos_analyze.png")); 0368 toolsWidget->tabBar()->setTabToolTip(index, i18n("Analyze")); 0369 0370 numPermanentTabs = index + 1; 0371 0372 // Temporary fix. Not sure how to resize Ekos Dialog to fit contents of the various tabs in the QScrollArea which are added 0373 // dynamically. I used setMinimumSize() but it doesn't appear to make any difference. 0374 // Also set Layout policy to SetMinAndMaxSize as well. Any idea how to fix this? 0375 // FIXME 0376 //resize(1000,750); 0377 0378 m_SummaryView.reset(new SummaryFITSView(capturePreview->previewWidget)); 0379 m_SummaryView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0380 // sterne-jaeger 2021-08-08: Do not set base size here, otherwise the zoom will be incorrect 0381 // summaryPreview->setBaseSize(capturePreview->previewWidget->size()); 0382 m_SummaryView->createFloatingToolBar(); 0383 m_SummaryView->setCursorMode(FITSView::dragCursor); 0384 m_SummaryView->showProcessInfo(false); 0385 capturePreview->setSummaryFITSView(m_SummaryView.get()); 0386 mountStatusLayout->setAlignment(Qt::AlignVCenter); 0387 0388 if (Options::ekosLeftIcons()) 0389 { 0390 toolsWidget->setTabPosition(QTabWidget::West); 0391 QTransform trans; 0392 trans.rotate(90); 0393 0394 for (int i = 0; i < numPermanentTabs; ++i) 0395 { 0396 QIcon icon = toolsWidget->tabIcon(i); 0397 QPixmap pix = icon.pixmap(QSize(48, 48)); 0398 icon = QIcon(pix.transformed(trans)); 0399 toolsWidget->setTabIcon(i, icon); 0400 } 0401 } 0402 0403 //Note: This is to prevent a button from being called the default button 0404 //and then executing when the user hits the enter key such as when on a Text Box 0405 0406 QList<QPushButton *> qButtons = findChildren<QPushButton *>(); 0407 for (auto &button : qButtons) 0408 button->setAutoDefault(false); 0409 0410 0411 resize(Options::ekosWindowWidth(), Options::ekosWindowHeight()); 0412 } 0413 0414 void Manager::changeAlwaysOnTop(Qt::ApplicationState state) 0415 { 0416 if (isVisible()) 0417 { 0418 if (state == Qt::ApplicationActive) 0419 setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); 0420 else 0421 setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); 0422 show(); 0423 } 0424 } 0425 0426 Manager::~Manager() 0427 { 0428 toolsWidget->disconnect(this); 0429 } 0430 0431 void Manager::closeEvent(QCloseEvent * event) 0432 { 0433 // QAction * a = KStars::Instance()->actionCollection()->action("show_ekos"); 0434 // a->setChecked(false); 0435 0436 // 2019-02-14 JM: Close event, for some reason, make all the children disappear 0437 // when the widget is shown again. Applying a workaround here 0438 0439 event->ignore(); 0440 hide(); 0441 } 0442 0443 void Manager::hideEvent(QHideEvent * /*event*/) 0444 { 0445 Options::setEkosWindowWidth(width()); 0446 Options::setEkosWindowHeight(height()); 0447 0448 QAction * a = KStars::Instance()->actionCollection()->action("show_ekos"); 0449 a->setChecked(false); 0450 } 0451 0452 void Manager::showEvent(QShowEvent * /*event*/) 0453 { 0454 QAction * a = KStars::Instance()->actionCollection()->action("show_ekos"); 0455 a->setChecked(true); 0456 0457 // Just show the profile wizard ONCE per session 0458 if (profileWizardLaunched == false && profiles.count() == 1) 0459 { 0460 profileWizardLaunched = true; 0461 wizardProfile(); 0462 } 0463 } 0464 0465 void Manager::resizeEvent(QResizeEvent *) 0466 { 0467 focusManager->updateFocusDetailView(); 0468 guideManager->updateGuideDetailView(); 0469 } 0470 0471 void Manager::loadProfiles() 0472 { 0473 profiles.clear(); 0474 KStarsData::Instance()->userdb()->GetAllProfiles(profiles); 0475 0476 profileModel->clear(); 0477 0478 for (auto &pi : profiles) 0479 { 0480 QList<QStandardItem *> info; 0481 0482 info << new QStandardItem(pi->id) << new QStandardItem(pi->name) << new QStandardItem(pi->host) 0483 << new QStandardItem(pi->port); 0484 profileModel->appendRow(info); 0485 } 0486 0487 profileModel->sort(0); 0488 profileCombo->blockSignals(true); 0489 profileCombo->setModel(profileModel.get()); 0490 profileCombo->setModelColumn(1); 0491 profileCombo->blockSignals(false); 0492 0493 // Load last used profile from options 0494 int index = profileCombo->findText(Options::profile()); 0495 // If not found, set it to first item 0496 if (index == -1) 0497 index = 0; 0498 profileCombo->setCurrentIndex(index); 0499 } 0500 0501 int Manager::addModuleTab(Manager::EkosModule module, QWidget *tab, const QIcon &icon) 0502 { 0503 int index = 0; 0504 switch(module) 0505 { 0506 case EkosModule::Observatory: 0507 index += guideProcess ? 1 : 0; /* FALLTHRU */ 0508 case EkosModule::Guide: 0509 index += alignProcess ? 1 : 0; /* FALLTHRU */ 0510 case EkosModule::Align: 0511 index += mountProcess ? 1 : 0; /* FALLTHRU */ 0512 case EkosModule::Mount: 0513 index += focusProcess ? 1 : 0; /* FALLTHRU */ 0514 case EkosModule::Focus: 0515 index += captureProcess ? 1 : 0; /* FALLTHRU */ 0516 case EkosModule::Capture: 0517 index += analyzeProcess ? 1 : 0; /* FALLTHRU */ 0518 case EkosModule::Analyze: 0519 index += schedulerProcess ? 1 : 0; /* FALLTHRU */ 0520 case EkosModule::Scheduler: 0521 index += 1; /* FALLTHRU */ 0522 case EkosModule::Setup: 0523 // do nothing 0524 break; 0525 default: 0526 index = toolsWidget->count(); 0527 break; 0528 } 0529 0530 toolsWidget->insertTab(index, tab, icon, ""); 0531 return index; 0532 } 0533 0534 void Manager::loadDrivers() 0535 { 0536 for (auto &dv : DriverManager::Instance()->getDrivers()) 0537 { 0538 if (dv->getDriverSource() != HOST_SOURCE) 0539 driversList[dv->getLabel()] = dv; 0540 } 0541 } 0542 0543 void Manager::reset() 0544 { 0545 qCDebug(KSTARS_EKOS) << "Resetting Ekos Manager..."; 0546 0547 ProfileSettings::release(); 0548 OpticalTrainManager::release(); 0549 OpticalTrainSettings::release(); 0550 RotatorUtils::release(); 0551 0552 m_DriverDevicesCount = 0; 0553 0554 removeTabs(); 0555 0556 captureProcess.reset(); 0557 focusProcess.reset(); 0558 guideProcess.reset(); 0559 alignProcess.reset(); 0560 mountProcess.reset(); 0561 observatoryProcess.reset(); 0562 0563 for (auto &oneManger : m_FilterManagers) 0564 oneManger.reset(); 0565 m_FilterManagers.clear(); 0566 0567 for (auto &oneController : m_RotatorControllers) 0568 oneController.reset(); 0569 m_RotatorControllers.clear(); 0570 0571 DarkLibrary::Release(); 0572 m_PortSelector.reset(); 0573 m_PortSelectorTimer.stop(); 0574 0575 Ekos::CommunicationStatus previousStatus; 0576 0577 previousStatus = m_settleStatus; 0578 m_settleStatus = Ekos::Idle; 0579 if (previousStatus != m_settleStatus) 0580 emit settleStatusChanged(m_settleStatus); 0581 0582 previousStatus = m_ekosStatus; 0583 m_ekosStatus = Ekos::Idle; 0584 if (previousStatus != m_ekosStatus) 0585 emit ekosStatusChanged(m_ekosStatus); 0586 0587 previousStatus = m_indiStatus; 0588 m_indiStatus = Ekos::Idle; 0589 if (previousStatus != m_indiStatus) 0590 emit indiStatusChanged(m_indiStatus); 0591 0592 connectB->setEnabled(false); 0593 disconnectB->setEnabled(false); 0594 //controlPanelB->setEnabled(false); 0595 processINDIB->setEnabled(true); 0596 0597 mountGroup->setEnabled(false); 0598 capturePreview->setEnabled(false); 0599 capturePreview->reset(); 0600 mountStatus->setStatus(i18n("Idle"), Qt::gray); 0601 mountStatus->setStyleSheet(QString()); 0602 focusManager->reset(); 0603 guideManager->reset(); 0604 0605 m_isStarted = false; 0606 0607 processINDIB->setIcon(QIcon::fromTheme("media-playback-start")); 0608 processINDIB->setToolTip(i18n("Start")); 0609 } 0610 0611 void Manager::processINDI() 0612 { 0613 if (m_isStarted == false) 0614 start(); 0615 else 0616 stop(); 0617 } 0618 0619 void Manager::stop() 0620 { 0621 cleanDevices(); 0622 m_PortSelector.reset(); 0623 m_PortSelectorTimer.stop(); 0624 m_CountdownTimer.stop(); 0625 portSelectorB->setEnabled(false); 0626 0627 if (indiHubAgent) 0628 indiHubAgent->terminate(); 0629 0630 profileGroup->setEnabled(true); 0631 0632 setWindowTitle(i18nc("@title:window", "Ekos")); 0633 } 0634 0635 void Manager::start() 0636 { 0637 // Don't start if it is already started before 0638 if (m_ekosStatus == Ekos::Pending || m_ekosStatus == Ekos::Success) 0639 { 0640 qCWarning(KSTARS_EKOS) << "Ekos Manager start called but current Ekos Status is" << m_ekosStatus << "Ignoring request."; 0641 return; 0642 } 0643 0644 managedDrivers.clear(); 0645 0646 // If clock was paused, unpaused it and sync time 0647 if (KStarsData::Instance()->clock()->isActive() == false) 0648 { 0649 KStarsData::Instance()->changeDateTime(KStarsDateTime::currentDateTimeUtc()); 0650 KStarsData::Instance()->clock()->start(); 0651 } 0652 0653 // Reset Ekos Manager 0654 reset(); 0655 0656 // Get Current Profile 0657 getCurrentProfile(m_CurrentProfile); 0658 m_LocalMode = m_CurrentProfile->isLocal(); 0659 0660 ProfileSettings::Instance()->setProfile(m_CurrentProfile); 0661 0662 // Load profile location if one exists 0663 updateProfileLocation(m_CurrentProfile); 0664 0665 bool haveCCD = false, haveGuider = false; 0666 0667 // If external guide is specified in the profile, set the 0668 // corresponding options 0669 if (m_CurrentProfile->guidertype == Ekos::Guide::GUIDE_PHD2) 0670 { 0671 Options::setPHD2Host(m_CurrentProfile->guiderhost); 0672 Options::setPHD2Port(m_CurrentProfile->guiderport); 0673 } 0674 else if (m_CurrentProfile->guidertype == Ekos::Guide::GUIDE_LINGUIDER) 0675 { 0676 Options::setLinGuiderHost(m_CurrentProfile->guiderhost); 0677 Options::setLinGuiderPort(m_CurrentProfile->guiderport); 0678 } 0679 0680 // Parse script, if any 0681 QJsonParseError jsonError; 0682 QJsonArray profileScripts; 0683 QJsonDocument doc = QJsonDocument::fromJson(m_CurrentProfile->scripts, &jsonError); 0684 0685 if (jsonError.error == QJsonParseError::NoError) 0686 profileScripts = doc.array(); 0687 0688 // For locally running INDI server 0689 if (m_LocalMode) 0690 { 0691 auto drv = driversList.value(m_CurrentProfile->mount()); 0692 0693 if (!drv.isNull()) 0694 managedDrivers.append(drv->clone()); 0695 0696 drv = driversList.value(m_CurrentProfile->ccd()); 0697 if (!drv.isNull()) 0698 { 0699 managedDrivers.append(drv->clone()); 0700 haveCCD = true; 0701 } 0702 0703 Options::setGuiderType(m_CurrentProfile->guidertype); 0704 0705 drv = driversList.value(m_CurrentProfile->guider()); 0706 if (!drv.isNull()) 0707 { 0708 haveGuider = true; 0709 0710 // If the guider and ccd are the same driver, we have two cases: 0711 // #1 Drivers that only support ONE device per driver (such as sbig) 0712 // #2 Drivers that supports multiples devices per driver (such as sx) 0713 // For #1, we modify guider_di to make a unique label for the other device with postfix "Guide" 0714 // For #2, we set guider_di to nullptr and we prompt the user to select which device is primary ccd and which is guider 0715 // since this is the only way to find out in real time. 0716 if (haveCCD && m_CurrentProfile->guider() == m_CurrentProfile->ccd()) 0717 { 0718 if (checkUniqueBinaryDriver( driversList.value(m_CurrentProfile->ccd()), drv)) 0719 { 0720 drv.clear(); 0721 } 0722 else 0723 { 0724 drv->setUniqueLabel(drv->getLabel() + " Guide"); 0725 } 0726 } 0727 0728 if (!drv.isNull()) 0729 managedDrivers.append(drv->clone()); 0730 } 0731 0732 drv = driversList.value(m_CurrentProfile->ao()); 0733 if (!drv.isNull()) 0734 managedDrivers.append(drv->clone()); 0735 0736 drv = driversList.value(m_CurrentProfile->filter()); 0737 if (!drv.isNull()) 0738 managedDrivers.append(drv->clone()); 0739 0740 drv = driversList.value(m_CurrentProfile->focuser()); 0741 if (!drv.isNull()) 0742 managedDrivers.append(drv->clone()); 0743 0744 drv = driversList.value(m_CurrentProfile->dome()); 0745 if (!drv.isNull()) 0746 managedDrivers.append(drv->clone()); 0747 0748 drv = driversList.value(m_CurrentProfile->weather()); 0749 if (!drv.isNull()) 0750 managedDrivers.append(drv->clone()); 0751 0752 drv = driversList.value(m_CurrentProfile->aux1()); 0753 if (!drv.isNull()) 0754 { 0755 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) && 0756 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv)) 0757 managedDrivers.append(drv->clone()); 0758 } 0759 drv = driversList.value(m_CurrentProfile->aux2()); 0760 if (!drv.isNull()) 0761 { 0762 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) && 0763 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv)) 0764 managedDrivers.append(drv->clone()); 0765 } 0766 0767 drv = driversList.value(m_CurrentProfile->aux3()); 0768 if (!drv.isNull()) 0769 { 0770 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) && 0771 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv)) 0772 managedDrivers.append(drv->clone()); 0773 } 0774 0775 drv = driversList.value(m_CurrentProfile->aux4()); 0776 if (!drv.isNull()) 0777 { 0778 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) && 0779 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv)) 0780 managedDrivers.append(drv->clone()); 0781 } 0782 0783 // Add remote drivers if we have any 0784 if (m_CurrentProfile->remotedrivers.isEmpty() == false && m_CurrentProfile->remotedrivers.contains("@")) 0785 { 0786 for (auto remoteDriver : m_CurrentProfile->remotedrivers.split(",")) 0787 { 0788 QString name, label, host("localhost"), port("7624"), hostport(host + ':' + port); 0789 0790 // Possible configurations: 0791 // - device 0792 // - device@host 0793 // - device@host:port 0794 // - @host 0795 // - @host:port 0796 0797 { 0798 QStringList device_location = remoteDriver.split('@'); 0799 0800 // device or device@host:port 0801 if (device_location.length() > 0) 0802 name = device_location[0]; 0803 0804 // device@host:port or @host:port 0805 if (device_location.length() > 1) 0806 hostport = device_location[1]; 0807 } 0808 0809 { 0810 QStringList location = hostport.split(':'); 0811 0812 // host or host:port 0813 if (location.length() > 0) 0814 host = location[0]; 0815 0816 // host:port 0817 if (location.length() > 1) 0818 port = location[1]; 0819 } 0820 0821 QSharedPointer<DriverInfo> dv(new DriverInfo(name)); 0822 dv->setRemoteHost(host); 0823 dv->setRemotePort(port); 0824 0825 label = name; 0826 // Remove extra quotes 0827 label.remove("\""); 0828 dv->setLabel(label); 0829 dv->setUniqueLabel(label); 0830 managedDrivers.append(dv); 0831 } 0832 } 0833 0834 0835 if (haveCCD == false && haveGuider == false && m_CurrentProfile->remotedrivers.isEmpty()) 0836 { 0837 KSNotification::error(i18n("Ekos requires at least one CCD or Guider to operate.")); 0838 managedDrivers.clear(); 0839 m_ekosStatus = Ekos::Error; 0840 emit ekosStatusChanged(m_ekosStatus); 0841 return; 0842 } 0843 0844 m_DriverDevicesCount = managedDrivers.count(); 0845 } 0846 else 0847 { 0848 QSharedPointer<DriverInfo> remote_indi(new DriverInfo(QString("Ekos Remote Host"))); 0849 0850 remote_indi->setHostParameters(m_CurrentProfile->host, m_CurrentProfile->port); 0851 0852 remote_indi->setDriverSource(GENERATED_SOURCE); 0853 0854 managedDrivers.append(remote_indi); 0855 0856 haveCCD = m_CurrentProfile->drivers.contains("CCD"); 0857 haveGuider = m_CurrentProfile->drivers.contains("Guider"); 0858 0859 Options::setGuiderType(m_CurrentProfile->guidertype); 0860 0861 if (haveCCD == false && haveGuider == false && m_CurrentProfile->remotedrivers.isEmpty()) 0862 { 0863 KSNotification::error(i18n("Ekos requires at least one CCD or Guider to operate.")); 0864 m_DriverDevicesCount = 0; 0865 m_ekosStatus = Ekos::Error; 0866 emit ekosStatusChanged(m_ekosStatus); 0867 return; 0868 } 0869 0870 m_DriverDevicesCount = m_CurrentProfile->drivers.count(); 0871 } 0872 0873 0874 // Prioritize profile script drivers over other drivers 0875 QList<QSharedPointer<DriverInfo>> sortedList; 0876 for (const auto &oneRule : qAsConst(profileScripts)) 0877 { 0878 auto matchingDriver = std::find_if(managedDrivers.begin(), managedDrivers.end(), [oneRule](const auto & oneDriver) 0879 { 0880 return oneDriver->getLabel() == oneRule.toObject()["Driver"].toString(); 0881 }); 0882 0883 if (matchingDriver != managedDrivers.end()) 0884 { 0885 (*matchingDriver)->setStartupRule(oneRule.toObject()); 0886 sortedList.append(*matchingDriver); 0887 } 0888 } 0889 0890 // If we have any profile scripts drivers, let's re-sort managed drivers 0891 // so that profile script drivers 0892 if (!sortedList.isEmpty()) 0893 { 0894 for (auto &oneDriver : managedDrivers) 0895 { 0896 if (sortedList.contains(oneDriver) == false) 0897 sortedList.append(oneDriver); 0898 } 0899 0900 managedDrivers = sortedList; 0901 } 0902 0903 connect(DriverManager::Instance(), &DriverManager::serverStarted, this, 0904 &Manager::setServerStarted, Qt::UniqueConnection); 0905 connect(DriverManager::Instance(), &DriverManager::serverFailed, this, 0906 &Manager::setServerFailed, Qt::UniqueConnection); 0907 connect(DriverManager::Instance(), &DriverManager::clientStarted, this, 0908 &Manager::setClientStarted, Qt::UniqueConnection); 0909 connect(DriverManager::Instance(), &DriverManager::clientFailed, this, 0910 &Manager::setClientFailed, Qt::UniqueConnection); 0911 connect(DriverManager::Instance(), &DriverManager::clientTerminated, this, 0912 &Manager::setClientTerminated, Qt::UniqueConnection); 0913 0914 connect(INDIListener::Instance(), &INDIListener::newDevice, this, &Ekos::Manager::processNewDevice); 0915 connect(INDIListener::Instance(), &INDIListener::deviceRemoved, this, &Ekos::Manager::removeDevice, Qt::DirectConnection); 0916 0917 0918 #ifdef Q_OS_OSX 0919 if (m_LocalMode || m_CurrentProfile->host == "localhost") 0920 { 0921 if (isRunning("PTPCamera")) 0922 { 0923 if (KMessageBox::Yes == 0924 (KMessageBox::questionYesNo(nullptr, 0925 i18n("Ekos detected that PTP Camera is running and may prevent a Canon or Nikon camera from connecting to Ekos. Do you want to quit PTP Camera now?"), 0926 i18n("PTP Camera"), KStandardGuiItem::yes(), KStandardGuiItem::no(), 0927 "ekos_shutdown_PTPCamera"))) 0928 { 0929 //TODO is there a better way to do this. 0930 QProcess p; 0931 p.start("killall PTPCamera"); 0932 p.waitForFinished(); 0933 } 0934 } 0935 } 0936 #endif 0937 if (m_LocalMode) 0938 { 0939 auto executeStartINDIServices = [this]() 0940 { 0941 appendLogText(i18n("Starting INDI services...")); 0942 0943 m_ekosStatus = Ekos::Pending; 0944 emit ekosStatusChanged(m_ekosStatus); 0945 0946 DriverManager::Instance()->startDevices(managedDrivers); 0947 }; 0948 0949 // If INDI server is already running, let's see if we need to shut it down first 0950 if (isRunning("indiserver")) 0951 { 0952 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, executeStartINDIServices]() 0953 { 0954 KSMessageBox::Instance()->disconnect(this); 0955 DriverManager::Instance()->stopAllDevices(); 0956 //TODO is there a better way to do this. 0957 QProcess p; 0958 const QString program = "pkill"; 0959 QStringList arguments; 0960 arguments << "indiserver"; 0961 p.start(program, arguments); 0962 p.waitForFinished(); 0963 0964 QTimer::singleShot(1000, this, executeStartINDIServices); 0965 }); 0966 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this, executeStartINDIServices]() 0967 { 0968 KSMessageBox::Instance()->disconnect(this); 0969 executeStartINDIServices(); 0970 }); 0971 0972 KSMessageBox::Instance()->questionYesNo(i18n("Ekos detected an instance of INDI server running. Do you wish to " 0973 "shut down the existing instance before starting a new one?"), 0974 i18n("INDI Server"), 5); 0975 } 0976 else 0977 executeStartINDIServices(); 0978 0979 } 0980 else 0981 { 0982 auto runConnection = [this]() 0983 { 0984 // If it got cancelled by the user, return immediately. 0985 if (m_ekosStatus != Ekos::Pending) 0986 return; 0987 0988 appendLogText( 0989 i18n("Connecting to remote INDI server at %1 on port %2 ...", m_CurrentProfile->host, m_CurrentProfile->port)); 0990 0991 DriverManager::Instance()->connectRemoteHost(managedDrivers.first()); 0992 }; 0993 0994 auto runProfile = [this, runConnection]() 0995 { 0996 // If it got cancelled by the user, return immediately. 0997 if (m_ekosStatus != Ekos::Pending) 0998 return; 0999 1000 INDI::WebManager::syncCustomDrivers(m_CurrentProfile); 1001 INDI::WebManager::checkVersion(m_CurrentProfile); 1002 1003 if (INDI::WebManager::areDriversRunning(m_CurrentProfile) == false) 1004 { 1005 INDI::WebManager::stopProfile(m_CurrentProfile); 1006 1007 if (INDI::WebManager::startProfile(m_CurrentProfile) == false) 1008 { 1009 appendLogText(i18n("Failed to start profile on remote INDI Web Manager.")); 1010 return; 1011 } 1012 1013 appendLogText(i18n("Starting profile on remote INDI Web Manager...")); 1014 m_RemoteManagerStart = true; 1015 } 1016 1017 runConnection(); 1018 }; 1019 1020 m_ekosStatus = Ekos::Pending; 1021 emit ekosStatusChanged(m_ekosStatus); 1022 1023 // If we need to use INDI Web Manager 1024 if (m_CurrentProfile->INDIWebManagerPort > 0) 1025 { 1026 appendLogText(i18n("Establishing communication with remote INDI Web Manager...")); 1027 m_RemoteManagerStart = false; 1028 QFutureWatcher<bool> *watcher = new QFutureWatcher<bool>(); 1029 connect(watcher, &QFutureWatcher<bool>::finished, this, [this, runConnection, runProfile, watcher]() 1030 { 1031 watcher->deleteLater(); 1032 1033 // If it got cancelled by the user, return immediately. 1034 if (m_ekosStatus != Ekos::Pending) 1035 return; 1036 1037 // If web manager is online, try to run the profile in it 1038 if (watcher->result()) 1039 { 1040 runProfile(); 1041 } 1042 // Else, try to connect directly to INDI server as there could be a chance 1043 // that it is already running. 1044 else 1045 { 1046 appendLogText(i18n("Warning: INDI Web Manager is not online.")); 1047 runConnection(); 1048 } 1049 1050 }); 1051 1052 QFuture<bool> result = INDI::AsyncWebManager::isOnline(m_CurrentProfile); 1053 watcher->setFuture(result); 1054 } 1055 else 1056 { 1057 runConnection(); 1058 } 1059 } 1060 } 1061 1062 void Manager::setClientStarted(const QString &host, int port) 1063 { 1064 if (managedDrivers.size() > 0) 1065 { 1066 if (m_LocalMode) 1067 { 1068 if (m_CurrentProfile->autoConnect) 1069 appendLogText(i18n("INDI services started on port %1.", port)); 1070 else 1071 appendLogText( 1072 i18n("INDI services started on port %1. Please connect devices.", port)); 1073 } 1074 else 1075 { 1076 appendLogText( 1077 i18n("INDI services started. Connection to remote INDI server %1:%2 is successful. Waiting for devices...", host, port)); 1078 } 1079 } 1080 1081 QTimer::singleShot(MAX_LOCAL_INDI_TIMEOUT, this, &Ekos::Manager::checkINDITimeout); 1082 } 1083 1084 void Manager::setClientFailed(const QString &host, int port, const QString &errorMessage) 1085 { 1086 if (m_LocalMode) 1087 appendLogText(i18n("Failed to connect to local INDI server %1:%2", host, port)); 1088 else 1089 appendLogText(i18n("Failed to connect to remote INDI server %1:%2", host, port)); 1090 1091 //INDIListener::Instance()->disconnect(this); 1092 // qDeleteAll(managedDrivers); 1093 // managedDrivers.clear(); 1094 m_ekosStatus = Ekos::Error; 1095 emit ekosStatusChanged(m_ekosStatus); 1096 KSNotification::error(errorMessage, i18n("Error"), 15); 1097 } 1098 1099 void Manager::setClientTerminated(const QString &host, int port, const QString &errorMessage) 1100 { 1101 if (m_LocalMode) 1102 appendLogText(i18n("Lost connection to local INDI server %1:%2", host, port)); 1103 else 1104 appendLogText(i18n("Lost connection to remote INDI server %1:%2", host, port)); 1105 1106 //INDIListener::Instance()->disconnect(this); 1107 // qDeleteAll(managedDrivers); 1108 // managedDrivers.clear(); 1109 m_ekosStatus = Ekos::Error; 1110 emit ekosStatusChanged(m_ekosStatus); 1111 KSNotification::error(errorMessage, i18n("Error"), 15); 1112 } 1113 1114 void Manager::setServerStarted(const QString &host, int port) 1115 { 1116 if (m_LocalMode && m_CurrentProfile->indihub != INDIHub::None) 1117 { 1118 if (QFile(Options::iNDIHubAgent()).exists()) 1119 { 1120 indiHubAgent = new QProcess(); 1121 QStringList args; 1122 1123 args << "--indi-server" << QString("%1:%2").arg(host).arg(port); 1124 if (m_CurrentProfile->guidertype == Ekos::Guide::GUIDE_PHD2) 1125 args << "--phd2-server" << QString("%1:%2").arg(m_CurrentProfile->guiderhost).arg(m_CurrentProfile->guiderport); 1126 args << "--mode" << INDIHub::toString(m_CurrentProfile->indihub); 1127 indiHubAgent->start(Options::iNDIHubAgent(), args); 1128 1129 qCDebug(KSTARS_EKOS) << "Started INDIHub agent."; 1130 } 1131 } 1132 } 1133 1134 void Manager::setServerFailed(const QString &host, int port, const QString &message) 1135 { 1136 Q_UNUSED(host) 1137 Q_UNUSED(port) 1138 managedDrivers.clear(); 1139 m_ekosStatus = Ekos::Error; 1140 emit ekosStatusChanged(m_ekosStatus); 1141 KSNotification::error(message, i18n("Error"), 15); 1142 } 1143 1144 //void Manager::setServerTerminated(const QString &host, int port, const QString &message) 1145 //{ 1146 // if ((m_LocalMode && managedDrivers.first()->getPort() == port) || 1147 // (currentProfile->host == host && currentProfile->port == port)) 1148 // { 1149 // cleanDevices(false); 1150 // if (indiHubAgent) 1151 // indiHubAgent->terminate(); 1152 // } 1153 1154 // INDIListener::Instance()->disconnect(this); 1155 // qDeleteAll(managedDrivers); 1156 // managedDrivers.clear(); 1157 // m_ekosStatus = Ekos::Error; 1158 // emit ekosStatusChanged(m_ekosStatus); 1159 // KSNotification::error(message, i18n("Error"), 15); 1160 //} 1161 1162 void Manager::checkINDITimeout() 1163 { 1164 // Don't check anything unless we're still pending 1165 if (m_ekosStatus != Ekos::Pending) 1166 { 1167 // All devices are connected already, nothing to do. 1168 if (m_indiStatus != Ekos::Pending || m_CurrentProfile->portSelector || m_CurrentProfile->autoConnect == false) 1169 return; 1170 1171 QStringList disconnectedDevices; 1172 for (auto &oneDevice : INDIListener::devices()) 1173 { 1174 if (oneDevice->isConnected() == false) 1175 disconnectedDevices << oneDevice->getDeviceName(); 1176 } 1177 1178 QString message; 1179 1180 if (disconnectedDevices.count() == 1) 1181 message = i18n("Failed to connect to %1. Please ensure device is connected and powered on.", disconnectedDevices.first()); 1182 else 1183 message = i18n("Failed to connect to \n%1\nPlease ensure each device is connected and powered on.", 1184 disconnectedDevices.join("\n")); 1185 1186 appendLogText(message); 1187 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn); 1188 return; 1189 } 1190 1191 1192 if (m_DriverDevicesCount <= 0) 1193 { 1194 m_ekosStatus = Ekos::Success; 1195 emit ekosStatusChanged(m_ekosStatus); 1196 return; 1197 } 1198 1199 if (m_LocalMode) 1200 { 1201 QStringList remainingDevices; 1202 for (auto &drv : managedDrivers) 1203 { 1204 if (drv->getDevices().count() == 0) 1205 remainingDevices << QString("+ %1").arg( 1206 drv->getUniqueLabel().isEmpty() == false ? drv->getUniqueLabel() : drv->getName()); 1207 } 1208 1209 if (remainingDevices.count() == 1) 1210 { 1211 QString message = i18n("Unable to establish:\n%1\nPlease ensure the device is connected and powered on.", 1212 remainingDevices.at(0)); 1213 appendLogText(message); 1214 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn); 1215 KNotification::beep(i18n("Ekos startup error")); 1216 } 1217 else 1218 { 1219 QString message = i18n("Unable to establish the following devices:\n%1\nPlease ensure each device is connected " 1220 "and powered on.", remainingDevices.join("\n")); 1221 appendLogText(message); 1222 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn); 1223 KNotification::beep(i18n("Ekos startup error")); 1224 } 1225 } 1226 else 1227 { 1228 QStringList remainingDevices; 1229 1230 for (auto &driver : m_CurrentProfile->drivers.values()) 1231 { 1232 bool driverFound = false; 1233 1234 for (auto &device : INDIListener::devices()) 1235 { 1236 if (device->getBaseDevice().getDriverName() == driver) 1237 { 1238 driverFound = true; 1239 break; 1240 } 1241 } 1242 1243 if (driverFound == false) 1244 remainingDevices << QString("+ %1").arg(driver); 1245 } 1246 1247 if (remainingDevices.count() == 1) 1248 { 1249 QString message = i18n("Unable to remotely establish:\n%1\nPlease ensure the device is connected and powered on.", 1250 remainingDevices.at(0)); 1251 appendLogText(message); 1252 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn); 1253 KNotification::beep(i18n("Ekos startup error")); 1254 } 1255 else 1256 { 1257 QString message = i18n("Unable to remotely establish the following devices:\n%1\nPlease ensure each device is connected " 1258 "and powered on.", remainingDevices.join("\n")); 1259 appendLogText(message); 1260 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn); 1261 KNotification::beep(i18n("Ekos startup error")); 1262 } 1263 } 1264 1265 m_ekosStatus = Ekos::Error; 1266 } 1267 1268 bool Manager::isINDIReady() 1269 { 1270 // Check if already connected 1271 int nConnected = 0; 1272 1273 Ekos::CommunicationStatus previousStatus = m_indiStatus; 1274 1275 auto devices = INDIListener::devices(); 1276 for (auto &device : devices) 1277 { 1278 // Make sure we're not only connected, but also ready (i.e. all properties have already been defined). 1279 if (device->isConnected() && device->isReady()) 1280 nConnected++; 1281 } 1282 if (devices.count() == nConnected) 1283 { 1284 m_indiStatus = Ekos::Success; 1285 emit indiStatusChanged(m_indiStatus); 1286 return true; 1287 } 1288 1289 m_indiStatus = Ekos::Pending; 1290 if (previousStatus != m_indiStatus) 1291 emit indiStatusChanged(m_indiStatus); 1292 1293 return false; 1294 } 1295 1296 void Manager::connectDevices() 1297 { 1298 if (isINDIReady()) 1299 return; 1300 1301 auto devices = INDIListener::devices(); 1302 1303 for (auto &device : devices) 1304 { 1305 qCDebug(KSTARS_EKOS) << "Connecting " << device->getDeviceName(); 1306 device->Connect(); 1307 } 1308 1309 connectB->setEnabled(false); 1310 disconnectB->setEnabled(true); 1311 1312 appendLogText(i18n("Connecting INDI devices...")); 1313 } 1314 1315 void Manager::disconnectDevices() 1316 { 1317 for (auto &device : INDIListener::devices()) 1318 { 1319 qCDebug(KSTARS_EKOS) << "Disconnecting " << device->getDeviceName(); 1320 device->Disconnect(); 1321 } 1322 1323 appendLogText(i18n("Disconnecting INDI devices...")); 1324 } 1325 1326 void Manager::cleanDevices(bool stopDrivers) 1327 { 1328 if (m_ekosStatus == Ekos::Idle) 1329 return; 1330 1331 if (mountModule()) 1332 mountModule()->stopTimers(); 1333 1334 INDIListener::Instance()->disconnect(this); 1335 DriverManager::Instance()->disconnect(this); 1336 1337 if (managedDrivers.isEmpty() == false) 1338 { 1339 if (m_LocalMode) 1340 { 1341 if (stopDrivers) 1342 DriverManager::Instance()->stopDevices(managedDrivers); 1343 } 1344 else 1345 { 1346 if (stopDrivers) 1347 { 1348 DriverManager::Instance()->disconnectRemoteHost(managedDrivers.first()); 1349 1350 if (m_RemoteManagerStart && m_CurrentProfile->INDIWebManagerPort != -1) 1351 INDI::WebManager::stopProfile(m_CurrentProfile); 1352 } 1353 m_RemoteManagerStart = false; 1354 } 1355 } 1356 1357 reset(); 1358 1359 profileGroup->setEnabled(true); 1360 1361 appendLogText(i18n("INDI services stopped.")); 1362 } 1363 1364 void Manager::processNewDevice(const QSharedPointer<ISD::GenericDevice> &device) 1365 { 1366 qCInfo(KSTARS_EKOS) << "Ekos received a new device: " << device->getDeviceName(); 1367 1368 Ekos::CommunicationStatus previousStatus = m_indiStatus; 1369 1370 // for(auto &oneDevice : INDIListener::devices()) 1371 // { 1372 // if (oneDevice->getDeviceName() == device->getDeviceName()) 1373 // { 1374 // qCWarning(KSTARS_EKOS) << "Found duplicate device, ignoring..."; 1375 // return; 1376 // } 1377 // } 1378 1379 // Always reset INDI Connection status if we receive a new device 1380 m_indiStatus = Ekos::Idle; 1381 if (previousStatus != m_indiStatus) 1382 emit indiStatusChanged(m_indiStatus); 1383 1384 m_DriverDevicesCount--; 1385 1386 connect(device.get(), &ISD::GenericDevice::ready, this, &Ekos::Manager::setDeviceReady, Qt::UniqueConnection); 1387 connect(device.get(), &ISD::GenericDevice::newMount, this, &Ekos::Manager::addMount, Qt::UniqueConnection); 1388 connect(device.get(), &ISD::GenericDevice::newCamera, this, &Ekos::Manager::addCamera, Qt::UniqueConnection); 1389 connect(device.get(), &ISD::GenericDevice::newGuider, this, &Ekos::Manager::addGuider, Qt::UniqueConnection); 1390 connect(device.get(), &ISD::GenericDevice::newFilterWheel, this, &Ekos::Manager::addFilterWheel, Qt::UniqueConnection); 1391 connect(device.get(), &ISD::GenericDevice::newFocuser, this, &Ekos::Manager::addFocuser, Qt::UniqueConnection); 1392 connect(device.get(), &ISD::GenericDevice::newDome, this, &Ekos::Manager::addDome, Qt::UniqueConnection); 1393 connect(device.get(), &ISD::GenericDevice::newRotator, this, &Ekos::Manager::addRotator, Qt::UniqueConnection); 1394 connect(device.get(), &ISD::GenericDevice::newWeather, this, &Ekos::Manager::addWeather, Qt::UniqueConnection); 1395 connect(device.get(), &ISD::GenericDevice::newDustCap, this, &Ekos::Manager::addDustCap, Qt::UniqueConnection); 1396 connect(device.get(), &ISD::GenericDevice::newLightBox, this, &Ekos::Manager::addLightBox, Qt::UniqueConnection); 1397 connect(device.get(), &ISD::GenericDevice::newGPS, this, &Ekos::Manager::addGPS, Qt::UniqueConnection); 1398 1399 connect(device.get(), &ISD::GenericDevice::Connected, this, &Ekos::Manager::deviceConnected, Qt::UniqueConnection); 1400 connect(device.get(), &ISD::GenericDevice::Disconnected, this, &Ekos::Manager::deviceDisconnected, Qt::UniqueConnection); 1401 connect(device.get(), &ISD::GenericDevice::propertyDefined, this, &Ekos::Manager::processNewProperty, Qt::UniqueConnection); 1402 connect(device.get(), &ISD::GenericDevice::propertyDeleted, this, &Ekos::Manager::processDeleteProperty, 1403 Qt::UniqueConnection); 1404 connect(device.get(), &ISD::GenericDevice::propertyUpdated, this, &Ekos::Manager::processUpdateProperty, 1405 Qt::UniqueConnection); 1406 connect(device.get(), &ISD::GenericDevice::interfaceDefined, this, &Ekos::Manager::syncActiveDevices, Qt::UniqueConnection); 1407 connect(device.get(), &ISD::GenericDevice::messageUpdated, this, &Ekos::Manager::processMessage, Qt::UniqueConnection); 1408 1409 1410 1411 // Only look for primary & guider CCDs if we can tell a difference between them 1412 // otherwise rely on saved options 1413 if (m_CurrentProfile->ccd() != m_CurrentProfile->guider()) 1414 { 1415 for (auto &oneCamera : INDIListener::devices()) 1416 { 1417 if (oneCamera->getDeviceName().startsWith(m_CurrentProfile->ccd(), Qt::CaseInsensitive)) 1418 m_PrimaryCamera = QString(oneCamera->getDeviceName()); 1419 else if (oneCamera->getDeviceName().startsWith(m_CurrentProfile->guider(), Qt::CaseInsensitive)) 1420 m_GuideCamera = QString(oneCamera->getDeviceName()); 1421 } 1422 } 1423 1424 if (m_DriverDevicesCount <= 0) 1425 { 1426 m_ekosStatus = Ekos::Success; 1427 emit ekosStatusChanged(m_ekosStatus); 1428 1429 connectB->setEnabled(true); 1430 disconnectB->setEnabled(false); 1431 1432 if (m_LocalMode == false && m_DriverDevicesCount == 0) 1433 { 1434 if (m_CurrentProfile->autoConnect) 1435 appendLogText(i18n("Remote devices established.")); 1436 else 1437 appendLogText(i18n("Remote devices established. Please connect devices.")); 1438 } 1439 } 1440 } 1441 1442 void Manager::deviceConnected() 1443 { 1444 connectB->setEnabled(false); 1445 disconnectB->setEnabled(true); 1446 processINDIB->setEnabled(false); 1447 1448 auto device = qobject_cast<ISD::GenericDevice *>(sender()); 1449 1450 if (Options::verboseLogging()) 1451 { 1452 qCInfo(KSTARS_EKOS) << device->getDeviceName() 1453 << "Version:" << device->getDriverVersion() 1454 << "Interface:" << device->getDriverInterface() 1455 << "is connected."; 1456 } 1457 1458 if (Options::neverLoadConfig() == false) 1459 { 1460 INDIConfig tConfig = Options::loadConfigOnConnection() ? LOAD_LAST_CONFIG : LOAD_DEFAULT_CONFIG; 1461 1462 for (auto &oneDevice : INDIListener::devices()) 1463 { 1464 if (oneDevice == device) 1465 { 1466 connect(device, &ISD::GenericDevice::propertyUpdated, this, &Ekos::Manager::watchDebugProperty, Qt::UniqueConnection); 1467 1468 auto configProp = device->getBaseDevice().getSwitch("CONFIG_PROCESS"); 1469 if (configProp && configProp.getState() == IPS_IDLE) 1470 device->setConfig(tConfig); 1471 break; 1472 } 1473 } 1474 } 1475 } 1476 1477 void Manager::deviceDisconnected() 1478 { 1479 ISD::GenericDevice * dev = static_cast<ISD::GenericDevice *>(sender()); 1480 1481 Ekos::CommunicationStatus previousStatus = m_indiStatus; 1482 1483 if (dev != nullptr) 1484 { 1485 if (dev->getState("CONNECTION") == IPS_ALERT) 1486 m_indiStatus = Ekos::Error; 1487 else if (dev->getState("CONNECTION") == IPS_BUSY) 1488 m_indiStatus = Ekos::Pending; 1489 else 1490 m_indiStatus = Ekos::Idle; 1491 1492 if (Options::verboseLogging()) 1493 qCDebug(KSTARS_EKOS) << dev->getDeviceName() << " is disconnected."; 1494 1495 // In case a device fails to connect, display and log a useful message for the user. 1496 if (m_indiStatus == Ekos::Error) 1497 { 1498 QString message = i18n("%1 failed to connect.\nPlease ensure the device is connected and powered on.", 1499 dev->getDeviceName()); 1500 appendLogText(message); 1501 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn); 1502 } 1503 else if (m_indiStatus == Ekos::Idle) 1504 { 1505 QString message = i18n("%1 is disconnected.", dev->getDeviceName()); 1506 appendLogText(message); 1507 } 1508 } 1509 else 1510 m_indiStatus = Ekos::Idle; 1511 1512 if (previousStatus != m_indiStatus) 1513 emit indiStatusChanged(m_indiStatus); 1514 1515 connectB->setEnabled(true); 1516 disconnectB->setEnabled(false); 1517 processINDIB->setEnabled(true); 1518 } 1519 1520 void Manager::addMount(ISD::Mount *device) 1521 { 1522 ekosLiveClient->message()->sendScopes(); 1523 1524 appendLogText(i18n("%1 is online.", device->getDeviceName())); 1525 1526 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1527 } 1528 1529 void Manager::addCamera(ISD::Camera * device) 1530 { 1531 ekosLiveClient.get()->media()->registerCameras(); 1532 1533 appendLogText(i18n("%1 is online.", device->getDeviceName())); 1534 1535 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1536 } 1537 1538 void Manager::addFilterWheel(ISD::FilterWheel * device) 1539 { 1540 QString name = device->getDeviceName(); 1541 appendLogText(i18n("%1 filter is online.", name)); 1542 1543 createFilterManager(device); 1544 1545 emit newDevice(name, device->getDriverInterface()); 1546 } 1547 1548 void Manager::addFocuser(ISD::Focuser *device) 1549 { 1550 appendLogText(i18n("%1 focuser is online.", device->getDeviceName())); 1551 1552 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1553 } 1554 1555 void Manager::addRotator(ISD::Rotator *device) 1556 { 1557 appendLogText(i18n("Rotator %1 is online.", device->getDeviceName())); 1558 1559 // createRotatorControl(device); 1560 1561 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1562 } 1563 1564 void Manager::addDome(ISD::Dome * device) 1565 { 1566 appendLogText(i18n("%1 is online.", device->getDeviceName())); 1567 1568 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1569 } 1570 1571 void Manager::addWeather(ISD::Weather * device) 1572 { 1573 appendLogText(i18n("%1 Weather is online.", device->getDeviceName())); 1574 1575 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1576 } 1577 1578 void Manager::addGPS(ISD::GPS * device) 1579 { 1580 appendLogText(i18n("%1 GPS is online.", device->getDeviceName())); 1581 1582 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1583 } 1584 1585 void Manager::addDustCap(ISD::DustCap * device) 1586 { 1587 OpticalTrainManager::Instance()->syncDevices(); 1588 1589 appendLogText(i18n("%1 Dust cap is online.", device->getDeviceName())); 1590 1591 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1592 } 1593 1594 void Manager::addLightBox(ISD::LightBox * device) 1595 { 1596 appendLogText(i18n("%1 Light box is online.", device->getDeviceName())); 1597 1598 emit newDevice(device->getDeviceName(), device->getDriverInterface()); 1599 } 1600 1601 void Manager::syncGenericDevice(const QSharedPointer<ISD::GenericDevice> &device) 1602 { 1603 createModules(device); 1604 1605 //////////////////////////////////////////////////////////////////////////////////////////////////// 1606 /// Cameras 1607 //////////////////////////////////////////////////////////////////////////////////////////////////// 1608 auto camera = device->getCamera(); 1609 if (camera) 1610 { 1611 // Focus Module 1612 if (focusProcess) 1613 { 1614 if (camera->hasCooler()) 1615 { 1616 QSharedPointer<ISD::GenericDevice> generic; 1617 if (INDIListener::findDevice(camera->getDeviceName(), generic)) 1618 focusModule()->addTemperatureSource(generic); 1619 } 1620 } 1621 1622 } 1623 1624 //////////////////////////////////////////////////////////////////////////////////////////////////// 1625 /// Mount 1626 //////////////////////////////////////////////////////////////////////////////////////////////////// 1627 1628 //////////////////////////////////////////////////////////////////////////////////////////////////// 1629 /// Focuser 1630 //////////////////////////////////////////////////////////////////////////////////////////////////// 1631 auto focuser = device->getFocuser(); 1632 if (focuser) 1633 { 1634 if (focusProcess) 1635 { 1636 // Temperature sources. 1637 QSharedPointer<ISD::GenericDevice> generic; 1638 if (INDIListener::findDevice(focuser->getDeviceName(), generic)) 1639 focusModule()->addTemperatureSource(generic); 1640 } 1641 } 1642 1643 //////////////////////////////////////////////////////////////////////////////////////////////////// 1644 /// Filter Wheel 1645 //////////////////////////////////////////////////////////////////////////////////////////////////// 1646 1647 //////////////////////////////////////////////////////////////////////////////////////////////////// 1648 /// Rotators 1649 //////////////////////////////////////////////////////////////////////////////////////////////////// 1650 1651 //////////////////////////////////////////////////////////////////////////////////////////////////// 1652 /// Domes 1653 //////////////////////////////////////////////////////////////////////////////////////////////////// 1654 auto dome = device->getDome(); 1655 if (dome) 1656 { 1657 if (captureProcess) 1658 captureProcess->setDome(dome); 1659 if (alignProcess) 1660 alignProcess->setDome(dome); 1661 if (observatoryProcess) 1662 observatoryProcess->setDome(dome); 1663 } 1664 1665 //////////////////////////////////////////////////////////////////////////////////////////////////// 1666 /// Weather 1667 //////////////////////////////////////////////////////////////////////////////////////////////////// 1668 auto weather = device->getWeather(); 1669 if (weather) 1670 { 1671 if (observatoryProcess) 1672 observatoryProcess->addWeatherSource(weather); 1673 1674 if (focusProcess) 1675 { 1676 QSharedPointer<ISD::GenericDevice> generic; 1677 if (INDIListener::findDevice(weather->getDeviceName(), generic)) 1678 focusModule()->addTemperatureSource(generic); 1679 } 1680 } 1681 1682 //////////////////////////////////////////////////////////////////////////////////////////////////// 1683 /// GPS 1684 //////////////////////////////////////////////////////////////////////////////////////////////////// 1685 auto gps = device->getGPS(); 1686 if (gps) 1687 { 1688 if (mountProcess) 1689 mountModule()->addGPS(gps); 1690 } 1691 1692 //////////////////////////////////////////////////////////////////////////////////////////////////// 1693 /// Dust Cap 1694 //////////////////////////////////////////////////////////////////////////////////////////////////// 1695 1696 //////////////////////////////////////////////////////////////////////////////////////////////////// 1697 /// Light Box 1698 //////////////////////////////////////////////////////////////////////////////////////////////////// 1699 } 1700 1701 void Manager::removeDevice(const QSharedPointer<ISD::GenericDevice> &device) 1702 { 1703 if (alignProcess) 1704 alignModule()->removeDevice(device); 1705 if (captureProcess) 1706 captureProcess->removeDevice(device); 1707 if (focusProcess) 1708 focusModule()->removeDevice(device); 1709 if (mountProcess) 1710 mountModule()->removeDevice(device); 1711 if (guideProcess) 1712 guideProcess->removeDevice(device); 1713 if (observatoryProcess) 1714 observatoryProcess->removeDevice(device); 1715 if (m_PortSelector) 1716 m_PortSelector->removeDevice(device->getDeviceName()); 1717 1718 DarkLibrary::Instance()->removeDevice(device); 1719 1720 // Remove from filter managers 1721 for (auto &oneManager : m_FilterManagers) 1722 { 1723 oneManager->removeDevice(device); 1724 } 1725 1726 ekosLiveClient->message()->clearPendingProperties(); 1727 1728 // Remove from rotator controllers 1729 for (auto &oneController : m_RotatorControllers) 1730 { 1731 oneController->close(); 1732 } 1733 1734 appendLogText(i18n("%1 is offline.", device->getDeviceName())); 1735 1736 1737 if (INDIListener::devices().isEmpty()) 1738 { 1739 cleanDevices(); 1740 removeTabs(); 1741 } 1742 } 1743 1744 void Manager::processDeleteProperty(INDI::Property prop) 1745 { 1746 ekosLiveClient.get()->message()->processDeleteProperty(prop); 1747 } 1748 1749 void Manager::processMessage(int id) 1750 { 1751 auto origin = static_cast<ISD::GenericDevice *>(sender()); 1752 // Shouldn't happen 1753 if (!origin) 1754 return; 1755 QSharedPointer<ISD::GenericDevice> device; 1756 if (!INDIListener::findDevice(origin->getDeviceName(), device)) 1757 return; 1758 1759 ekosLiveClient.get()->message()->processMessage(device, id); 1760 } 1761 1762 void Manager::processUpdateProperty(INDI::Property prop) 1763 { 1764 ekosLiveClient.get()->message()->processUpdateProperty(prop); 1765 1766 if (prop.isNameMatch("CCD_INFO") || 1767 prop.isNameMatch("GUIDER_INFO") || 1768 prop.isNameMatch("CCD_FRAME") || 1769 prop.isNameMatch("GUIDER_FRAME")) 1770 { 1771 if (focusModule() != nullptr) 1772 focusModule()->syncCameraInfo(); 1773 1774 if (guideModule() != nullptr) 1775 guideModule()->syncCameraInfo(); 1776 1777 if (alignModule() != nullptr) 1778 alignModule()->syncCameraInfo(); 1779 1780 return; 1781 } 1782 else if (prop.isNameMatch("ACTIVE_DEVICES")) 1783 { 1784 syncActiveDevices(); 1785 } 1786 } 1787 1788 void Manager::processNewProperty(INDI::Property prop) 1789 { 1790 QSharedPointer<ISD::GenericDevice> device; 1791 if (!INDIListener::findDevice(prop.getDeviceName(), device)) 1792 return; 1793 1794 settleTimer.start(); 1795 1796 ekosLiveClient.get()->message()->processNewProperty(prop); 1797 1798 if (prop.isNameMatch("DEVICE_PORT_SCAN") || prop.isNameMatch("CONNECTION_TYPE")) 1799 { 1800 if (!m_PortSelector) 1801 { 1802 m_PortSelector.reset(new Selector::Dialog(KStars::Instance())); 1803 connect(m_PortSelector.get(), &Selector::Dialog::accepted, this, &Manager::setPortSelectionComplete); 1804 } 1805 m_PortSelectorTimer.start(); 1806 portSelectorB->setEnabled(true); 1807 m_PortSelector->addDevice(device); 1808 return; 1809 } 1810 1811 // Check if we need to turn on DEBUG for logging purposes 1812 if (prop.isNameMatch("DEBUG")) 1813 { 1814 uint16_t interface = device->getDriverInterface(); 1815 if ( opsLogs->getINDIDebugInterface() & interface ) 1816 { 1817 // Check if we need to enable debug logging for the INDI drivers. 1818 auto debugSP = prop.getSwitch(); 1819 debugSP->at(0)->setState(ISS_ON); 1820 debugSP->at(1)->setState(ISS_OFF); 1821 device->sendNewProperty(debugSP); 1822 } 1823 return; 1824 } 1825 1826 // Handle debug levels for logging purposes 1827 if (prop.isNameMatch("DEBUG_LEVEL")) 1828 { 1829 uint16_t interface = device->getDriverInterface(); 1830 // Check if the logging option for the specific device class is on and if the device interface matches it. 1831 if ( opsLogs->getINDIDebugInterface() & interface ) 1832 { 1833 // Turn on everything 1834 auto debugLevel = prop.getSwitch(); 1835 for (auto &it : *debugLevel) 1836 it.setState(ISS_ON); 1837 1838 device->sendNewProperty(debugLevel); 1839 } 1840 return; 1841 } 1842 1843 if (prop.isNameMatch("ACTIVE_DEVICES")) 1844 { 1845 if (device->getDriverInterface() > 0) 1846 syncActiveDevices(); 1847 1848 return; 1849 } 1850 1851 if (prop.isNameMatch("ASTROMETRY_SOLVER")) 1852 { 1853 for (auto &oneDevice : INDIListener::devices()) 1854 { 1855 if (oneDevice->getDeviceName() == prop.getDeviceName()) 1856 { 1857 initAlign(); 1858 alignModule()->setAstrometryDevice(oneDevice); 1859 break; 1860 } 1861 } 1862 1863 return; 1864 } 1865 1866 if (focusModule() != nullptr && strstr(prop.getName(), "FOCUS_")) 1867 { 1868 focusModule()->checkFocuser(); 1869 return; 1870 } 1871 } 1872 1873 void Manager::processTabChange() 1874 { 1875 auto currentWidget = toolsWidget->currentWidget(); 1876 1877 if (alignProcess && alignModule() == currentWidget) 1878 { 1879 auto alignReady = alignModule()->isEnabled() == false && alignModule()->isParserOK(); 1880 auto captureReady = captureProcess && captureModule()->isEnabled(); 1881 auto mountReady = mountProcess && mountModule()->isEnabled(); 1882 if (alignReady && captureReady && mountReady) 1883 alignModule()->setEnabled(true); 1884 1885 alignModule()->checkCamera(); 1886 } 1887 else if (captureProcess && currentWidget == captureModule()) 1888 { 1889 captureModule()->process()->checkCamera(); 1890 } 1891 else if (focusProcess && currentWidget == focusModule()) 1892 { 1893 focusModule()->checkCamera(); 1894 } 1895 else if (guideProcess && currentWidget == guideModule()) 1896 { 1897 guideModule()->checkCamera(); 1898 } 1899 1900 updateLog(); 1901 } 1902 1903 void Manager::updateLog() 1904 { 1905 QWidget * currentWidget = toolsWidget->currentWidget(); 1906 1907 if (currentWidget == setupTab) 1908 ekosLogOut->setPlainText(m_LogText.join("\n")); 1909 else if (currentWidget == alignModule()) 1910 ekosLogOut->setPlainText(alignModule()->getLogText()); 1911 else if (currentWidget == captureModule()) 1912 ekosLogOut->setPlainText(captureModule()->getLogText()); 1913 else if (currentWidget == focusModule()) 1914 ekosLogOut->setPlainText(focusModule()->getLogText()); 1915 else if (currentWidget == guideModule()) 1916 ekosLogOut->setPlainText(guideModule()->getLogText()); 1917 else if (currentWidget == mountModule()) 1918 ekosLogOut->setPlainText(mountModule()->getLogText()); 1919 else if (currentWidget == schedulerModule()) 1920 ekosLogOut->setPlainText(schedulerModule()->getLogText()); 1921 else if (currentWidget == observatoryProcess.get()) 1922 ekosLogOut->setPlainText(observatoryProcess->getLogText()); 1923 else if (currentWidget == analyzeProcess.get()) 1924 ekosLogOut->setPlainText(analyzeProcess->getLogText()); 1925 1926 #ifdef Q_OS_OSX 1927 repaint(); //This is a band-aid for a bug in QT 5.10.0 1928 #endif 1929 } 1930 1931 void Manager::appendLogText(const QString &text) 1932 { 1933 m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", 1934 KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), text)); 1935 1936 qCInfo(KSTARS_EKOS) << text; 1937 1938 emit newLog(text); 1939 1940 updateLog(); 1941 } 1942 1943 void Manager::clearLog() 1944 { 1945 QWidget * currentWidget = toolsWidget->currentWidget(); 1946 1947 if (currentWidget == setupTab) 1948 { 1949 m_LogText.clear(); 1950 updateLog(); 1951 } 1952 else if (currentWidget == alignModule()) 1953 alignModule()->clearLog(); 1954 else if (currentWidget == captureModule()) 1955 captureModule()->clearLog(); 1956 else if (currentWidget == focusModule()) 1957 focusModule()->clearLog(); 1958 else if (currentWidget == guideModule()) 1959 guideModule()->clearLog(); 1960 else if (currentWidget == mountModule()) 1961 mountModule()->clearLog(); 1962 else if (currentWidget == schedulerModule()) 1963 schedulerModule()->clearLog(); 1964 else if (currentWidget == observatoryProcess.get()) 1965 observatoryProcess->clearLog(); 1966 else if (currentWidget == analyzeProcess.get()) 1967 analyzeProcess->clearLog(); 1968 } 1969 1970 void Manager::initCapture() 1971 { 1972 if (captureModule() != nullptr) 1973 return; 1974 1975 captureProcess.reset(new Capture()); 1976 1977 emit newModule("Capture"); 1978 1979 // retrieve the meridian flip state machine from the mount module if the module is already present 1980 if (mountModule() != nullptr) 1981 captureModule()->setMeridianFlipState(mountModule()->getMeridianFlipState()); 1982 1983 capturePreview->shareCaptureModule(captureModule()); 1984 int index = addModuleTab(EkosModule::Capture, captureModule(), QIcon(":/icons/ekos_ccd.png")); 1985 toolsWidget->tabBar()->setTabToolTip(index, i18nc("Charge-Coupled Device", "CCD")); 1986 if (Options::ekosLeftIcons()) 1987 { 1988 QTransform trans; 1989 trans.rotate(90); 1990 QIcon icon = toolsWidget->tabIcon(index); 1991 QPixmap pix = icon.pixmap(QSize(48, 48)); 1992 icon = QIcon(pix.transformed(trans)); 1993 toolsWidget->setTabIcon(index, icon); 1994 } 1995 connect(captureModule(), &Ekos::Capture::newLog, this, &Ekos::Manager::updateLog); 1996 connect(captureModule(), &Ekos::Capture::newLog, ekosLiveClient.get()->message(), 1997 [this]() 1998 { 1999 QJsonObject cStatus = 2000 { 2001 {"log", captureModule()->getLogText()} 2002 }; 2003 2004 ekosLiveClient.get()->message()->updateCaptureStatus(cStatus); 2005 }); 2006 connect(captureModule(), &Ekos::Capture::newStatus, this, &Ekos::Manager::updateCaptureStatus); 2007 connect(captureModule(), &Ekos::Capture::newImage, this, &Ekos::Manager::updateCaptureProgress); 2008 connect(captureModule(), &Ekos::Capture::driverTimedout, this, &Ekos::Manager::restartDriver); 2009 connect(captureModule(), &Ekos::Capture::newExposureProgress, this, &Ekos::Manager::updateExposureProgress); 2010 capturePreview->setEnabled(true); 2011 2012 // display capture status changes 2013 connect(captureModule(), &Ekos::Capture::newFilterStatus, capturePreview->captureStatusWidget, 2014 &LedStatusWidget::setFilterState); 2015 2016 // display target drift 2017 connect(schedulerModule(), &Ekos::Scheduler::targetDistance, 2018 captureModule(), &Ekos::Capture::updateTargetDistance, Qt::UniqueConnection); 2019 connect(schedulerModule(), &Ekos::Scheduler::targetDistance, this, [this](double distance) 2020 { 2021 capturePreview->updateTargetDistance(distance); 2022 }); 2023 2024 2025 connectModules(); 2026 } 2027 2028 void Manager::initAlign() 2029 { 2030 if (alignModule() != nullptr) 2031 return; 2032 2033 alignProcess.reset(new Ekos::Align(m_CurrentProfile)); 2034 2035 emit newModule("Align"); 2036 2037 int index = addModuleTab(EkosModule::Align, alignModule(), QIcon(":/icons/ekos_align.png")); 2038 toolsWidget->tabBar()->setTabToolTip(index, i18n("Align")); 2039 connect(alignModule(), &Ekos::Align::newLog, this, &Ekos::Manager::updateLog); 2040 if (Options::ekosLeftIcons()) 2041 { 2042 QTransform trans; 2043 trans.rotate(90); 2044 QIcon icon = toolsWidget->tabIcon(index); 2045 QPixmap pix = icon.pixmap(QSize(48, 48)); 2046 icon = QIcon(pix.transformed(trans)); 2047 toolsWidget->setTabIcon(index, icon); 2048 } 2049 2050 connectModules(); 2051 } 2052 2053 void Manager::initFocus() 2054 { 2055 if (focusModule() != nullptr) 2056 return; 2057 2058 focusProcess.reset(new Ekos::Focus()); 2059 2060 emit newModule("Focus"); 2061 2062 int index = addModuleTab(EkosModule::Focus, focusModule(), QIcon(":/icons/ekos_focus.png")); 2063 2064 toolsWidget->tabBar()->setTabToolTip(index, i18n("Focus")); 2065 2066 // Focus <---> Manager connections 2067 connect(focusModule(), &Ekos::Focus::newLog, this, &Ekos::Manager::updateLog); 2068 connect(focusModule(), &Ekos::Focus::newStatus, this, &Ekos::Manager::updateFocusStatus); 2069 connect(focusModule(), &Ekos::Focus::newStarPixmap, focusManager, &Ekos::FocusManager::updateFocusStarPixmap); 2070 connect(focusModule(), &Ekos::Focus::newHFR, this, &Ekos::Manager::updateCurrentHFR); 2071 connect(focusModule(), &Ekos::Focus::focuserTimedout, this, &Ekos::Manager::restartDriver); 2072 2073 // connect HFR plot widget 2074 connect(focusModule(), &Ekos::Focus::initHFRPlot, focusManager->hfrVPlot, &FocusHFRVPlot::init); 2075 connect(focusModule(), &Ekos::Focus::redrawHFRPlot, focusManager->hfrVPlot, &FocusHFRVPlot::redraw); 2076 connect(focusModule(), &Ekos::Focus::newHFRPlotPosition, focusManager->hfrVPlot, &FocusHFRVPlot::addPosition); 2077 connect(focusModule(), &Ekos::Focus::drawPolynomial, focusManager->hfrVPlot, &FocusHFRVPlot::drawPolynomial); 2078 connect(focusModule(), &Ekos::Focus::setTitle, focusManager->hfrVPlot, &FocusHFRVPlot::setTitle); 2079 connect(focusModule(), &Ekos::Focus::finalUpdates, focusManager->hfrVPlot, &FocusHFRVPlot::finalUpdates); 2080 connect(focusModule(), &Ekos::Focus::minimumFound, focusManager->hfrVPlot, &FocusHFRVPlot::drawMinimum); 2081 // setup signal/slots for Linear 1 Pass focus algo 2082 connect(focusModule(), &Ekos::Focus::drawCurve, focusManager->hfrVPlot, &FocusHFRVPlot::drawCurve); 2083 connect(focusModule(), &Ekos::Focus::drawCFZ, focusManager->hfrVPlot, &FocusHFRVPlot::drawCFZ); 2084 2085 if (Options::ekosLeftIcons()) 2086 { 2087 QTransform trans; 2088 trans.rotate(90); 2089 QIcon icon = toolsWidget->tabIcon(index); 2090 QPixmap pix = icon.pixmap(QSize(48, 48)); 2091 icon = QIcon(pix.transformed(trans)); 2092 toolsWidget->setTabIcon(index, icon); 2093 } 2094 2095 focusManager->init(); 2096 focusManager->setEnabled(true); 2097 2098 for (auto &oneDevice : INDIListener::devices()) 2099 { 2100 auto prop1 = oneDevice->getProperty("CCD_TEMPERATURE"); 2101 auto prop2 = oneDevice->getProperty("FOCUSER_TEMPERATURE"); 2102 auto prop3 = oneDevice->getProperty("WEATHER_PARAMETERS"); 2103 if (prop1 || prop2 || prop3) 2104 focusModule()->addTemperatureSource(oneDevice); 2105 } 2106 2107 connectModules(); 2108 } 2109 2110 void Manager::updateCurrentHFR(double newHFR, int position, bool inAutofocus) 2111 { 2112 Q_UNUSED(inAutofocus); 2113 focusManager->updateCurrentHFR(newHFR); 2114 2115 QJsonObject cStatus = 2116 { 2117 {"hfr", newHFR}, 2118 {"pos", position} 2119 }; 2120 2121 ekosLiveClient.get()->message()->updateFocusStatus(cStatus); 2122 } 2123 2124 void Manager::updateSigmas(double ra, double de) 2125 { 2126 guideManager->updateSigmas(ra, de); 2127 2128 QJsonObject cStatus = { {"rarms", ra}, {"derms", de} }; 2129 2130 ekosLiveClient.get()->message()->updateGuideStatus(cStatus); 2131 } 2132 2133 void Manager::initMount() 2134 { 2135 if (mountModule() != nullptr) 2136 return; 2137 2138 mountProcess.reset(new Ekos::Mount()); 2139 2140 // share the meridian flip state with capture if the module is already present 2141 if (captureModule() != nullptr) 2142 captureModule()->setMeridianFlipState(mountModule()->getMeridianFlipState()); 2143 2144 emit newModule("Mount"); 2145 2146 int index = addModuleTab(EkosModule::Mount, mountModule(), QIcon(":/icons/ekos_mount.png")); 2147 2148 toolsWidget->tabBar()->setTabToolTip(index, i18n("Mount")); 2149 connect(mountModule(), &Ekos::Mount::newLog, this, &Ekos::Manager::updateLog); 2150 connect(mountModule(), &Ekos::Mount::newCoords, this, &Ekos::Manager::updateMountCoords); 2151 connect(mountModule(), &Ekos::Mount::newStatus, this, &Ekos::Manager::updateMountStatus); 2152 connect(mountModule(), &Ekos::Mount::newTargetName, this, [this](const QString & name) 2153 { 2154 setTarget(name); 2155 }); 2156 connect(mountModule(), &Ekos::Mount::pierSideChanged, this, [&](ISD::Mount::PierSide side) 2157 { 2158 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"pierSide", side}})); 2159 }); 2160 connect(mountModule()->getMeridianFlipState().get(), 2161 &Ekos::MeridianFlipState::newMountMFStatus, [&](MeridianFlipState::MeridianFlipMountState status) 2162 { 2163 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject( 2164 { 2165 {"meridianFlipStatus", status}, 2166 })); 2167 }); 2168 connect(mountModule()->getMeridianFlipState().get(), 2169 &Ekos::MeridianFlipState::newMeridianFlipMountStatusText, [&](const QString & text) 2170 { 2171 // Throttle this down 2172 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject( 2173 { 2174 {"meridianFlipText", text}, 2175 }), mountModule()->getMeridianFlipState()->getMeridianFlipMountState() == MeridianFlipState::MOUNT_FLIP_NONE); 2176 meridianFlipStatusWidget->setStatus(text); 2177 }); 2178 connect(mountModule(), &Ekos::Mount::autoParkCountdownUpdated, this, [&](const QString & text) 2179 { 2180 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"autoParkCountdown", text}}), true); 2181 }); 2182 2183 connect(mountModule(), &Ekos::Mount::trainChanged, ekosLiveClient.get()->message(), 2184 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection); 2185 2186 connect(mountModule(), &Ekos::Mount::slewRateChanged, this, [&](int slewRate) 2187 { 2188 QJsonObject status = { { "slewRate", slewRate} }; 2189 ekosLiveClient.get()->message()->updateMountStatus(status); 2190 }); 2191 2192 if (Options::ekosLeftIcons()) 2193 { 2194 QTransform trans; 2195 trans.rotate(90); 2196 QIcon icon = toolsWidget->tabIcon(index); 2197 QPixmap pix = icon.pixmap(QSize(48, 48)); 2198 icon = QIcon(pix.transformed(trans)); 2199 toolsWidget->setTabIcon(index, icon); 2200 } 2201 2202 mountGroup->setEnabled(true); 2203 capturePreview->shareMountModule(mountModule()); 2204 2205 connectModules(); 2206 } 2207 2208 void Manager::initGuide() 2209 { 2210 if (guideModule() == nullptr) 2211 { 2212 guideProcess.reset(new Ekos::Guide()); 2213 2214 emit newModule("Guide"); 2215 } 2216 2217 if (toolsWidget->indexOf(guideModule()) == -1) 2218 { 2219 // if (managedDevices.contains(KSTARS_TELESCOPE) && managedDevices.value(KSTARS_TELESCOPE)->isConnected()) 2220 // guideProcess->addMount(managedDevices.value(KSTARS_TELESCOPE)); 2221 2222 int index = addModuleTab(EkosModule::Guide, guideModule(), QIcon(":/icons/ekos_guide.png")); 2223 toolsWidget->tabBar()->setTabToolTip(index, i18n("Guide")); 2224 connect(guideModule(), &Ekos::Guide::newLog, this, &Ekos::Manager::updateLog); 2225 connect(guideModule(), &Ekos::Guide::driverTimedout, this, &Ekos::Manager::restartDriver); 2226 2227 guideManager->setEnabled(true); 2228 2229 connect(guideModule(), &Ekos::Guide::newStatus, this, &Ekos::Manager::updateGuideStatus); 2230 connect(guideModule(), &Ekos::Guide::newStarPixmap, guideManager, &Ekos::GuideManager::updateGuideStarPixmap); 2231 connect(guideModule(), &Ekos::Guide::newAxisSigma, this, &Ekos::Manager::updateSigmas); 2232 connect(guideModule(), &Ekos::Guide::newAxisDelta, [&](double ra, double de) 2233 { 2234 QJsonObject status = { { "drift_ra", ra}, {"drift_de", de} }; 2235 ekosLiveClient.get()->message()->updateGuideStatus(status); 2236 }); 2237 2238 if (Options::ekosLeftIcons()) 2239 { 2240 QTransform trans; 2241 trans.rotate(90); 2242 QIcon icon = toolsWidget->tabIcon(index); 2243 QPixmap pix = icon.pixmap(QSize(48, 48)); 2244 icon = QIcon(pix.transformed(trans)); 2245 toolsWidget->setTabIcon(index, icon); 2246 } 2247 guideManager->init(guideModule()); 2248 } 2249 2250 connectModules(); 2251 } 2252 2253 void Manager::initObservatory() 2254 { 2255 if (observatoryProcess.get() == nullptr) 2256 { 2257 // Initialize the Observatory Module 2258 observatoryProcess.reset(new Ekos::Observatory()); 2259 2260 emit newModule("Observatory"); 2261 2262 int index = addModuleTab(EkosModule::Observatory, observatoryProcess.get(), QIcon(":/icons/ekos_observatory.png")); 2263 toolsWidget->tabBar()->setTabToolTip(index, i18n("Observatory")); 2264 connect(observatoryProcess.get(), &Ekos::Observatory::newLog, this, &Ekos::Manager::updateLog); 2265 2266 if (Options::ekosLeftIcons()) 2267 { 2268 QTransform trans; 2269 trans.rotate(90); 2270 QIcon icon = toolsWidget->tabIcon(index); 2271 QPixmap pix = icon.pixmap(QSize(48, 48)); 2272 icon = QIcon(pix.transformed(trans)); 2273 toolsWidget->setTabIcon(index, icon); 2274 } 2275 } 2276 } 2277 2278 void Manager::addGuider(ISD::Guider * device) 2279 { 2280 appendLogText(i18n("Guider port from %1 is ready.", device->getDeviceName())); 2281 } 2282 2283 void Manager::removeTabs() 2284 { 2285 disconnect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange); 2286 2287 for (int i = numPermanentTabs; i < toolsWidget->count(); i++) 2288 toolsWidget->removeTab(i); 2289 2290 alignProcess.reset(); 2291 captureProcess.reset(); 2292 focusProcess.reset(); 2293 guideProcess.reset(); 2294 mountProcess.reset(); 2295 observatoryProcess.reset(); 2296 2297 connect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange, Qt::UniqueConnection); 2298 } 2299 2300 bool Manager::isRunning(const QString &process) 2301 { 2302 QProcess ps; 2303 #ifdef Q_OS_OSX 2304 ps.start("pgrep", QStringList() << process); 2305 ps.waitForFinished(); 2306 QString output = ps.readAllStandardOutput(); 2307 return output.length() > 0; 2308 #else 2309 ps.start("ps", QStringList() << "-o" 2310 << "comm" 2311 << "--no-headers" 2312 << "-C" << process); 2313 ps.waitForFinished(); 2314 QString output = ps.readAllStandardOutput(); 2315 return output.contains(process); 2316 #endif 2317 } 2318 2319 void Manager::addObjectToScheduler(SkyObject * object) 2320 { 2321 if (schedulerModule() != nullptr) 2322 schedulerModule()->addObject(object); 2323 } 2324 2325 QString Manager::getCurrentJobName() 2326 { 2327 return schedulerModule()->getCurrentJobName(); 2328 } 2329 2330 bool Manager::setProfile(const QString &profileName) 2331 { 2332 int index = profileCombo->findText(profileName); 2333 2334 if (index < 0) 2335 return false; 2336 2337 profileCombo->setCurrentIndex(index); 2338 2339 return true; 2340 } 2341 2342 void Manager::editNamedProfile(const QJsonObject &profileInfo) 2343 { 2344 ProfileEditor editor(this); 2345 setProfile(profileInfo["name"].toString()); 2346 if (getCurrentProfile(m_CurrentProfile)) 2347 { 2348 editor.setPi(m_CurrentProfile); 2349 editor.setSettings(profileInfo); 2350 editor.saveProfile(); 2351 } 2352 } 2353 2354 void Manager::addNamedProfile(const QJsonObject &profileInfo) 2355 { 2356 ProfileEditor editor(this); 2357 2358 editor.setSettings(profileInfo); 2359 editor.saveProfile(); 2360 profiles.clear(); 2361 loadProfiles(); 2362 profileCombo->setCurrentIndex(profileCombo->count() - 1); 2363 getCurrentProfile(m_CurrentProfile); 2364 } 2365 2366 void Manager::deleteNamedProfile(const QString &name) 2367 { 2368 if (!getCurrentProfile(m_CurrentProfile)) 2369 return; 2370 2371 for (auto &pi : profiles) 2372 { 2373 // Do not delete an actively running profile 2374 // Do not delete simulator profile 2375 if (pi->name == "Simulators" || pi->name != name || (pi.get() == m_CurrentProfile && ekosStatus() != Idle)) 2376 continue; 2377 2378 KStarsData::Instance()->userdb()->PurgeProfile(pi); 2379 profiles.clear(); 2380 loadProfiles(); 2381 getCurrentProfile(m_CurrentProfile); 2382 return; 2383 } 2384 } 2385 2386 QJsonObject Manager::getNamedProfile(const QString &name) 2387 { 2388 QJsonObject profileInfo; 2389 2390 // Get current profile 2391 for (auto &pi : profiles) 2392 { 2393 if (name == pi->name) 2394 return pi->toJson(); 2395 } 2396 2397 return QJsonObject(); 2398 } 2399 2400 QStringList Manager::getProfiles() 2401 { 2402 QStringList profiles; 2403 2404 for (int i = 0; i < profileCombo->count(); i++) 2405 profiles << profileCombo->itemText(i); 2406 2407 return profiles; 2408 } 2409 2410 void Manager::addProfile() 2411 { 2412 ProfileEditor editor(this); 2413 2414 if (editor.exec() == QDialog::Accepted) 2415 { 2416 profiles.clear(); 2417 loadProfiles(); 2418 profileCombo->setCurrentIndex(profileCombo->count() - 1); 2419 } 2420 2421 getCurrentProfile(m_CurrentProfile); 2422 } 2423 2424 void Manager::editProfile() 2425 { 2426 ProfileEditor editor(this); 2427 2428 if (getCurrentProfile(m_CurrentProfile)) 2429 { 2430 2431 editor.setPi(m_CurrentProfile); 2432 2433 if (editor.exec() == QDialog::Accepted) 2434 { 2435 int currentIndex = profileCombo->currentIndex(); 2436 2437 profiles.clear(); 2438 loadProfiles(); 2439 profileCombo->setCurrentIndex(currentIndex); 2440 } 2441 2442 getCurrentProfile(m_CurrentProfile); 2443 } 2444 } 2445 2446 void Manager::deleteProfile() 2447 { 2448 if (!getCurrentProfile(m_CurrentProfile)) 2449 return; 2450 2451 if (m_CurrentProfile->name == "Simulators") 2452 return; 2453 2454 auto executeDeleteProfile = [&]() 2455 { 2456 KStarsData::Instance()->userdb()->PurgeProfile(m_CurrentProfile); 2457 profiles.clear(); 2458 loadProfiles(); 2459 getCurrentProfile(m_CurrentProfile); 2460 }; 2461 2462 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, executeDeleteProfile]() 2463 { 2464 //QObject::disconnect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, nullptr); 2465 KSMessageBox::Instance()->disconnect(this); 2466 executeDeleteProfile(); 2467 }); 2468 2469 KSMessageBox::Instance()->questionYesNo(i18n("Are you sure you want to delete the profile?"), 2470 i18n("Confirm Delete")); 2471 2472 } 2473 2474 void Manager::wizardProfile() 2475 { 2476 ProfileWizard wz; 2477 if (wz.exec() != QDialog::Accepted) 2478 return; 2479 2480 ProfileEditor editor(this); 2481 2482 editor.setProfileName(wz.profileName); 2483 editor.setAuxDrivers(wz.selectedAuxDrivers()); 2484 if (wz.useInternalServer == false) 2485 editor.setHostPort(wz.host, wz.port); 2486 editor.setWebManager(wz.useWebManager); 2487 editor.setGuiderType(wz.selectedExternalGuider()); 2488 // Disable connection options 2489 editor.setConnectionOptionsEnabled(false); 2490 2491 if (editor.exec() == QDialog::Accepted) 2492 { 2493 profiles.clear(); 2494 loadProfiles(); 2495 profileCombo->setCurrentIndex(profileCombo->count() - 1); 2496 } 2497 2498 getCurrentProfile(m_CurrentProfile); 2499 } 2500 2501 bool Manager::getCurrentProfile(QSharedPointer<ProfileInfo> &profile) const 2502 { 2503 // Get current profile 2504 for (auto &pi : profiles) 2505 { 2506 if (profileCombo->currentText() == pi->name) 2507 { 2508 profile = pi; 2509 return true; 2510 } 2511 } 2512 2513 return false; 2514 } 2515 2516 void Manager::updateProfileLocation(const QSharedPointer<ProfileInfo> &profile) 2517 { 2518 if (profile->city.isEmpty() == false) 2519 { 2520 bool cityFound = KStars::Instance()->setGeoLocation(profile->city, profile->province, profile->country); 2521 if (cityFound) 2522 appendLogText(i18n("Site location updated to %1.", KStarsData::Instance()->geo()->fullName())); 2523 else 2524 appendLogText(i18n("Failed to update site location to %1. City not found.", 2525 KStarsData::Instance()->geo()->fullName())); 2526 } 2527 } 2528 2529 void Manager::updateMountStatus(ISD::Mount::Status status) 2530 { 2531 static ISD::Mount::Status lastStatus = ISD::Mount::MOUNT_IDLE; 2532 2533 if (status == lastStatus) 2534 return; 2535 2536 lastStatus = status; 2537 2538 mountStatus->setMountState(mountModule()->statusString(), status); 2539 mountStatus->setStyleSheet(QString()); 2540 2541 QJsonObject cStatus = 2542 { 2543 {"status", mountModule()->statusString(false)} 2544 }; 2545 2546 ekosLiveClient.get()->message()->updateMountStatus(cStatus); 2547 } 2548 2549 void Manager::updateMountCoords(const SkyPoint position, ISD::Mount::PierSide pierSide, const dms &ha) 2550 { 2551 Q_UNUSED(pierSide) 2552 raOUT->setText(position.ra().toHMSString()); 2553 decOUT->setText(position.dec().toDMSString()); 2554 azOUT->setText(position.az().toDMSString()); 2555 altOUT->setText(position.alt().toDMSString()); 2556 2557 QJsonObject cStatus = 2558 { 2559 {"ra", dms::fromString(raOUT->text(), false).Degrees()}, 2560 {"de", dms::fromString(decOUT->text(), true).Degrees()}, 2561 {"ra0", position.ra0().Degrees()}, 2562 {"de0", position.dec0().Degrees()}, 2563 {"az", dms::fromString(azOUT->text(), true).Degrees()}, 2564 {"at", dms::fromString(altOUT->text(), true).Degrees()}, 2565 {"ha", ha.Degrees()}, 2566 }; 2567 2568 ekosLiveClient.get()->message()->updateMountStatus(cStatus, true); 2569 } 2570 2571 void Manager::updateCaptureStatus(Ekos::CaptureState status) 2572 { 2573 capturePreview->updateCaptureStatus(status); 2574 2575 switch (status) 2576 { 2577 case Ekos::CAPTURE_IDLE: 2578 /* Fall through */ 2579 case Ekos::CAPTURE_ABORTED: 2580 /* Fall through */ 2581 case Ekos::CAPTURE_COMPLETE: 2582 m_CountdownTimer.stop(); 2583 break; 2584 case Ekos::CAPTURE_CAPTURING: 2585 m_CountdownTimer.start(); 2586 break; 2587 default: 2588 break; 2589 } 2590 2591 QJsonObject cStatus = 2592 { 2593 {"status", captureStates[status]}, 2594 {"seqt", capturePreview->captureCountsWidget->sequenceRemainingTime->text()}, 2595 {"ovt", capturePreview->captureCountsWidget->overallRemainingTime->text()} 2596 }; 2597 2598 ekosLiveClient.get()->message()->updateCaptureStatus(cStatus); 2599 } 2600 2601 void Manager::updateCaptureProgress(Ekos::SequenceJob * job, const QSharedPointer<FITSData> &data) 2602 { 2603 capturePreview->updateJobProgress(job, data); 2604 2605 QJsonObject status = 2606 { 2607 {"seqv", job->getCompleted()}, 2608 {"seqr", job->getCoreProperty(SequenceJob::SJ_Count).toInt()}, 2609 {"seql", capturePreview->captureCountsWidget->sequenceRemainingTime->text()} 2610 }; 2611 2612 ekosLiveClient.get()->message()->updateCaptureStatus(status); 2613 2614 //const QString filename = ; 2615 //if (!filename.isEmpty() && job->getStatus() == SequenceJob::JOB_BUSY) 2616 if (data && job->getStatus() == JOB_BUSY) 2617 { 2618 QString uuid = QUuid::createUuid().toString(); 2619 uuid = uuid.remove(QRegularExpression("[-{}]")); 2620 2621 // Normally FITS Viewer would trigger an upload 2622 // If off, then rely on summary view or raw data 2623 if (Options::useFITSViewer() == false) 2624 { 2625 if (Options::useSummaryPreview()) 2626 ekosLiveClient.get()->media()->sendView(m_SummaryView, uuid); 2627 2628 else 2629 ekosLiveClient.get()->media()->sendData(data, uuid); 2630 } 2631 2632 if (job->jobType() != SequenceJob::JOBTYPE_PREVIEW) 2633 ekosLiveClient.get()->cloud()->upload(data, uuid); 2634 2635 } 2636 } 2637 2638 void Manager::updateExposureProgress(Ekos::SequenceJob * job) 2639 { 2640 QJsonObject status 2641 { 2642 {"expv", job->getExposeLeft()}, 2643 {"expr", job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble()} 2644 }; 2645 2646 ekosLiveClient.get()->message()->updateCaptureStatus(status); 2647 } 2648 2649 void Manager::updateCaptureCountDown() 2650 { 2651 capturePreview->updateCaptureCountDown(-1); 2652 2653 QJsonObject status = 2654 { 2655 {"seqt", capturePreview->captureCountsWidget->sequenceRemainingTime->text()}, 2656 {"ovt", capturePreview->captureCountsWidget->overallRemainingTime->text()}, 2657 {"ovp", capturePreview->captureCountsWidget->gr_overallProgressBar->value()}, 2658 {"ovl", capturePreview->captureCountsWidget->gr_overallLabel->text()} 2659 }; 2660 2661 ekosLiveClient.get()->message()->updateCaptureStatus(status); 2662 } 2663 2664 2665 void Manager::updateFocusStatus(Ekos::FocusState status) 2666 { 2667 focusManager->updateFocusStatus(status); 2668 2669 QJsonObject cStatus = 2670 { 2671 {"status", getFocusStatusString(status, false)} 2672 }; 2673 2674 ekosLiveClient.get()->message()->updateFocusStatus(cStatus); 2675 } 2676 2677 void Manager::updateGuideStatus(Ekos::GuideState status) 2678 { 2679 guideManager->updateGuideStatus(status); 2680 QJsonObject cStatus = 2681 { 2682 {"status", getGuideStatusString(status, false)} 2683 }; 2684 2685 ekosLiveClient.get()->message()->updateGuideStatus(cStatus); 2686 } 2687 2688 void Manager::setTarget(const QString &name) 2689 { 2690 capturePreview->targetLabel->setVisible(!name.isEmpty()); 2691 capturePreview->mountTarget->setVisible(!name.isEmpty()); 2692 capturePreview->mountTarget->setText(name); 2693 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"target", name}})); 2694 } 2695 2696 void Manager::showEkosOptions() 2697 { 2698 QWidget * currentWidget = toolsWidget->currentWidget(); 2699 2700 if (alignModule() && alignModule() == currentWidget) 2701 { 2702 KConfigDialog * alignSettings = KConfigDialog::exists("alignsettings"); 2703 if (alignSettings) 2704 { 2705 alignSettings->setEnabled(true); 2706 alignSettings->show(); 2707 } 2708 return; 2709 } 2710 2711 if (guideModule() && guideModule() == currentWidget) 2712 { 2713 KConfigDialog::showDialog("guidesettings"); 2714 return; 2715 } 2716 2717 if (focusModule() && focusModule() == currentWidget) 2718 { 2719 KConfigDialog * focusSettings = KConfigDialog::exists("focussettings"); 2720 if (focusSettings) 2721 { 2722 focusSettings->show(); 2723 focusSettings->raise(); 2724 } 2725 return; 2726 } 2727 2728 if ((captureModule() && captureModule() == currentWidget) || 2729 (schedulerModule() && schedulerModule() == currentWidget)) 2730 { 2731 if (opsEkos) 2732 { 2733 // Scheduler is tab 1, Capture is tab 2. 2734 const int index = schedulerModule() == currentWidget ? 1 : 2; 2735 opsEkos->setCurrentIndex(index); 2736 } 2737 KConfigDialog * cDialog = KConfigDialog::exists("settings"); 2738 if (cDialog) 2739 { 2740 cDialog->setCurrentPage(ekosOptionsWidget); 2741 cDialog->show(); 2742 cDialog->raise(); // for MacOS 2743 cDialog->activateWindow(); // for Windows 2744 } 2745 return; 2746 } 2747 2748 if (ekosOptionsWidget == nullptr) 2749 { 2750 optionsB->click(); 2751 } 2752 else if (KConfigDialog::showDialog("settings")) 2753 { 2754 KConfigDialog * cDialog = KConfigDialog::exists("settings"); 2755 if (cDialog) 2756 { 2757 cDialog->setCurrentPage(ekosOptionsWidget); 2758 cDialog->show(); 2759 cDialog->raise(); // for MacOS 2760 cDialog->activateWindow(); // for Windows 2761 } 2762 } 2763 } 2764 2765 void Manager::updateDebugInterfaces() 2766 { 2767 KSUtils::Logging::SyncFilterRules(); 2768 2769 for (auto &device : INDIListener::devices()) 2770 { 2771 auto debugProp = device->getProperty("DEBUG"); 2772 if (!debugProp) 2773 continue; 2774 2775 auto debugSP = debugProp.getSwitch(); 2776 2777 // Check if the debug interface matches the driver device class 2778 if ( ( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) && 2779 debugSP->sp[0].s != ISS_ON) 2780 { 2781 debugSP->at(0)->setState(ISS_ON); 2782 debugSP->at(1)->setState(ISS_OFF); 2783 device->sendNewProperty(debugSP); 2784 appendLogText(i18n("Enabling debug logging for %1...", device->getDeviceName())); 2785 } 2786 else if ( !( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) && 2787 debugSP->sp[0].s != ISS_OFF) 2788 { 2789 debugSP->at(0)->setState(ISS_OFF); 2790 debugSP->at(1)->setState(ISS_ON); 2791 device->sendNewProperty(debugSP); 2792 appendLogText(i18n("Disabling debug logging for %1...", device->getDeviceName())); 2793 } 2794 2795 if (opsLogs->isINDISettingsChanged()) 2796 device->setConfig(SAVE_CONFIG); 2797 } 2798 } 2799 2800 void Manager::watchDebugProperty(INDI::Property prop) 2801 { 2802 if (prop.isNameMatch("DEBUG")) 2803 { 2804 auto svp = prop.getSwitch(); 2805 2806 ISD::GenericDevice * deviceInterface = qobject_cast<ISD::GenericDevice *>(sender()); 2807 2808 // We don't process pure general interfaces 2809 if (deviceInterface->getDriverInterface() == INDI::BaseDevice::GENERAL_INTERFACE) 2810 return; 2811 2812 // If debug was turned off, but our logging policy requires it then turn it back on. 2813 // We turn on debug logging if AT LEAST one driver interface is selected by the logging settings 2814 if (svp->s == IPS_OK && svp->sp[0].s == ISS_OFF && 2815 (opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface())) 2816 { 2817 svp->sp[0].s = ISS_ON; 2818 svp->sp[1].s = ISS_OFF; 2819 deviceInterface->sendNewProperty(svp); 2820 appendLogText(i18n("Re-enabling debug logging for %1...", deviceInterface->getDeviceName())); 2821 } 2822 // To turn off debug logging, NONE of the driver interfaces should be enabled in logging settings. 2823 // For example, if we have CCD+FilterWheel device and CCD + Filter Wheel logging was turned on in 2824 // the log settings, then if the user turns off only CCD logging, the debug logging is NOT 2825 // turned off until he turns off Filter Wheel logging as well. 2826 else if (svp->s == IPS_OK && svp->sp[0].s == ISS_ON 2827 && !(opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface())) 2828 { 2829 svp->sp[0].s = ISS_OFF; 2830 svp->sp[1].s = ISS_ON; 2831 deviceInterface->sendNewProperty(svp); 2832 appendLogText(i18n("Re-disabling debug logging for %1...", deviceInterface->getDeviceName())); 2833 } 2834 } 2835 } 2836 2837 void Manager::announceEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event) 2838 { 2839 ekosLiveClient.get()->message()->sendEvent(message, source, event); 2840 } 2841 2842 void Manager::connectModules() 2843 { 2844 // Dark Library 2845 connect(DarkLibrary::Instance(), &DarkLibrary::newImage, ekosLiveClient.get()->media(), 2846 &EkosLive::Media::sendDarkLibraryData, Qt::UniqueConnection); 2847 connect(DarkLibrary::Instance(), &DarkLibrary::trainChanged, ekosLiveClient.get()->message(), 2848 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection); 2849 connect(DarkLibrary::Instance(), &DarkLibrary::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame, 2850 Qt::UniqueConnection); 2851 connect(DarkLibrary::Instance(), &DarkLibrary::settingsUpdated, ekosLiveClient.get()->message(), 2852 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection); 2853 2854 // Guide <---> Capture connections 2855 if (captureProcess && guideProcess) 2856 { 2857 // captureProcess.get()->disconnect(guideProcess.get()); 2858 // guideProcess.get()->disconnect(captureProcess.get()); 2859 2860 // Guide Limits 2861 connect(guideModule(), &Ekos::Guide::newStatus, captureModule(), &Ekos::Capture::setGuideStatus, 2862 Qt::UniqueConnection); 2863 connect(guideModule(), &Ekos::Guide::newAxisDelta, captureModule(), &Ekos::Capture::setGuideDeviation, 2864 Qt::UniqueConnection); 2865 2866 // Dithering 2867 connect(captureModule(), &Ekos::Capture::newStatus, guideModule(), &Ekos::Guide::setCaptureStatus, 2868 Qt::UniqueConnection); 2869 2870 // Guide Head 2871 connect(captureModule(), &Ekos::Capture::suspendGuiding, guideModule(), &Ekos::Guide::suspend, 2872 Qt::UniqueConnection); 2873 connect(captureModule(), &Ekos::Capture::resumeGuiding, guideModule(), &Ekos::Guide::resume, 2874 Qt::UniqueConnection); 2875 connect(guideModule(), &Ekos::Guide::guideChipUpdated, captureModule(), &Ekos::Capture::setGuideChip, 2876 Qt::UniqueConnection); 2877 2878 // Meridian Flip 2879 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, guideModule(), &Ekos::Guide::abort, 2880 Qt::UniqueConnection); 2881 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, guideModule(), 2882 &Ekos::Guide::guideAfterMeridianFlip, Qt::UniqueConnection); 2883 } 2884 2885 // Guide <---> Mount connections 2886 if (guideProcess && mountProcess) 2887 { 2888 // Parking 2889 connect(mountModule(), &Ekos::Mount::newStatus, guideModule(), &Ekos::Guide::setMountStatus, 2890 Qt::UniqueConnection); 2891 connect(mountModule(), &Ekos::Mount::newCoords, guideModule(), &Ekos::Guide::setMountCoords, 2892 Qt::UniqueConnection); 2893 2894 } 2895 2896 // Focus <---> Guide connections 2897 if (guideProcess && focusProcess) 2898 { 2899 // Suspend 2900 connect(focusModule(), &Ekos::Focus::suspendGuiding, guideModule(), &Ekos::Guide::suspend, Qt::UniqueConnection); 2901 connect(focusModule(), &Ekos::Focus::resumeGuiding, guideModule(), &Ekos::Guide::resume, Qt::UniqueConnection); 2902 } 2903 2904 // Capture <---> Focus connections 2905 if (captureProcess && focusProcess) 2906 { 2907 // Check focus HFR value and if above threshold parameter, run autoFocus 2908 connect(captureModule(), &Ekos::Capture::checkFocus, focusModule(), &Ekos::Focus::checkFocus, 2909 Qt::UniqueConnection); 2910 2911 // Run autoFocus 2912 connect(captureProcess.get(), &Ekos::Capture::runAutoFocus, focusProcess.get(), &Ekos::Focus::runAutoFocus, 2913 Qt::UniqueConnection); 2914 2915 // Reset Focus 2916 connect(captureModule(), &Ekos::Capture::resetFocus, focusModule(), &Ekos::Focus::resetFrame, 2917 Qt::UniqueConnection); 2918 2919 // Abort Focus 2920 connect(captureModule(), &Ekos::Capture::abortFocus, focusModule(), &Ekos::Focus::abort, 2921 Qt::UniqueConnection); 2922 2923 // New Focus Status 2924 connect(focusModule(), &Ekos::Focus::newStatus, captureModule(), &Ekos::Capture::setFocusStatus, 2925 Qt::UniqueConnection); 2926 2927 // Perform adaptive focus 2928 connect(captureModule(), &Ekos::Capture::adaptiveFocus, focusModule(), &Ekos::Focus::adaptiveFocus, 2929 Qt::UniqueConnection); 2930 2931 // New Adaptive Focus Status 2932 connect(focusModule(), &Ekos::Focus::focusAdaptiveComplete, captureModule(), 2933 &Ekos::Capture::focusAdaptiveComplete, 2934 Qt::UniqueConnection); 2935 2936 // New Focus HFR 2937 connect(focusModule(), &Ekos::Focus::newHFR, captureModule(), &Ekos::Capture::setHFR, Qt::UniqueConnection); 2938 2939 // New Focus temperature delta 2940 connect(focusModule(), &Ekos::Focus::newFocusTemperatureDelta, captureModule(), 2941 &Ekos::Capture::setFocusTemperatureDelta, Qt::UniqueConnection); 2942 2943 // Meridian Flip 2944 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, focusModule(), &Ekos::Focus::meridianFlipStarted, 2945 Qt::UniqueConnection); 2946 } 2947 2948 // Capture <---> Align connections 2949 if (captureProcess && alignProcess) 2950 { 2951 // Alignment flag 2952 connect(alignModule(), &Ekos::Align::newStatus, captureModule(), &Ekos::Capture::setAlignStatus, 2953 Qt::UniqueConnection); 2954 // Solver data 2955 connect(alignModule(), &Ekos::Align::newSolverResults, captureModule(), &Ekos::Capture::setAlignResults, 2956 Qt::UniqueConnection); 2957 // Capture Status 2958 connect(captureModule(), &Ekos::Capture::newStatus, alignModule(), &Ekos::Align::setCaptureStatus, 2959 Qt::UniqueConnection); 2960 } 2961 2962 // Capture <---> Mount connections 2963 if (captureProcess && mountProcess) 2964 { 2965 // Register both modules since both are now created and ready 2966 // In case one module misses the DBus signal, then it will be correctly initialized. 2967 captureModule()->registerNewModule("Mount"); 2968 mountModule()->registerNewModule("Capture"); 2969 2970 // Meridian Flip states 2971 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, mountModule(), &Ekos::Mount::suspendAltLimits, 2972 Qt::UniqueConnection); 2973 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, mountModule(), &Ekos::Mount::resumeAltLimits, 2974 Qt::UniqueConnection); 2975 2976 // Mount Status 2977 connect(mountModule(), &Ekos::Mount::newStatus, captureModule(), &Ekos::Capture::setMountStatus, 2978 Qt::UniqueConnection); 2979 } 2980 2981 // Optical Train Manager ---> EkosLive connections 2982 if (ekosLiveClient) 2983 { 2984 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated, ekosLiveClient->message(), 2985 &EkosLive::Message::sendTrains, Qt::UniqueConnection); 2986 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::configurationRequested, ekosLiveClient->message(), 2987 &EkosLive::Message::requestOpticalTrains, Qt::UniqueConnection); 2988 } 2989 2990 // Capture <---> EkosLive connections 2991 if (captureProcess && ekosLiveClient) 2992 { 2993 //captureProcess.get()->disconnect(ekosLiveClient.get()->message()); 2994 2995 connect(captureModule(), &Ekos::Capture::dslrInfoRequested, ekosLiveClient.get()->message(), 2996 &EkosLive::Message::requestDSLRInfo, Qt::UniqueConnection); 2997 connect(captureModule(), &Ekos::Capture::sequenceChanged, ekosLiveClient.get()->message(), 2998 &EkosLive::Message::sendCaptureSequence, Qt::UniqueConnection); 2999 connect(captureModule(), &Ekos::Capture::settingsUpdated, ekosLiveClient.get()->message(), 3000 &EkosLive::Message::sendCaptureSettings, Qt::UniqueConnection); 3001 connect(captureModule(), &Ekos::Capture::newLocalPreview, ekosLiveClient.get()->message(), 3002 &EkosLive::Message::sendPreviewLabel, Qt::UniqueConnection); 3003 connect(captureModule(), &Ekos::Capture::trainChanged, ekosLiveClient.get()->message(), 3004 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection); 3005 } 3006 3007 // Focus <---> Align connections 3008 if (focusProcess && alignProcess) 3009 { 3010 connect(focusModule(), &Ekos::Focus::newStatus, alignModule(), &Ekos::Align::setFocusStatus, 3011 Qt::UniqueConnection); 3012 } 3013 3014 // Focus <---> Mount connections 3015 if (focusProcess && mountProcess) 3016 { 3017 connect(mountModule(), &Ekos::Mount::newStatus, focusModule(), &Ekos::Focus::setMountStatus, 3018 Qt::UniqueConnection); 3019 connect(mountModule(), &Ekos::Mount::newCoords, focusModule(), &Ekos::Focus::setMountCoords, 3020 Qt::UniqueConnection); 3021 } 3022 3023 // Mount <---> Align connections 3024 if (mountProcess && alignProcess) 3025 { 3026 connect(mountModule(), &Ekos::Mount::newStatus, alignModule(), &Ekos::Align::setMountStatus, 3027 Qt::UniqueConnection); 3028 connect(mountModule(), &Ekos::Mount::newTarget, alignModule(), &Ekos::Align::setTarget, 3029 Qt::UniqueConnection); 3030 connect(mountModule(), &Ekos::Mount::newCoords, alignModule(), &Ekos::Align::setTelescopeCoordinates, 3031 Qt::UniqueConnection); 3032 connect(alignModule(), &Ekos::Align::newPAAStage, mountModule(), &Ekos::Mount::paaStageChanged, 3033 Qt::UniqueConnection); 3034 } 3035 3036 // Mount <---> Guide connections 3037 if (mountProcess && guideProcess) 3038 { 3039 connect(mountModule(), &Ekos::Mount::pierSideChanged, guideModule(), &Ekos::Guide::setPierSide, 3040 Qt::UniqueConnection); 3041 } 3042 3043 // Align <--> EkosLive connections 3044 if (alignProcess && ekosLiveClient) 3045 { 3046 // alignProcess.get()->disconnect(ekosLiveClient.get()->message()); 3047 // alignProcess.get()->disconnect(ekosLiveClient.get()->media()); 3048 3049 connect(alignModule(), &Ekos::Align::newStatus, ekosLiveClient.get()->message(), &EkosLive::Message::setAlignStatus, 3050 Qt::UniqueConnection); 3051 connect(alignModule(), &Ekos::Align::newSolution, ekosLiveClient.get()->message(), 3052 &EkosLive::Message::setAlignSolution, Qt::UniqueConnection); 3053 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHStage, 3054 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHStage, 3055 Qt::UniqueConnection); 3056 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHMessage, 3057 ekosLiveClient.get()->message(), 3058 &EkosLive::Message::setPAHMessage, Qt::UniqueConnection); 3059 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::PAHEnabled, 3060 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHEnabled, 3061 Qt::UniqueConnection); 3062 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::polarResultUpdated, 3063 ekosLiveClient.get()->message(), 3064 &EkosLive::Message::setPolarResults, Qt::UniqueConnection); 3065 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::updatedErrorsChanged, 3066 ekosLiveClient.get()->message(), 3067 &EkosLive::Message::setUpdatedErrors, Qt::UniqueConnection); 3068 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newCorrectionVector, 3069 ekosLiveClient.get()->media(), 3070 &EkosLive::Media::setCorrectionVector, Qt::UniqueConnection); 3071 3072 connect(alignModule(), &Ekos::Align::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame, 3073 Qt::UniqueConnection); 3074 connect(alignModule(), &Ekos::Align::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendUpdatedFrame, 3075 Qt::UniqueConnection); 3076 3077 connect(alignModule(), &Ekos::Align::settingsUpdated, ekosLiveClient.get()->message(), 3078 &EkosLive::Message::sendAlignSettings, Qt::UniqueConnection); 3079 3080 connect(alignModule(), &Ekos::Align::trainChanged, ekosLiveClient.get()->message(), 3081 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection); 3082 3083 connect(alignModule(), &Ekos::Align::manualRotatorChanged, ekosLiveClient.get()->message(), 3084 &EkosLive::Message::sendManualRotatorStatus, Qt::UniqueConnection); 3085 } 3086 3087 // Focus <--> EkosLive Connections 3088 if (focusProcess && ekosLiveClient) 3089 { 3090 connect(focusModule(), &Ekos::Focus::settingsUpdated, ekosLiveClient.get()->message(), 3091 &EkosLive::Message::sendFocusSettings, Qt::UniqueConnection); 3092 3093 connect(focusModule(), &Ekos::Focus::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame, 3094 Qt::UniqueConnection); 3095 3096 connect(focusModule(), &Ekos::Focus::trainChanged, ekosLiveClient.get()->message(), 3097 &EkosLive::Message::sendTrainProfiles, 3098 Qt::UniqueConnection); 3099 3100 connect(focusModule(), &Ekos::Focus::autofocusAborted, 3101 ekosLiveClient.get()->message(), &EkosLive::Message::autofocusAborted, Qt::UniqueConnection); 3102 } 3103 3104 // Guide <--> EkosLive Connections 3105 if (guideProcess && ekosLiveClient) 3106 { 3107 connect(guideModule(), &Ekos::Guide::settingsUpdated, ekosLiveClient.get()->message(), 3108 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection); 3109 3110 connect(guideModule(), &Ekos::Guide::trainChanged, ekosLiveClient.get()->message(), 3111 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection); 3112 3113 connect(guideModule(), &Ekos::Guide::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame, 3114 Qt::UniqueConnection); 3115 } 3116 3117 // Analyze connections. 3118 if (analyzeProcess) 3119 { 3120 // Scheduler <---> Analyze 3121 connect(schedulerModule(), &Ekos::Scheduler::jobStarted, 3122 analyzeProcess.get(), &Ekos::Analyze::schedulerJobStarted, Qt::UniqueConnection); 3123 connect(schedulerModule(), &Ekos::Scheduler::jobEnded, 3124 analyzeProcess.get(), &Ekos::Analyze::schedulerJobEnded, Qt::UniqueConnection); 3125 connect(schedulerModule(), &Ekos::Scheduler::targetDistance, 3126 analyzeProcess.get(), &Ekos::Analyze::newTargetDistance, Qt::UniqueConnection); 3127 3128 // Capture <---> Analyze 3129 if (captureProcess) 3130 { 3131 connect(captureModule(), &Ekos::Capture::captureComplete, 3132 analyzeProcess.get(), &Ekos::Analyze::captureComplete, Qt::UniqueConnection); 3133 connect(captureModule(), &Ekos::Capture::captureStarting, 3134 analyzeProcess.get(), &Ekos::Analyze::captureStarting, Qt::UniqueConnection); 3135 connect(captureModule(), &Ekos::Capture::captureAborted, 3136 analyzeProcess.get(), &Ekos::Analyze::captureAborted, Qt::UniqueConnection); 3137 #if 0 3138 // Meridian Flip 3139 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, 3140 analyzeProcess.get(), &Ekos::Analyze::meridianFlipStarted, Qt::UniqueConnection); 3141 connect(captureModule(), &Ekos::Capture::meridianFlipCompleted, 3142 analyzeProcess.get(), &Ekos::Analyze::meridianFlipComplete, Qt::UniqueConnection); 3143 #endif 3144 } 3145 3146 // Guide <---> Analyze 3147 if (guideProcess) 3148 { 3149 connect(guideModule(), &Ekos::Guide::newStatus, 3150 analyzeProcess.get(), &Ekos::Analyze::guideState, Qt::UniqueConnection); 3151 3152 connect(guideModule(), &Ekos::Guide::guideStats, 3153 analyzeProcess.get(), &Ekos::Analyze::guideStats, Qt::UniqueConnection); 3154 } 3155 } 3156 3157 3158 // Focus <---> Analyze connections 3159 if (focusProcess && analyzeProcess) 3160 { 3161 connect(focusModule(), &Ekos::Focus::autofocusComplete, 3162 analyzeProcess.get(), &Ekos::Analyze::autofocusComplete, Qt::UniqueConnection); 3163 connect(focusModule(), &Ekos::Focus::adaptiveFocusComplete, 3164 analyzeProcess.get(), &Ekos::Analyze::adaptiveFocusComplete, Qt::UniqueConnection); 3165 connect(focusModule(), &Ekos::Focus::autofocusStarting, 3166 analyzeProcess.get(), &Ekos::Analyze::autofocusStarting, Qt::UniqueConnection); 3167 connect(focusModule(), &Ekos::Focus::autofocusAborted, 3168 analyzeProcess.get(), &Ekos::Analyze::autofocusAborted, Qt::UniqueConnection); 3169 connect(focusModule(), &Ekos::Focus::newFocusTemperatureDelta, 3170 analyzeProcess.get(), &Ekos::Analyze::newTemperature, Qt::UniqueConnection); 3171 } 3172 3173 // Align <---> Analyze connections 3174 if (alignProcess && analyzeProcess) 3175 { 3176 connect(alignModule(), &Ekos::Align::newStatus, 3177 analyzeProcess.get(), &Ekos::Analyze::alignState, Qt::UniqueConnection); 3178 3179 } 3180 3181 // Mount <---> Analyze connections 3182 if (mountProcess && analyzeProcess) 3183 { 3184 connect(mountModule(), &Ekos::Mount::newStatus, 3185 analyzeProcess.get(), &Ekos::Analyze::mountState, Qt::UniqueConnection); 3186 connect(mountModule(), &Ekos::Mount::newCoords, 3187 analyzeProcess.get(), &Ekos::Analyze::mountCoords, Qt::UniqueConnection); 3188 connect(mountModule()->getMeridianFlipState().get(), &Ekos::MeridianFlipState::newMountMFStatus, 3189 analyzeProcess.get(), &Ekos::Analyze::mountFlipStatus, Qt::UniqueConnection); 3190 } 3191 } 3192 3193 void Manager::setEkosLiveConnected(bool enabled) 3194 { 3195 ekosLiveClient.get()->setConnected(enabled); 3196 } 3197 3198 void Manager::setEkosLiveConfig(bool rememberCredentials, bool autoConnect) 3199 { 3200 ekosLiveClient.get()->setConfig(rememberCredentials, autoConnect); 3201 } 3202 3203 void Manager::setEkosLiveUser(const QString &username, const QString &password) 3204 { 3205 ekosLiveClient.get()->setUser(username, password); 3206 } 3207 3208 bool Manager::ekosLiveStatus() 3209 { 3210 return ekosLiveClient.get()->isConnected(); 3211 } 3212 3213 void Manager::syncActiveDevices() 3214 { 3215 for (auto oneDevice : INDIListener::devices()) 3216 { 3217 // Find out what ACTIVE_DEVICES properties this driver needs 3218 // and update it from the existing drivers. 3219 auto tvp = oneDevice->getProperty("ACTIVE_DEVICES"); 3220 if (!tvp) 3221 continue; 3222 3223 // Only applicable to non-train devices 3224 for (auto &it : *tvp.getText()) 3225 { 3226 QList<QSharedPointer<ISD::GenericDevice>> devs; 3227 if (it.isNameMatch("ACTIVE_DOME")) 3228 { 3229 devs = INDIListener::devicesByInterface(INDI::BaseDevice::DOME_INTERFACE); 3230 } 3231 else if (it.isNameMatch("ACTIVE_GPS")) 3232 { 3233 devs = INDIListener::devicesByInterface(INDI::BaseDevice::GPS_INTERFACE); 3234 } 3235 3236 if (!devs.empty()) 3237 { 3238 if (it.getText() != devs.first()->getDeviceName()) 3239 { 3240 it.setText(devs.first()->getDeviceName().toLatin1().constData()); 3241 oneDevice->sendNewProperty(tvp.getText()); 3242 } 3243 } 3244 } 3245 } 3246 } 3247 3248 bool Manager::checkUniqueBinaryDriver(const QSharedPointer<DriverInfo> &primaryDriver, 3249 const QSharedPointer<DriverInfo> &secondaryDriver) 3250 { 3251 if (!primaryDriver || !secondaryDriver) 3252 return false; 3253 3254 return (primaryDriver->getExecutable() == secondaryDriver->getExecutable() && 3255 primaryDriver->getAuxInfo().value("mdpd", false).toBool() == true); 3256 } 3257 3258 void Manager::restartDriver(const QString &deviceName) 3259 { 3260 qCInfo(KSTARS_EKOS) << "Restarting driver" << deviceName; 3261 if (m_LocalMode) 3262 { 3263 for (auto &oneDevice : INDIListener::devices()) 3264 { 3265 if (oneDevice->getDeviceName() == deviceName) 3266 { 3267 DriverManager::Instance()->restartDriver(oneDevice->getDriverInfo()); 3268 break; 3269 } 3270 } 3271 } 3272 else 3273 INDI::WebManager::restartDriver(m_CurrentProfile, deviceName); 3274 } 3275 3276 void Manager::setEkosLoggingEnabled(const QString &name, bool enabled) 3277 { 3278 // LOGGING, FILE, DEFAULT are exclusive, so one of them must be SET to TRUE 3279 if (name == "LOGGING") 3280 { 3281 Options::setDisableLogging(!enabled); 3282 if (!enabled) 3283 KSUtils::Logging::Disable(); 3284 } 3285 else if (name == "FILE") 3286 { 3287 Options::setLogToFile(enabled); 3288 if (enabled) 3289 KSUtils::Logging::UseFile(); 3290 } 3291 else if (name == "DEFAULT") 3292 { 3293 Options::setLogToDefault(enabled); 3294 if (enabled) 3295 KSUtils::Logging::UseDefault(); 3296 } 3297 // VERBOSE should be set to TRUE if INDI or Ekos logging is selected. 3298 else if (name == "VERBOSE") 3299 { 3300 Options::setVerboseLogging(enabled); 3301 KSUtils::Logging::SyncFilterRules(); 3302 } 3303 // Toggle INDI Logging 3304 else if (name == "INDI") 3305 { 3306 Options::setINDILogging(enabled); 3307 KSUtils::Logging::SyncFilterRules(); 3308 } 3309 else if (name == "FITS") 3310 { 3311 Options::setFITSLogging(enabled); 3312 KSUtils::Logging::SyncFilterRules(); 3313 } 3314 else if (name == "CAPTURE") 3315 { 3316 Options::setCaptureLogging(enabled); 3317 Options::setINDICCDLogging(enabled); 3318 Options::setINDIFilterWheelLogging(enabled); 3319 KSUtils::Logging::SyncFilterRules(); 3320 } 3321 else if (name == "FOCUS") 3322 { 3323 Options::setFocusLogging(enabled); 3324 Options::setINDIFocuserLogging(enabled); 3325 KSUtils::Logging::SyncFilterRules(); 3326 } 3327 else if (name == "GUIDE") 3328 { 3329 Options::setGuideLogging(enabled); 3330 Options::setINDICCDLogging(enabled); 3331 KSUtils::Logging::SyncFilterRules(); 3332 } 3333 else if (name == "ALIGNMENT") 3334 { 3335 Options::setAlignmentLogging(enabled); 3336 KSUtils::Logging::SyncFilterRules(); 3337 } 3338 else if (name == "MOUNT") 3339 { 3340 Options::setMountLogging(enabled); 3341 Options::setINDIMountLogging(enabled); 3342 KSUtils::Logging::SyncFilterRules(); 3343 } 3344 else if (name == "SCHEDULER") 3345 { 3346 Options::setSchedulerLogging(enabled); 3347 KSUtils::Logging::SyncFilterRules(); 3348 } 3349 else if (name == "OBSERVATORY") 3350 { 3351 Options::setObservatoryLogging(enabled); 3352 KSUtils::Logging::SyncFilterRules(); 3353 } 3354 } 3355 3356 void Manager::acceptPortSelection() 3357 { 3358 if (m_PortSelector) 3359 m_PortSelector->accept(); 3360 } 3361 3362 void Manager::setPortSelectionComplete() 3363 { 3364 if (m_CurrentProfile->portSelector) 3365 { 3366 // Turn off port selector 3367 m_CurrentProfile->portSelector = false; 3368 KStarsData::Instance()->userdb()->SaveProfile(m_CurrentProfile); 3369 } 3370 3371 if (m_CurrentProfile->autoConnect) 3372 connectDevices(); 3373 } 3374 3375 void Manager::activateModule(const QString &name, bool popup) 3376 { 3377 auto child = toolsWidget->findChild<QWidget *>(name); 3378 if (child) 3379 { 3380 toolsWidget->setCurrentWidget(child); 3381 if (popup) 3382 { 3383 raise(); 3384 activateWindow(); 3385 showNormal(); 3386 } 3387 } 3388 } 3389 3390 void Manager::createModules(const QSharedPointer<ISD::GenericDevice> &device) 3391 { 3392 if (device->isConnected()) 3393 { 3394 if (device->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE) 3395 { 3396 initCapture(); 3397 initFocus(); 3398 initAlign(); 3399 initGuide(); 3400 } 3401 if (device->getDriverInterface() & INDI::BaseDevice::FILTER_INTERFACE) 3402 { 3403 initCapture(); 3404 initFocus(); 3405 initAlign(); 3406 } 3407 if (device->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE) 3408 initFocus(); 3409 if (device->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE) 3410 { 3411 initCapture(); 3412 initAlign(); 3413 initGuide(); 3414 initMount(); 3415 } 3416 if (device->getDriverInterface() & INDI::BaseDevice::ROTATOR_INTERFACE) 3417 { 3418 initCapture(); 3419 initAlign(); 3420 } 3421 if (device->getDriverInterface() & INDI::BaseDevice::DOME_INTERFACE) 3422 { 3423 initCapture(); 3424 initAlign(); 3425 initObservatory(); 3426 } 3427 if (device->getDriverInterface() & INDI::BaseDevice::WEATHER_INTERFACE) 3428 { 3429 initFocus(); 3430 initObservatory(); 3431 } 3432 if (device->getDriverInterface() & INDI::BaseDevice::DUSTCAP_INTERFACE) 3433 { 3434 initCapture(); 3435 } 3436 if (device->getDriverInterface() & INDI::BaseDevice::LIGHTBOX_INTERFACE) 3437 { 3438 initCapture(); 3439 } 3440 if (device->getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE) 3441 { 3442 initMount(); 3443 } 3444 } 3445 } 3446 3447 void Manager::setDeviceReady() 3448 { 3449 // Check if ALL our devices are ready. 3450 // Ready indicates that all properties have been defined. 3451 if (isINDIReady() == false) 3452 { 3453 auto device = static_cast<ISD::GenericDevice*>(sender()); 3454 if (device) 3455 { 3456 3457 if (device->isConnected() == false && m_CurrentProfile->autoConnect) 3458 { 3459 // Do we have port selector checked? 3460 if (m_CurrentProfile->portSelector) 3461 { 3462 // If port selector was not initialized, kick off the timer 3463 // so we can check if all devices should be connected. 3464 // Otherwise, if port selector is started, then let user 3465 // select ports first and then manually connect time. 3466 if (!m_PortSelector) 3467 m_PortSelectorTimer.start(); 3468 } 3469 else 3470 { 3471 qCInfo(KSTARS_EKOS) << "Connecting to" << device->getDeviceName(); 3472 device->Connect(); 3473 } 3474 } 3475 else 3476 qCInfo(KSTARS_EKOS) << device->getDeviceName() << "is connected and ready."; 3477 } 3478 3479 if (m_ekosStatus != Ekos::Success) 3480 return; 3481 } 3482 3483 for (auto &device : INDIListener::devices()) 3484 syncGenericDevice(device); 3485 3486 // If port selector is active, then do not show optical train dialog unless it is dismissed first. 3487 if (m_DriverDevicesCount <= 0 && (m_CurrentProfile->portSelector == false || !m_PortSelector)) 3488 OpticalTrainManager::Instance()->setProfile(m_CurrentProfile); 3489 } 3490 3491 void Manager::createFilterManager(ISD::FilterWheel *device) 3492 { 3493 auto name = device->getDeviceName(); 3494 if (m_FilterManagers.contains(name) == false) 3495 { 3496 QSharedPointer<FilterManager> newFM(new FilterManager(this)); 3497 newFM->setFilterWheel(device); 3498 m_FilterManagers[name] = newFM; 3499 } 3500 else 3501 m_FilterManagers[name]->setFilterWheel(device); 3502 3503 } 3504 3505 bool Manager::getFilterManager(const QString &name, QSharedPointer<FilterManager> &fm) 3506 { 3507 if (m_FilterManagers.contains(name)) 3508 { 3509 fm = m_FilterManagers[name]; 3510 return true; 3511 } 3512 return false; 3513 } 3514 3515 bool Manager::getFilterManager(QSharedPointer<FilterManager> &fm) 3516 { 3517 if (m_FilterManagers.size() > 0) 3518 { 3519 fm = m_FilterManagers.values()[0]; 3520 return true; 3521 } 3522 return false; 3523 } 3524 3525 void Manager::createRotatorController(ISD::Rotator *device) 3526 { 3527 auto Name = device->getDeviceName(); 3528 if (m_RotatorControllers.contains(Name) == false) 3529 { 3530 QSharedPointer<RotatorSettings> newRC(new RotatorSettings(this)); 3531 // Properties are fetched in RotatorSettings::initRotator! 3532 m_RotatorControllers[Name] = newRC; 3533 } 3534 } 3535 3536 bool Manager::getRotatorController(const QString &Name, QSharedPointer<RotatorSettings> &rs) 3537 { 3538 if (m_RotatorControllers.contains(Name)) 3539 { 3540 rs = m_RotatorControllers[Name]; 3541 return true; 3542 } 3543 return false; 3544 } 3545 3546 bool Manager::existRotatorController() 3547 { 3548 return (!m_RotatorControllers.empty()); 3549 } 3550 3551 }