Kstars

manager.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikartech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "manager.h"
8
9#include "analyze/analyze.h"
10#include "capture/capture.h"
11#include "scheduler/scheduler.h"
12#include "scheduler/schedulerprocess.h"
13#include "scheduler/schedulermodulestate.h"
14#include "focus/focus.h"
15#include "align/align.h"
16#include "guide/guide.h"
17#include "mount/mount.h"
18#include "observatory/observatory.h"
19
20#include "opsekos.h"
21#include "ekosadaptor.h"
22#include "kstars.h"
23#include "kstarsdata.h"
24#include "Options.h"
25#include "ekos/capture/rotatorsettings.h"
26#include "profileeditor.h"
27#include "profilewizard.h"
28#include "indihub.h"
29#include "auxiliary/darklibrary.h"
30#include "auxiliary/ksmessagebox.h"
31#include "auxiliary/profilesettings.h"
32#include "capture/sequencejob.h"
33#include "capture/captureprocess.h"
34#include "fitsviewer/fitsview.h"
35#include "fitsviewer/fitsdata.h"
36#include "indi/clientmanager.h"
37#include "indi/driverinfo.h"
38#include "indi/drivermanager.h"
39#include "indi/guimanager.h"
40#include "indi/indilistener.h"
41#include "auxiliary/opticaltrainmanager.h"
42#include "auxiliary/opticaltrainsettings.h"
43#include "indi/indiwebmanager.h"
44#include "indi/indigps.h"
45#include "indi/indiguider.h"
46#include "indi/indirotator.h"
47#include "mount/meridianflipstatuswidget.h"
48#include "ekos/auxiliary/rotatorutils.h"
49
50#include "ekoslive/ekosliveclient.h"
51#include "ekoslive/message.h"
52#include "ekoslive/media.h"
53
54#include <basedevice.h>
55
56#include <KConfigDialog>
57#include <KMessageBox>
58#include <KActionCollection>
59#include <KNotifications/KNotification>
60
61#include <QFutureWatcher>
62#include <QComboBox>
63
64#include <ekos_debug.h>
65
66#define MAX_REMOTE_INDI_TIMEOUT 15000
67#define MAX_LOCAL_INDI_TIMEOUT 10000
68
69namespace Ekos
70{
71
72Manager *Manager::_Manager = nullptr;
73
74Manager *Manager::Instance()
75{
76 if (_Manager == nullptr)
77 _Manager = new Manager(Options::independentWindowEkos() ? nullptr : KStars::Instance());
78
79 return _Manager;
80}
81
82void Manager::release()
83{
84 ProfileSettings::release();
85 OpticalTrainManager::release();
86 OpticalTrainSettings::release();
87 RotatorUtils::release();
88 delete _Manager;
89}
90
91Manager::Manager(QWidget * parent) : QDialog(parent)
92{
93#ifdef Q_OS_OSX
94
95 if (Options::independentWindowEkos())
96 setWindowFlags(Qt::Window);
97 else
98 {
99 setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
100 connect(QApplication::instance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this,
101 SLOT(changeAlwaysOnTop(Qt::ApplicationState)));
102 }
103#else
104 if (Options::independentWindowEkos())
105 //setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
106 setWindowFlags(Qt::Window);
107#endif
108 setupUi(this);
109 // do not show empty targets
110 capturePreview->targetLabel->setVisible(false);
111 capturePreview->mountTarget->setVisible(false);
112
113 // position the vertical splitter by 2/3
114 deviceSplitter->setSizes(QList<int>({20000, 10000}));
115
116 qRegisterMetaType<Ekos::CommunicationStatus>("Ekos::CommunicationStatus");
118
119 new EkosAdaptor(this);
120 QDBusConnection::sessionBus().registerObject("/KStars/Ekos", this);
121
122 setWindowIcon(QIcon::fromTheme("kstars_ekos"));
123
124 profileModel.reset(new QStandardItemModel(0, 4));
125 profileModel->setHorizontalHeaderLabels(QStringList() << "id"
126 << "name"
127 << "host"
128 << "port");
129
130 m_CountdownTimer.setInterval(1000);
131 connect(&m_CountdownTimer, &QTimer::timeout, this, &Ekos::Manager::updateCaptureCountDown);
132
133 toolsWidget->setIconSize(QSize(48, 48));
134 connect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange, Qt::UniqueConnection);
135
136 // Enable scheduler Tab
137 toolsWidget->setTabEnabled(1, false);
138
139 // Enable analyze Tab
140 toolsWidget->setTabEnabled(2, false);
141
142 // Start/Stop INDI Server
143 connect(processINDIB, &QPushButton::clicked, this, &Ekos::Manager::processINDI);
144 processINDIB->setIcon(QIcon::fromTheme("media-playback-start"));
145 processINDIB->setToolTip(i18n("Start"));
146
147 // Connect/Disconnect INDI devices
148 connect(connectB, &QPushButton::clicked, this, &Ekos::Manager::connectDevices);
149 connect(disconnectB, &QPushButton::clicked, this, &Ekos::Manager::disconnectDevices);
150
151 // Init EkosLive client
153 ekosLiveClient.reset(new EkosLive::Client(this));
154 connect(ekosLiveClient.get(), &EkosLive::Client::connected, this, [this]()
155 {
156 emit ekosLiveStatusChanged(true);
157 });
158 connect(ekosLiveClient.get(), &EkosLive::Client::disconnected, this, [this]()
159 {
160 emit ekosLiveStatusChanged(false);
161 });
162
163 // INDI Control Panel
164 //connect(controlPanelB, &QPushButton::clicked, GUIManager::Instance(), SLOT(show()));
166 {
167 ekosLiveClient.get()->show();
168 ekosLiveClient.get()->raise();
169 });
170
171 connect(this, &Manager::ekosStatusChanged, ekosLiveClient.get()->message(), &EkosLive::Message::setEkosStatingStatus);
172 connect(this, &Manager::indiStatusChanged, ekosLiveClient.get()->message(), &EkosLive::Message::setINDIStatus);
173 connect(ekosLiveClient.get()->message(), &EkosLive::Message::connected, this, [&]()
174 {
175 ekosLiveB->setIcon(QIcon(":/icons/cloud-online.svg"));
176 });
177 connect(ekosLiveClient.get()->message(), &EkosLive::Message::disconnected, this, [&]()
178 {
179 ekosLiveB->setIcon(QIcon::fromTheme("folder-cloud"));
180 });
181 connect(ekosLiveClient.get()->media(), &EkosLive::Media::newBoundingRect, ekosLiveClient.get()->message(),
182 &EkosLive::Message::setBoundingRect);
183 connect(ekosLiveClient.get()->message(), &EkosLive::Message::resetPolarView, ekosLiveClient.get()->media(),
184 &EkosLive::Media::resetPolarView);
185 connect(KSMessageBox::Instance(), &KSMessageBox::newMessage, ekosLiveClient.get()->message(),
186 &EkosLive::Message::sendDialog);
187
188 // Port Selector
189 m_PortSelectorTimer.setInterval(500);
190 m_PortSelectorTimer.setSingleShot(true);
191 connect(&m_PortSelectorTimer, &QTimer::timeout, this, [this]()
192 {
193 if (m_PortSelector && m_CurrentProfile->portSelector)
194 {
195 if (m_PortSelector->shouldShow())
196 {
197 m_PortSelector->show();
198 m_PortSelector->raise();
199
200 ekosLiveClient.get()->message()->requestPortSelection(true);
201 }
202 // If port selector is enabled, but we have zero ports to work with, let's proceed to connecting if it is enabled.
203 else if (m_CurrentProfile->autoConnect)
204 setPortSelectionComplete();
205 }
206 else if (m_CurrentProfile->autoConnect)
207 setPortSelectionComplete();
208 });
210 {
211 if (m_PortSelector)
212 {
213 m_PortSelector->show();
214 m_PortSelector->raise();
215 }
216 });
217
218 connect(this, &Ekos::Manager::ekosStatusChanged, this, [&](Ekos::CommunicationStatus status)
219 {
220 indiControlPanelB->setEnabled(status == Ekos::Success);
221 connectB->setEnabled(false);
222 disconnectB->setEnabled(false);
223 profileGroup->setEnabled(status == Ekos::Idle || status == Ekos::Error);
224 m_isStarted = (status == Ekos::Success || status == Ekos::Pending);
225 if (status == Ekos::Success)
226 {
227 processINDIB->setIcon(QIcon::fromTheme("media-playback-stop"));
228 processINDIB->setToolTip(i18n("Stop"));
229 setWindowTitle(i18nc("@title:window", "Ekos - %1 Profile", m_CurrentProfile->name));
230 }
231 else if (status == Ekos::Error || status == Ekos::Idle)
232 {
233 processINDIB->setIcon(QIcon::fromTheme("media-playback-start"));
234 processINDIB->setToolTip(i18n("Start"));
235 }
236 else
237 {
238 processINDIB->setIcon(QIcon::fromTheme("call-stop"));
239 processINDIB->setToolTip(i18n("Connection in progress. Click to abort."));
240 }
241 });
243 {
244 KStars::Instance()->actionCollection()->action("show_control_panel")->trigger();
245 });
247 {
248 KStars::Instance()->actionCollection()->action("configure")->trigger();
249 });
250 // Save as above, but it appears in all modules
251 connect(ekosOptionsB, &QPushButton::clicked, this, &Ekos::Manager::showEkosOptions);
252
253 // Clear Ekos Log
254 connect(clearB, &QPushButton::clicked, this, &Ekos::Manager::clearLog);
255
256 // Logs
257 KConfigDialog * dialog = new KConfigDialog(this, "logssettings", Options::self());
258 opsLogs = new Ekos::OpsLogs();
259 KPageWidgetItem * page = dialog->addPage(opsLogs, i18n("Logging"));
260 page->setIcon(QIcon::fromTheme("configure"));
262 connect(dialog->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &Ekos::Manager::updateDebugInterfaces);
263 connect(dialog->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &Ekos::Manager::updateDebugInterfaces);
264
265 // Profiles
266 connect(addProfileB, &QPushButton::clicked, this, &Ekos::Manager::addProfile);
267 connect(editProfileB, &QPushButton::clicked, this, &Ekos::Manager::editProfile);
268 connect(deleteProfileB, &QPushButton::clicked, this, &Ekos::Manager::deleteProfile);
269 connect(profileCombo, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), this,
270 [ = ](const QString & text)
271 {
272 Options::setProfile(text);
273 if (text == "Simulators")
274 {
275 editProfileB->setEnabled(false);
276 deleteProfileB->setEnabled(false);
277 }
278 else
279 {
280 editProfileB->setEnabled(true);
281 deleteProfileB->setEnabled(true);
282 }
283 });
284
285 // Settle timer
286 // Debounce until property stream settles down for a second.
287 settleTimer.setInterval(1000);
288 connect(&settleTimer, &QTimer::timeout, this, [&]()
289 {
290 if (m_settleStatus != Ekos::Success)
291 {
292 m_settleStatus = Ekos::Success;
293 emit settleStatusChanged(m_settleStatus);
294 }
295 });
296
297 // Ekos Wizard
298 connect(wizardProfileB, &QPushButton::clicked, this, &Ekos::Manager::wizardProfile);
299
303
304 // Set Profile icons
305 addProfileB->setIcon(QIcon::fromTheme("list-add"));
307 editProfileB->setIcon(QIcon::fromTheme("document-edit"));
309 deleteProfileB->setIcon(QIcon::fromTheme("list-remove"));
311 wizardProfileB->setIcon(QIcon::fromTheme("tools-wizard"));
313 customDriversB->setIcon(QIcon::fromTheme("roll"));
315
316 connect(customDriversB, &QPushButton::clicked, DriverManager::Instance(), &DriverManager::showCustomDrivers);
317
318 // Load all drivers
319 loadDrivers();
320
321 // Load add driver profiles
322 loadProfiles();
323
324 // INDI Control Panel and Ekos Options
325 optionsB->setIcon(QIcon::fromTheme("configure", QIcon(":/icons/ekos_setup.png")));
327
328 // Setup Tab
329 toolsWidget->tabBar()->setTabIcon(0, QIcon(":/icons/ekos_setup.png"));
330 toolsWidget->tabBar()->setTabToolTip(0, i18n("Setup"));
331
332 // Initialize Ekos Scheduler Module
333 schedulerProcess.reset(new Scheduler());
334 int index = addModuleTab(EkosModule::Scheduler, schedulerModule(), QIcon(":/icons/ekos_scheduler.png"));
335 toolsWidget->tabBar()->setTabToolTip(index, i18n("Scheduler"));
336 capturePreview->shareSchedulerModuleState(schedulerModule()->moduleState());
337 connect(schedulerModule()->process().data(), &SchedulerProcess::newLog, this, &Ekos::Manager::updateLog);
338 connect(schedulerModule(), &Ekos::Scheduler::newTarget, this, &Manager::setTarget);
339 // Scheduler <---> EkosLive connections
340 connect(schedulerModule(), &Ekos::Scheduler::jobsUpdated, ekosLiveClient.get()->message(),
341 &EkosLive::Message::sendSchedulerJobs, Qt::UniqueConnection);
342 connect(schedulerModule(), &Ekos::Scheduler::settingsUpdated, ekosLiveClient.get()->message(),
343 &EkosLive::Message::sendSchedulerSettings, Qt::UniqueConnection);
344 connect(schedulerModule()->process().data(), &SchedulerProcess::newLog, ekosLiveClient.get()->message(),
345 [this]()
346 {
347 QJsonObject cStatus =
348 {
349 {"log", schedulerModule()->moduleState()->getLogText()}
350 };
351
352 ekosLiveClient.get()->message()->sendSchedulerStatus(cStatus);
353 });
354 connect(schedulerModule(), &Ekos::Scheduler::newStatus, ekosLiveClient.get()->message(),
355 [this](Ekos::SchedulerState state)
356 {
357 QJsonObject cStatus =
358 {
359 {"status", state}
360 };
361
362 ekosLiveClient.get()->message()->sendSchedulerStatus(cStatus);
363 });
364
365 // Initialize Ekos Analyze Module
366 analyzeProcess.reset(new Ekos::Analyze());
367 connect(analyzeProcess.get(), &Ekos::Analyze::newLog, this, &Ekos::Manager::updateLog);
368
369 index = addModuleTab(EkosModule::Analyze, analyzeProcess.get(), QIcon(":/icons/ekos_analyze.png"));
370 toolsWidget->tabBar()->setTabToolTip(index, i18n("Analyze"));
371
372 numPermanentTabs = index + 1;
373
374 // Temporary fix. Not sure how to resize Ekos Dialog to fit contents of the various tabs in the QScrollArea which are added
375 // dynamically. I used setMinimumSize() but it doesn't appear to make any difference.
376 // Also set Layout policy to SetMinAndMaxSize as well. Any idea how to fix this?
377 // FIXME
378 //resize(1000,750);
379
380 m_SummaryView.reset(new SummaryFITSView(capturePreview->previewWidget));
381 m_SummaryView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
382 // sterne-jaeger 2021-08-08: Do not set base size here, otherwise the zoom will be incorrect
383 // summaryPreview->setBaseSize(capturePreview->previewWidget->size());
384 m_SummaryView->createFloatingToolBar();
385 m_SummaryView->setCursorMode(FITSView::dragCursor);
386 m_SummaryView->showProcessInfo(false);
387 capturePreview->setSummaryFITSView(m_SummaryView.get());
388 mountStatusLayout->setAlignment(Qt::AlignVCenter);
389
390 if (Options::ekosLeftIcons())
391 {
392 toolsWidget->setTabPosition(QTabWidget::West);
394 trans.rotate(90);
395
396 for (int i = 0; i < numPermanentTabs; ++i)
397 {
398 QIcon icon = toolsWidget->tabIcon(i);
399 QPixmap pix = icon.pixmap(QSize(48, 48));
400 icon = QIcon(pix.transformed(trans));
401 toolsWidget->setTabIcon(i, icon);
402 }
403 }
404
405 //Note: This is to prevent a button from being called the default button
406 //and then executing when the user hits the enter key such as when on a Text Box
407
409 for (auto &button : qButtons)
410 button->setAutoDefault(false);
411
412
413 resize(Options::ekosWindowWidth(), Options::ekosWindowHeight());
414}
415
416void Manager::changeAlwaysOnTop(Qt::ApplicationState state)
417{
418 if (isVisible())
419 {
420 if (state == Qt::ApplicationActive)
421 setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
422 else
423 setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
424 show();
425 }
426}
427
428Manager::~Manager()
429{
430 toolsWidget->disconnect(this);
431}
432
433void Manager::closeEvent(QCloseEvent * event)
434{
435 // QAction * a = KStars::Instance()->actionCollection()->action("show_ekos");
436 // a->setChecked(false);
437
438 // 2019-02-14 JM: Close event, for some reason, make all the children disappear
439 // when the widget is shown again. Applying a workaround here
440
441 event->ignore();
442 hide();
443}
444
445void Manager::hideEvent(QHideEvent * /*event*/)
446{
447 Options::setEkosWindowWidth(width());
448 Options::setEkosWindowHeight(height());
449
450 QAction * a = KStars::Instance()->actionCollection()->action("show_ekos");
451 a->setChecked(false);
452}
453
454void Manager::showEvent(QShowEvent * /*event*/)
455{
456 QAction * a = KStars::Instance()->actionCollection()->action("show_ekos");
457 a->setChecked(true);
458
459 // Just show the profile wizard ONCE per session
460 if (profileWizardLaunched == false && profiles.count() == 1)
461 {
462 profileWizardLaunched = true;
463 wizardProfile();
464 }
465}
466
467void Manager::resizeEvent(QResizeEvent *)
468{
469 focusManager->updateFocusDetailView();
470 guideManager->updateGuideDetailView();
471}
472
473void Manager::loadProfiles()
474{
475 profiles.clear();
476 KStarsData::Instance()->userdb()->GetAllProfiles(profiles);
477
478 profileModel->clear();
479
480 for (auto &pi : profiles)
481 {
483
484 info << new QStandardItem(pi->id) << new QStandardItem(pi->name) << new QStandardItem(pi->host)
485 << new QStandardItem(pi->port);
486 profileModel->appendRow(info);
487 }
488
489 profileModel->sort(0);
490 profileCombo->blockSignals(true);
491 profileCombo->setModel(profileModel.get());
492 profileCombo->setModelColumn(1);
493 profileCombo->blockSignals(false);
494
495 // Load last used profile from options
496 int index = profileCombo->findText(Options::profile());
497 // If not found, set it to first item
498 if (index == -1)
499 index = 0;
500 profileCombo->setCurrentIndex(index);
501}
502
503int Manager::addModuleTab(Manager::EkosModule module, QWidget *tab, const QIcon &icon)
504{
505 int index = 0;
506 switch(module)
507 {
508 case EkosModule::Observatory:
509 index += guideProcess ? 1 : 0; /* FALLTHRU */
510 case EkosModule::Guide:
511 index += alignProcess ? 1 : 0; /* FALLTHRU */
512 case EkosModule::Align:
513 index += mountProcess ? 1 : 0; /* FALLTHRU */
514 case EkosModule::Mount:
515 index += focusProcess ? 1 : 0; /* FALLTHRU */
516 case EkosModule::Focus:
517 index += captureProcess ? 1 : 0; /* FALLTHRU */
518 case EkosModule::Capture:
519 index += analyzeProcess ? 1 : 0; /* FALLTHRU */
520 case EkosModule::Analyze:
521 index += schedulerProcess ? 1 : 0; /* FALLTHRU */
522 case EkosModule::Scheduler:
523 index += 1; /* FALLTHRU */
524 case EkosModule::Setup:
525 // do nothing
526 break;
527 default:
528 index = toolsWidget->count();
529 break;
530 }
531
532 toolsWidget->insertTab(index, tab, icon, "");
533 return index;
534}
535
536void Manager::loadDrivers()
537{
538 for (auto &dv : DriverManager::Instance()->getDrivers())
539 {
540 if (dv->getDriverSource() != HOST_SOURCE)
541 driversList[dv->getLabel()] = dv;
542 }
543}
544
545void Manager::reset()
546{
547 qCDebug(KSTARS_EKOS) << "Resetting Ekos Manager...";
548
549 ProfileSettings::release();
550 OpticalTrainManager::release();
551 OpticalTrainSettings::release();
552 RotatorUtils::release();
553
554 m_DriverDevicesCount = 0;
555
556 removeTabs();
557
558 captureProcess.reset();
559 focusProcess.reset();
560 guideProcess.reset();
561 alignProcess.reset();
562 mountProcess.reset();
563 observatoryProcess.reset();
564
565 for (auto &oneManger : m_FilterManagers)
567 m_FilterManagers.clear();
568
569 for (auto &oneController : m_RotatorControllers)
571 m_RotatorControllers.clear();
572
573 DarkLibrary::Release();
574 m_PortSelector.reset();
575 m_PortSelectorTimer.stop();
576
577 Ekos::CommunicationStatus previousStatus;
578
579 previousStatus = m_settleStatus;
580 m_settleStatus = Ekos::Idle;
581 if (previousStatus != m_settleStatus)
582 emit settleStatusChanged(m_settleStatus);
583
584 previousStatus = m_ekosStatus;
585 m_ekosStatus = Ekos::Idle;
586 if (previousStatus != m_ekosStatus)
587 emit ekosStatusChanged(m_ekosStatus);
588
589 previousStatus = m_indiStatus;
590 m_indiStatus = Ekos::Idle;
591 if (previousStatus != m_indiStatus)
592 emit indiStatusChanged(m_indiStatus);
593
594 connectB->setEnabled(false);
595 disconnectB->setEnabled(false);
596 //controlPanelB->setEnabled(false);
597 processINDIB->setEnabled(true);
598
599 mountGroup->setEnabled(false);
600 capturePreview->setEnabled(false);
601 capturePreview->reset();
602 mountStatus->setStatus(i18n("Idle"), Qt::gray);
603 mountStatus->setStyleSheet(QString());
604 focusManager->reset();
605 guideManager->reset();
606
607 m_isStarted = false;
608
609 processINDIB->setIcon(QIcon::fromTheme("media-playback-start"));
610 processINDIB->setToolTip(i18n("Start"));
611}
612
613void Manager::processINDI()
614{
615 if (m_isStarted == false)
616 start();
617 else
618 stop();
619}
620
621void Manager::stop()
622{
623 cleanDevices();
624 m_PortSelector.reset();
625 m_PortSelectorTimer.stop();
626 m_CountdownTimer.stop();
627 portSelectorB->setEnabled(false);
628
629 if (indiHubAgent)
630 indiHubAgent->terminate();
631
632 profileGroup->setEnabled(true);
633
634 setWindowTitle(i18nc("@title:window", "Ekos"));
635}
636
637void Manager::start()
638{
639 // Don't start if it is already started before
640 if (m_ekosStatus == Ekos::Pending || m_ekosStatus == Ekos::Success)
641 {
642 qCWarning(KSTARS_EKOS) << "Ekos Manager start called but current Ekos Status is" << m_ekosStatus << "Ignoring request.";
643 return;
644 }
645
646 managedDrivers.clear();
647
648 // If clock was paused, unpaused it and sync time
649 if (KStarsData::Instance()->clock()->isActive() == false)
650 {
651 KStarsData::Instance()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
652 KStarsData::Instance()->clock()->start();
653 }
654
655 // Reset Ekos Manager
656 reset();
657
658 // Get Current Profile
659 getCurrentProfile(m_CurrentProfile);
660 m_LocalMode = m_CurrentProfile->isLocal();
661
662 ProfileSettings::Instance()->setProfile(m_CurrentProfile);
663
664 // Load profile location if one exists
665 updateProfileLocation(m_CurrentProfile);
666
667 bool haveCCD = false, haveGuider = false;
668
669 // If external guide is specified in the profile, set the
670 // corresponding options
671 if (m_CurrentProfile->guidertype == Ekos::Guide::GUIDE_PHD2)
672 {
673 Options::setPHD2Host(m_CurrentProfile->guiderhost);
674 Options::setPHD2Port(m_CurrentProfile->guiderport);
675 }
676 else if (m_CurrentProfile->guidertype == Ekos::Guide::GUIDE_LINGUIDER)
677 {
678 Options::setLinGuiderHost(m_CurrentProfile->guiderhost);
679 Options::setLinGuiderPort(m_CurrentProfile->guiderport);
680 }
681
682 // Parse script, if any
685 QJsonDocument doc = QJsonDocument::fromJson(m_CurrentProfile->scripts, &jsonError);
686
688 profileScripts = doc.array();
689
690 // For locally running INDI server
691 if (m_LocalMode)
692 {
693 auto drv = driversList.value(m_CurrentProfile->mount());
694
695 if (!drv.isNull())
696 managedDrivers.append(drv->clone());
697
698 drv = driversList.value(m_CurrentProfile->ccd());
699 if (!drv.isNull())
700 {
701 managedDrivers.append(drv->clone());
702 haveCCD = true;
703 }
704
705 Options::setGuiderType(m_CurrentProfile->guidertype);
706
707 drv = driversList.value(m_CurrentProfile->guider());
708 if (!drv.isNull())
709 {
710 haveGuider = true;
711
712 // If the guider and ccd are the same driver, we have two cases:
713 // #1 Drivers that only support ONE device per driver (such as sbig)
714 // #2 Drivers that supports multiples devices per driver (such as sx)
715 // For #1, we modify guider_di to make a unique label for the other device with postfix "Guide"
716 // For #2, we set guider_di to nullptr and we prompt the user to select which device is primary ccd and which is guider
717 // since this is the only way to find out in real time.
718 if (haveCCD && m_CurrentProfile->guider() == m_CurrentProfile->ccd())
719 {
720 if (checkUniqueBinaryDriver( driversList.value(m_CurrentProfile->ccd()), drv))
721 {
722 drv.clear();
723 }
724 else
725 {
726 drv->setUniqueLabel(drv->getLabel() + " Guide");
727 }
728 }
729
730 if (!drv.isNull())
731 managedDrivers.append(drv->clone());
732 }
733
734 drv = driversList.value(m_CurrentProfile->ao());
735 if (!drv.isNull())
736 managedDrivers.append(drv->clone());
737
738 drv = driversList.value(m_CurrentProfile->filter());
739 if (!drv.isNull())
740 managedDrivers.append(drv->clone());
741
742 drv = driversList.value(m_CurrentProfile->focuser());
743 if (!drv.isNull())
744 managedDrivers.append(drv->clone());
745
746 drv = driversList.value(m_CurrentProfile->dome());
747 if (!drv.isNull())
748 managedDrivers.append(drv->clone());
749
750 drv = driversList.value(m_CurrentProfile->weather());
751 if (!drv.isNull())
752 managedDrivers.append(drv->clone());
753
754 drv = driversList.value(m_CurrentProfile->aux1());
755 if (!drv.isNull())
756 {
757 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) &&
758 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv))
759 managedDrivers.append(drv->clone());
760 }
761 drv = driversList.value(m_CurrentProfile->aux2());
762 if (!drv.isNull())
763 {
764 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) &&
765 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv))
766 managedDrivers.append(drv->clone());
767 }
768
769 drv = driversList.value(m_CurrentProfile->aux3());
770 if (!drv.isNull())
771 {
772 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) &&
773 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv))
774 managedDrivers.append(drv->clone());
775 }
776
777 drv = driversList.value(m_CurrentProfile->aux4());
778 if (!drv.isNull())
779 {
780 if (!checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->ccd()), drv) &&
781 !checkUniqueBinaryDriver(driversList.value(m_CurrentProfile->guider()), drv))
782 managedDrivers.append(drv->clone());
783 }
784
785 // Add remote drivers if we have any
786 if (m_CurrentProfile->remotedrivers.isEmpty() == false && m_CurrentProfile->remotedrivers.contains("@"))
787 {
788 for (auto remoteDriver : m_CurrentProfile->remotedrivers.split(","))
789 {
790 QString name, label, host("localhost"), port("7624"), hostport(host + ':' + port);
791
792 // Possible configurations:
793 // - device
794 // - device@host
795 // - device@host:port
796 // - @host
797 // - @host:port
798
799 {
801
802 // device or device@host:port
803 if (device_location.length() > 0)
805
806 // device@host:port or @host:port
807 if (device_location.length() > 1)
809 }
810
811 {
812 QStringList location = hostport.split(':');
813
814 // host or host:port
815 if (location.length() > 0)
816 host = location[0];
817
818 // host:port
819 if (location.length() > 1)
820 port = location[1];
821 }
822
824 dv->setRemoteHost(host);
825 dv->setRemotePort(port);
826
827 label = name;
828 // Remove extra quotes
829 label.remove("\"");
830 dv->setLabel(label);
831 dv->setUniqueLabel(label);
832 managedDrivers.append(dv);
833 }
834 }
835
836
837 if (haveCCD == false && haveGuider == false && m_CurrentProfile->remotedrivers.isEmpty())
838 {
839 KSNotification::error(i18n("Ekos requires at least one CCD or Guider to operate."));
840 managedDrivers.clear();
841 m_ekosStatus = Ekos::Error;
842 emit ekosStatusChanged(m_ekosStatus);
843 return;
844 }
845
846 m_DriverDevicesCount = managedDrivers.count();
847 }
848 else
849 {
851
852 remote_indi->setHostParameters(m_CurrentProfile->host, m_CurrentProfile->port);
853
854 remote_indi->setDriverSource(GENERATED_SOURCE);
855
856 managedDrivers.append(remote_indi);
857
858 haveCCD = m_CurrentProfile->drivers.contains("CCD");
859 haveGuider = m_CurrentProfile->drivers.contains("Guider");
860
861 Options::setGuiderType(m_CurrentProfile->guidertype);
862
863 if (haveCCD == false && haveGuider == false && m_CurrentProfile->remotedrivers.isEmpty())
864 {
865 KSNotification::error(i18n("Ekos requires at least one CCD or Guider to operate."));
866 m_DriverDevicesCount = 0;
867 m_ekosStatus = Ekos::Error;
868 emit ekosStatusChanged(m_ekosStatus);
869 return;
870 }
871
872 m_DriverDevicesCount = m_CurrentProfile->drivers.count();
873 }
874
875
876 // Prioritize profile script drivers over other drivers
878 for (const auto &oneRule : qAsConst(profileScripts))
879 {
880 auto matchingDriver = std::find_if(managedDrivers.begin(), managedDrivers.end(), [oneRule](const auto & oneDriver)
881 {
882 return oneDriver->getLabel() == oneRule.toObject()["Driver"].toString();
883 });
884
885 if (matchingDriver != managedDrivers.end())
886 {
887 (*matchingDriver)->setStartupRule(oneRule.toObject());
888 sortedList.append(*matchingDriver);
889 }
890 }
891
892 // If we have any profile scripts drivers, let's re-sort managed drivers
893 // so that profile script drivers
894 if (!sortedList.isEmpty())
895 {
896 for (auto &oneDriver : managedDrivers)
897 {
898 if (sortedList.contains(oneDriver) == false)
899 sortedList.append(oneDriver);
900 }
901
902 managedDrivers = sortedList;
903 }
904
905 connect(DriverManager::Instance(), &DriverManager::serverStarted, this,
906 &Manager::setServerStarted, Qt::UniqueConnection);
907 connect(DriverManager::Instance(), &DriverManager::serverFailed, this,
908 &Manager::setServerFailed, Qt::UniqueConnection);
909 connect(DriverManager::Instance(), &DriverManager::clientStarted, this,
910 &Manager::setClientStarted, Qt::UniqueConnection);
911 connect(DriverManager::Instance(), &DriverManager::clientFailed, this,
912 &Manager::setClientFailed, Qt::UniqueConnection);
913 connect(DriverManager::Instance(), &DriverManager::clientTerminated, this,
914 &Manager::setClientTerminated, Qt::UniqueConnection);
915
916 connect(INDIListener::Instance(), &INDIListener::newDevice, this, &Ekos::Manager::processNewDevice);
917 connect(INDIListener::Instance(), &INDIListener::deviceRemoved, this, &Ekos::Manager::removeDevice, Qt::DirectConnection);
918
919
920#ifdef Q_OS_OSX
921 if (m_LocalMode || m_CurrentProfile->host == "localhost")
922 {
923 if (isRunning("PTPCamera"))
924 {
925 if (KMessageBox::Yes ==
926 (KMessageBox::questionYesNo(nullptr,
927 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?"),
928 i18n("PTP Camera"), KStandardGuiItem::yes(), KStandardGuiItem::no(),
929 "ekos_shutdown_PTPCamera")))
930 {
931 //TODO is there a better way to do this.
932 QProcess p;
933 p.start("killall PTPCamera");
934 p.waitForFinished();
935 }
936 }
937 }
938#endif
939 if (m_LocalMode)
940 {
941 auto executeStartINDIServices = [this]()
942 {
943 appendLogText(i18n("Starting INDI services..."));
944
945 m_ekosStatus = Ekos::Pending;
946 emit ekosStatusChanged(m_ekosStatus);
947
948 DriverManager::Instance()->startDevices(managedDrivers);
949 };
950
951 // If INDI server is already running, let's see if we need to shut it down first
952 if (isRunning("indiserver"))
953 {
954 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, executeStartINDIServices]()
955 {
956 KSMessageBox::Instance()->disconnect(this);
957 DriverManager::Instance()->stopAllDevices();
958 //TODO is there a better way to do this.
959 QProcess p;
960 const QString program = "pkill";
961 QStringList arguments;
962 arguments << "indiserver";
963 p.start(program, arguments);
964 p.waitForFinished();
965
967 });
968 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this, executeStartINDIServices]()
969 {
970 KSMessageBox::Instance()->disconnect(this);
972 });
973
974 KSMessageBox::Instance()->questionYesNo(i18n("Ekos detected an instance of INDI server running. Do you wish to "
975 "shut down the existing instance before starting a new one?"),
976 i18n("INDI Server"), 5);
977 }
978 else
980
981 }
982 else
983 {
984 auto runConnection = [this]()
985 {
986 // If it got cancelled by the user, return immediately.
987 if (m_ekosStatus != Ekos::Pending)
988 return;
989
990 appendLogText(
991 i18n("Connecting to remote INDI server at %1 on port %2 ...", m_CurrentProfile->host, m_CurrentProfile->port));
992
993 DriverManager::Instance()->connectRemoteHost(managedDrivers.first());
994 };
995
996 auto runProfile = [this, runConnection]()
997 {
998 // If it got cancelled by the user, return immediately.
999 if (m_ekosStatus != Ekos::Pending)
1000 return;
1001
1002 INDI::WebManager::syncCustomDrivers(m_CurrentProfile);
1003 INDI::WebManager::checkVersion(m_CurrentProfile);
1004
1005 if (INDI::WebManager::areDriversRunning(m_CurrentProfile) == false)
1006 {
1007 INDI::WebManager::stopProfile(m_CurrentProfile);
1008
1009 if (INDI::WebManager::startProfile(m_CurrentProfile) == false)
1010 {
1011 appendLogText(i18n("Failed to start profile on remote INDI Web Manager."));
1012 return;
1013 }
1014
1015 appendLogText(i18n("Starting profile on remote INDI Web Manager..."));
1016 m_RemoteManagerStart = true;
1017 }
1018
1019 runConnection();
1020 };
1021
1022 m_ekosStatus = Ekos::Pending;
1023 emit ekosStatusChanged(m_ekosStatus);
1024
1025 // If we need to use INDI Web Manager
1026 if (m_CurrentProfile->INDIWebManagerPort > 0)
1027 {
1028 appendLogText(i18n("Establishing communication with remote INDI Web Manager..."));
1029 m_RemoteManagerStart = false;
1031 connect(watcher, &QFutureWatcher<bool>::finished, this, [this, runConnection, runProfile, watcher]()
1032 {
1033 watcher->deleteLater();
1034
1035 // If it got cancelled by the user, return immediately.
1036 if (m_ekosStatus != Ekos::Pending)
1037 return;
1038
1039 // If web manager is online, try to run the profile in it
1040 if (watcher->result())
1041 {
1042 runProfile();
1043 }
1044 // Else, try to connect directly to INDI server as there could be a chance
1045 // that it is already running.
1046 else
1047 {
1048 appendLogText(i18n("Warning: INDI Web Manager is not online."));
1049 runConnection();
1050 }
1051
1052 });
1053
1054 QFuture<bool> result = INDI::AsyncWebManager::isOnline(m_CurrentProfile);
1055 watcher->setFuture(result);
1056 }
1057 else
1058 {
1059 runConnection();
1060 }
1061 }
1062}
1063
1064void Manager::setClientStarted(const QString &host, int port)
1065{
1066 if (managedDrivers.size() > 0)
1067 {
1068 if (m_LocalMode)
1069 {
1070 if (m_CurrentProfile->autoConnect)
1071 appendLogText(i18n("INDI services started on port %1.", port));
1072 else
1073 appendLogText(
1074 i18n("INDI services started on port %1. Please connect devices.", port));
1075 }
1076 else
1077 {
1078 appendLogText(
1079 i18n("INDI services started. Connection to remote INDI server %1:%2 is successful. Waiting for devices...", host, port));
1080 }
1081 }
1082
1083 QTimer::singleShot(MAX_LOCAL_INDI_TIMEOUT, this, &Ekos::Manager::checkINDITimeout);
1084}
1085
1086void Manager::setClientFailed(const QString &host, int port, const QString &errorMessage)
1087{
1088 if (m_LocalMode)
1089 appendLogText(i18n("Failed to connect to local INDI server %1:%2", host, port));
1090 else
1091 appendLogText(i18n("Failed to connect to remote INDI server %1:%2", host, port));
1092
1093 //INDIListener::Instance()->disconnect(this);
1094 // qDeleteAll(managedDrivers);
1095 // managedDrivers.clear();
1096 m_ekosStatus = Ekos::Error;
1097 emit ekosStatusChanged(m_ekosStatus);
1098 KSNotification::error(errorMessage, i18n("Error"), 15);
1099}
1100
1101void Manager::setClientTerminated(const QString &host, int port, const QString &errorMessage)
1102{
1103 if (m_LocalMode)
1104 appendLogText(i18n("Lost connection to local INDI server %1:%2", host, port));
1105 else
1106 appendLogText(i18n("Lost connection to remote INDI server %1:%2", host, port));
1107
1108 //INDIListener::Instance()->disconnect(this);
1109 // qDeleteAll(managedDrivers);
1110 // managedDrivers.clear();
1111 m_ekosStatus = Ekos::Error;
1112 emit ekosStatusChanged(m_ekosStatus);
1113 KSNotification::error(errorMessage, i18n("Error"), 15);
1114}
1115
1116void Manager::setServerStarted(const QString &host, int port)
1117{
1118 if (m_LocalMode && m_CurrentProfile->indihub != INDIHub::None)
1119 {
1120 if (QFile(Options::iNDIHubAgent()).exists())
1121 {
1122 indiHubAgent = new QProcess();
1124
1125 args << "--indi-server" << QString("%1:%2").arg(host).arg(port);
1126 if (m_CurrentProfile->guidertype == Ekos::Guide::GUIDE_PHD2)
1127 args << "--phd2-server" << QString("%1:%2").arg(m_CurrentProfile->guiderhost).arg(m_CurrentProfile->guiderport);
1128 args << "--mode" << INDIHub::toString(m_CurrentProfile->indihub);
1129 indiHubAgent->start(Options::iNDIHubAgent(), args);
1130
1131 qCDebug(KSTARS_EKOS) << "Started INDIHub agent.";
1132 }
1133 }
1134}
1135
1136void Manager::setServerFailed(const QString &host, int port, const QString &message)
1137{
1138 Q_UNUSED(host)
1139 Q_UNUSED(port)
1140 managedDrivers.clear();
1141 m_ekosStatus = Ekos::Error;
1142 emit ekosStatusChanged(m_ekosStatus);
1143 KSNotification::error(message, i18n("Error"), 15);
1144}
1145
1146//void Manager::setServerTerminated(const QString &host, int port, const QString &message)
1147//{
1148// if ((m_LocalMode && managedDrivers.first()->getPort() == port) ||
1149// (currentProfile->host == host && currentProfile->port == port))
1150// {
1151// cleanDevices(false);
1152// if (indiHubAgent)
1153// indiHubAgent->terminate();
1154// }
1155
1156// INDIListener::Instance()->disconnect(this);
1157// qDeleteAll(managedDrivers);
1158// managedDrivers.clear();
1159// m_ekosStatus = Ekos::Error;
1160// emit ekosStatusChanged(m_ekosStatus);
1161// KSNotification::error(message, i18n("Error"), 15);
1162//}
1163
1164void Manager::checkINDITimeout()
1165{
1166 // Don't check anything unless we're still pending
1167 if (m_ekosStatus != Ekos::Pending)
1168 {
1169 // All devices are connected already, nothing to do.
1170 if (m_indiStatus != Ekos::Pending || m_CurrentProfile->portSelector || m_CurrentProfile->autoConnect == false)
1171 return;
1172
1174 for (auto &oneDevice : INDIListener::devices())
1175 {
1176 if (oneDevice->isConnected() == false)
1177 disconnectedDevices << oneDevice->getDeviceName();
1178 }
1179
1180 QString message;
1181
1182 if (disconnectedDevices.count() == 1)
1183 message = i18n("Failed to connect to %1. Please ensure device is connected and powered on.", disconnectedDevices.first());
1184 else
1185 message = i18n("Failed to connect to \n%1\nPlease ensure each device is connected and powered on.",
1186 disconnectedDevices.join("\n"));
1187
1188 appendLogText(message);
1189 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn);
1190 return;
1191 }
1192
1193
1194 if (m_DriverDevicesCount <= 0)
1195 {
1196 m_ekosStatus = Ekos::Success;
1197 emit ekosStatusChanged(m_ekosStatus);
1198 return;
1199 }
1200
1201 if (m_LocalMode)
1202 {
1204 for (auto &drv : managedDrivers)
1205 {
1206 if (drv->getDevices().count() == 0)
1207 remainingDevices << QString("+ %1").arg(
1208 drv->getUniqueLabel().isEmpty() == false ? drv->getUniqueLabel() : drv->getName());
1209 }
1210
1211 if (remainingDevices.count() == 1)
1212 {
1213 QString message = i18n("Unable to establish:\n%1\nPlease ensure the device is connected and powered on.",
1214 remainingDevices.at(0));
1215 appendLogText(message);
1216 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn);
1217 KNotification::beep(i18n("Ekos startup error"));
1218 }
1219 else
1220 {
1221 QString message = i18n("Unable to establish the following devices:\n%1\nPlease ensure each device is connected "
1222 "and powered on.", remainingDevices.join("\n"));
1223 appendLogText(message);
1224 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn);
1225 KNotification::beep(i18n("Ekos startup error"));
1226 }
1227 }
1228 else
1229 {
1231
1232 for (auto &driver : m_CurrentProfile->drivers.values())
1233 {
1234 bool driverFound = false;
1235
1236 for (auto &device : INDIListener::devices())
1237 {
1238 if (device->getBaseDevice().getDriverName() == driver)
1239 {
1240 driverFound = true;
1241 break;
1242 }
1243 }
1244
1245 if (driverFound == false)
1246 remainingDevices << QString("+ %1").arg(driver);
1247 }
1248
1249 if (remainingDevices.count() == 1)
1250 {
1251 QString message = i18n("Unable to remotely establish:\n%1\nPlease ensure the device is connected and powered on.",
1252 remainingDevices.at(0));
1253 appendLogText(message);
1254 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn);
1255 KNotification::beep(i18n("Ekos startup error"));
1256 }
1257 else
1258 {
1259 QString message = i18n("Unable to remotely establish the following devices:\n%1\nPlease ensure each device is connected "
1260 "and powered on.", remainingDevices.join("\n"));
1261 appendLogText(message);
1262 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn);
1263 KNotification::beep(i18n("Ekos startup error"));
1264 }
1265 }
1266
1267 m_ekosStatus = Ekos::Error;
1268}
1269
1270bool Manager::isINDIReady()
1271{
1272 // Check if already connected
1273 int nConnected = 0;
1274
1275 Ekos::CommunicationStatus previousStatus = m_indiStatus;
1276
1277 auto devices = INDIListener::devices();
1278 for (auto &device : devices)
1279 {
1280 // Make sure we're not only connected, but also ready (i.e. all properties have already been defined).
1281 if (device->isConnected() && device->isReady())
1282 nConnected++;
1283 }
1284 if (devices.count() == nConnected)
1285 {
1286 m_indiStatus = Ekos::Success;
1287 emit indiStatusChanged(m_indiStatus);
1288 return true;
1289 }
1290
1291 m_indiStatus = Ekos::Pending;
1292 if (previousStatus != m_indiStatus)
1293 emit indiStatusChanged(m_indiStatus);
1294
1295 return false;
1296}
1297
1298void Manager::connectDevices()
1299{
1300 if (isINDIReady())
1301 return;
1302
1303 auto devices = INDIListener::devices();
1304
1305 for (auto &device : devices)
1306 {
1307 qCDebug(KSTARS_EKOS) << "Connecting " << device->getDeviceName();
1308 device->Connect();
1309 }
1310
1311 connectB->setEnabled(false);
1312 disconnectB->setEnabled(true);
1313
1314 appendLogText(i18n("Connecting INDI devices..."));
1315}
1316
1317void Manager::disconnectDevices()
1318{
1319 for (auto &device : INDIListener::devices())
1320 {
1321 qCDebug(KSTARS_EKOS) << "Disconnecting " << device->getDeviceName();
1322 device->Disconnect();
1323 }
1324
1325 appendLogText(i18n("Disconnecting INDI devices..."));
1326}
1327
1328void Manager::cleanDevices(bool stopDrivers)
1329{
1330 if (m_ekosStatus == Ekos::Idle)
1331 return;
1332
1333 if (mountModule())
1334 mountModule()->stopTimers();
1335
1336 ekosLiveClient->message()->clearPendingProperties();
1337 INDIListener::Instance()->disconnect(this);
1338 DriverManager::Instance()->disconnect(this);
1339
1340 if (managedDrivers.isEmpty() == false)
1341 {
1342 if (m_LocalMode)
1343 {
1344 if (stopDrivers)
1345 DriverManager::Instance()->stopDevices(managedDrivers);
1346 }
1347 else
1348 {
1349 if (stopDrivers)
1350 {
1351 DriverManager::Instance()->disconnectRemoteHost(managedDrivers.first());
1352
1353 if (m_RemoteManagerStart && m_CurrentProfile->INDIWebManagerPort != -1)
1354 INDI::WebManager::stopProfile(m_CurrentProfile);
1355 }
1356 m_RemoteManagerStart = false;
1357 }
1358 }
1359
1360 reset();
1361
1362 profileGroup->setEnabled(true);
1363
1364 appendLogText(i18n("INDI services stopped."));
1365}
1366
1367void Manager::processNewDevice(const QSharedPointer<ISD::GenericDevice> &device)
1368{
1369 qCInfo(KSTARS_EKOS) << "Ekos received a new device: " << device->getDeviceName();
1370
1371 Ekos::CommunicationStatus previousStatus = m_indiStatus;
1372
1373 // for(auto &oneDevice : INDIListener::devices())
1374 // {
1375 // if (oneDevice->getDeviceName() == device->getDeviceName())
1376 // {
1377 // qCWarning(KSTARS_EKOS) << "Found duplicate device, ignoring...";
1378 // return;
1379 // }
1380 // }
1381
1382 // Always reset INDI Connection status if we receive a new device
1383 m_indiStatus = Ekos::Idle;
1384 if (previousStatus != m_indiStatus)
1385 emit indiStatusChanged(m_indiStatus);
1386
1387 m_DriverDevicesCount--;
1388
1389 connect(device.get(), &ISD::GenericDevice::ready, this, &Ekos::Manager::setDeviceReady, Qt::UniqueConnection);
1390 connect(device.get(), &ISD::GenericDevice::newMount, this, &Ekos::Manager::addMount, Qt::UniqueConnection);
1391 connect(device.get(), &ISD::GenericDevice::newCamera, this, &Ekos::Manager::addCamera, Qt::UniqueConnection);
1392 connect(device.get(), &ISD::GenericDevice::newGuider, this, &Ekos::Manager::addGuider, Qt::UniqueConnection);
1393 connect(device.get(), &ISD::GenericDevice::newFilterWheel, this, &Ekos::Manager::addFilterWheel, Qt::UniqueConnection);
1394 connect(device.get(), &ISD::GenericDevice::newFocuser, this, &Ekos::Manager::addFocuser, Qt::UniqueConnection);
1395 connect(device.get(), &ISD::GenericDevice::newDome, this, &Ekos::Manager::addDome, Qt::UniqueConnection);
1396 connect(device.get(), &ISD::GenericDevice::newRotator, this, &Ekos::Manager::addRotator, Qt::UniqueConnection);
1397 connect(device.get(), &ISD::GenericDevice::newWeather, this, &Ekos::Manager::addWeather, Qt::UniqueConnection);
1398 connect(device.get(), &ISD::GenericDevice::newDustCap, this, &Ekos::Manager::addDustCap, Qt::UniqueConnection);
1399 connect(device.get(), &ISD::GenericDevice::newLightBox, this, &Ekos::Manager::addLightBox, Qt::UniqueConnection);
1400 connect(device.get(), &ISD::GenericDevice::newGPS, this, &Ekos::Manager::addGPS, Qt::UniqueConnection);
1401
1402 connect(device.get(), &ISD::GenericDevice::Connected, this, &Ekos::Manager::deviceConnected, Qt::UniqueConnection);
1403 connect(device.get(), &ISD::GenericDevice::Disconnected, this, &Ekos::Manager::deviceDisconnected, Qt::UniqueConnection);
1404 connect(device.get(), &ISD::GenericDevice::propertyDefined, this, &Ekos::Manager::processNewProperty, Qt::UniqueConnection);
1405 connect(device.get(), &ISD::GenericDevice::propertyDeleted, this, &Ekos::Manager::processDeleteProperty,
1407 connect(device.get(), &ISD::GenericDevice::propertyUpdated, this, &Ekos::Manager::processUpdateProperty,
1409 connect(device.get(), &ISD::GenericDevice::messageUpdated, this, &Ekos::Manager::processMessage, Qt::UniqueConnection);
1410
1411
1412
1413 // Only look for primary & guider CCDs if we can tell a difference between them
1414 // otherwise rely on saved options
1415 if (m_CurrentProfile->ccd() != m_CurrentProfile->guider())
1416 {
1417 for (auto &oneCamera : INDIListener::devices())
1418 {
1419 if (oneCamera->getDeviceName().startsWith(m_CurrentProfile->ccd(), Qt::CaseInsensitive))
1420 m_PrimaryCamera = QString(oneCamera->getDeviceName());
1421 else if (oneCamera->getDeviceName().startsWith(m_CurrentProfile->guider(), Qt::CaseInsensitive))
1422 m_GuideCamera = QString(oneCamera->getDeviceName());
1423 }
1424 }
1425
1426 if (m_DriverDevicesCount <= 0)
1427 {
1428 m_ekosStatus = Ekos::Success;
1429 emit ekosStatusChanged(m_ekosStatus);
1430
1431 connectB->setEnabled(true);
1432 disconnectB->setEnabled(false);
1433
1434 if (m_LocalMode == false && m_DriverDevicesCount == 0)
1435 {
1436 if (m_CurrentProfile->autoConnect)
1437 appendLogText(i18n("Remote devices established."));
1438 else
1439 appendLogText(i18n("Remote devices established. Please connect devices."));
1440 }
1441 }
1442}
1443
1444void Manager::deviceConnected()
1445{
1446 connectB->setEnabled(false);
1447 disconnectB->setEnabled(true);
1448 processINDIB->setEnabled(false);
1449
1450 auto device = qobject_cast<ISD::GenericDevice *>(sender());
1451
1452 if (Options::verboseLogging())
1453 {
1454 qCInfo(KSTARS_EKOS) << device->getDeviceName()
1455 << "Version:" << device->getDriverVersion()
1456 << "Interface:" << device->getDriverInterface()
1457 << "is connected.";
1458 }
1459
1460 if (Options::neverLoadConfig() == false)
1461 {
1462 INDIConfig tConfig = Options::loadConfigOnConnection() ? LOAD_LAST_CONFIG : LOAD_DEFAULT_CONFIG;
1463
1464 for (auto &oneDevice : INDIListener::devices())
1465 {
1466 if (oneDevice == device)
1467 {
1468 connect(device, &ISD::GenericDevice::propertyUpdated, this, &Ekos::Manager::watchDebugProperty, Qt::UniqueConnection);
1469
1470 auto configProp = device->getBaseDevice().getSwitch("CONFIG_PROCESS");
1471 if (configProp && configProp.getState() == IPS_IDLE)
1472 device->setConfig(tConfig);
1473 break;
1474 }
1475 }
1476 }
1477}
1478
1479void Manager::deviceDisconnected()
1480{
1481 ISD::GenericDevice * dev = static_cast<ISD::GenericDevice *>(sender());
1482
1483 Ekos::CommunicationStatus previousStatus = m_indiStatus;
1484
1485 if (dev != nullptr)
1486 {
1487 if (dev->getState("CONNECTION") == IPS_ALERT)
1488 m_indiStatus = Ekos::Error;
1489 else if (dev->getState("CONNECTION") == IPS_BUSY)
1490 m_indiStatus = Ekos::Pending;
1491 else
1492 m_indiStatus = Ekos::Idle;
1493
1494 if (Options::verboseLogging())
1495 qCDebug(KSTARS_EKOS) << dev->getDeviceName() << " is disconnected.";
1496
1497 // In case a device fails to connect, display and log a useful message for the user.
1498 if (m_indiStatus == Ekos::Error)
1499 {
1500 QString message = i18n("%1 failed to connect.\nPlease ensure the device is connected and powered on.",
1501 dev->getDeviceName());
1502 appendLogText(message);
1503 KSNotification::event(QLatin1String("IndiServerMessage"), message, KSNotification::General, KSNotification::Warn);
1504 }
1505 else if (m_indiStatus == Ekos::Idle)
1506 {
1507 QString message = i18n("%1 is disconnected.", dev->getDeviceName());
1508 appendLogText(message);
1509 }
1510 }
1511 else
1512 m_indiStatus = Ekos::Idle;
1513
1514 if (previousStatus != m_indiStatus)
1515 emit indiStatusChanged(m_indiStatus);
1516
1517 connectB->setEnabled(true);
1518 disconnectB->setEnabled(false);
1519 processINDIB->setEnabled(true);
1520}
1521
1522void Manager::addMount(ISD::Mount *device)
1523{
1524 ekosLiveClient->message()->sendScopes();
1525
1526 appendLogText(i18n("%1 is online.", device->getDeviceName()));
1527
1528 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1529}
1530
1531void Manager::addCamera(ISD::Camera * device)
1532{
1533 ekosLiveClient.get()->media()->registerCameras();
1534
1535 appendLogText(i18n("%1 is online.", device->getDeviceName()));
1536
1537 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1538}
1539
1540void Manager::addFilterWheel(ISD::FilterWheel * device)
1541{
1542 QString name = device->getDeviceName();
1543 appendLogText(i18n("%1 filter is online.", name));
1544
1545 createFilterManager(device);
1546
1547 emit newDevice(name, device->getDriverInterface());
1548}
1549
1550void Manager::addFocuser(ISD::Focuser *device)
1551{
1552 appendLogText(i18n("%1 focuser is online.", device->getDeviceName()));
1553
1554 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1555}
1556
1557void Manager::addRotator(ISD::Rotator *device)
1558{
1559 appendLogText(i18n("Rotator %1 is online.", device->getDeviceName()));
1560
1561 // createRotatorControl(device);
1562
1563 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1564}
1565
1566void Manager::addDome(ISD::Dome * device)
1567{
1568 appendLogText(i18n("%1 is online.", device->getDeviceName()));
1569
1570 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1571}
1572
1573void Manager::addWeather(ISD::Weather * device)
1574{
1575 appendLogText(i18n("%1 Weather is online.", device->getDeviceName()));
1576
1577 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1578}
1579
1580void Manager::addGPS(ISD::GPS * device)
1581{
1582 appendLogText(i18n("%1 GPS is online.", device->getDeviceName()));
1583
1584 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1585}
1586
1587void Manager::addDustCap(ISD::DustCap * device)
1588{
1589 OpticalTrainManager::Instance()->syncDevices();
1590
1591 appendLogText(i18n("%1 Dust cap is online.", device->getDeviceName()));
1592
1593 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1594}
1595
1596void Manager::addLightBox(ISD::LightBox * device)
1597{
1598 appendLogText(i18n("%1 Light box is online.", device->getDeviceName()));
1599
1600 emit newDevice(device->getDeviceName(), device->getDriverInterface());
1601}
1602
1603void Manager::syncGenericDevice(const QSharedPointer<ISD::GenericDevice> &device)
1604{
1605 createModules(device);
1606
1607 ////////////////////////////////////////////////////////////////////////////////////////////////////
1608 /// Cameras
1609 ////////////////////////////////////////////////////////////////////////////////////////////////////
1610 auto camera = device->getCamera();
1611 if (camera)
1612 {
1613 // Focus Module
1614 if (focusProcess)
1615 {
1616 if (camera->hasCooler())
1617 {
1619 if (INDIListener::findDevice(camera->getDeviceName(), generic))
1620 focusModule()->addTemperatureSource(generic);
1621 }
1622 }
1623
1624 }
1625
1626 ////////////////////////////////////////////////////////////////////////////////////////////////////
1627 /// Mount
1628 ////////////////////////////////////////////////////////////////////////////////////////////////////
1629
1630 ////////////////////////////////////////////////////////////////////////////////////////////////////
1631 /// Focuser
1632 ////////////////////////////////////////////////////////////////////////////////////////////////////
1633 auto focuser = device->getFocuser();
1634 if (focuser)
1635 {
1636 if (focusProcess)
1637 {
1638 // Temperature sources.
1640 if (INDIListener::findDevice(focuser->getDeviceName(), generic))
1641 focusModule()->addTemperatureSource(generic);
1642 }
1643 }
1644
1645 ////////////////////////////////////////////////////////////////////////////////////////////////////
1646 /// Filter Wheel
1647 ////////////////////////////////////////////////////////////////////////////////////////////////////
1648
1649 ////////////////////////////////////////////////////////////////////////////////////////////////////
1650 /// Rotators
1651 ////////////////////////////////////////////////////////////////////////////////////////////////////
1652
1653 ////////////////////////////////////////////////////////////////////////////////////////////////////
1654 /// Domes
1655 ////////////////////////////////////////////////////////////////////////////////////////////////////
1656 auto dome = device->getDome();
1657 if (dome)
1658 {
1659 if (captureProcess)
1660 captureProcess->setDome(dome);
1661 if (alignProcess)
1662 alignProcess->setDome(dome);
1663 if (observatoryProcess)
1664 observatoryProcess->setDome(dome);
1665 }
1666
1667 ////////////////////////////////////////////////////////////////////////////////////////////////////
1668 /// Weather
1669 ////////////////////////////////////////////////////////////////////////////////////////////////////
1670 auto weather = device->getWeather();
1671 if (weather)
1672 {
1673 if (observatoryProcess)
1674 observatoryProcess->addWeatherSource(weather);
1675
1676 if (focusProcess)
1677 {
1679 if (INDIListener::findDevice(weather->getDeviceName(), generic))
1680 focusModule()->addTemperatureSource(generic);
1681 }
1682 }
1683
1684 ////////////////////////////////////////////////////////////////////////////////////////////////////
1685 /// GPS
1686 ////////////////////////////////////////////////////////////////////////////////////////////////////
1687 auto gps = device->getGPS();
1688 if (gps)
1689 {
1690 if (mountProcess)
1691 mountModule()->addGPS(gps);
1692 }
1693
1694 ////////////////////////////////////////////////////////////////////////////////////////////////////
1695 /// Dust Cap
1696 ////////////////////////////////////////////////////////////////////////////////////////////////////
1697
1698 ////////////////////////////////////////////////////////////////////////////////////////////////////
1699 /// Light Box
1700 ////////////////////////////////////////////////////////////////////////////////////////////////////
1701}
1702
1703void Manager::removeDevice(const QSharedPointer<ISD::GenericDevice> &device)
1704{
1705 if (alignProcess)
1706 alignModule()->removeDevice(device);
1707 if (captureProcess)
1708 captureProcess->removeDevice(device);
1709 if (focusProcess)
1710 focusModule()->removeDevice(device);
1711 if (mountProcess)
1712 mountModule()->removeDevice(device);
1713 if (guideProcess)
1714 guideProcess->removeDevice(device);
1715 if (observatoryProcess)
1716 observatoryProcess->removeDevice(device);
1717 if (m_PortSelector)
1718 m_PortSelector->removeDevice(device->getDeviceName());
1719
1720 DarkLibrary::Instance()->removeDevice(device);
1721
1722 // Remove from filter managers
1723 for (auto &oneManager : m_FilterManagers)
1724 {
1725 oneManager->removeDevice(device);
1726 }
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
1744void Manager::processDeleteProperty(INDI::Property prop)
1745{
1746 ekosLiveClient.get()->message()->processDeleteProperty(prop);
1747}
1748
1749void Manager::processMessage(int id)
1750{
1751 auto origin = static_cast<ISD::GenericDevice *>(sender());
1752 // Shouldn't happen
1753 if (!origin)
1754 return;
1756 if (!INDIListener::findDevice(origin->getDeviceName(), device))
1757 return;
1758
1759 ekosLiveClient.get()->message()->processMessage(device, id);
1760}
1761
1762void 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}
1783
1784void Manager::processNewProperty(INDI::Property prop)
1785{
1787 if (!INDIListener::findDevice(prop.getDeviceName(), device))
1788 return;
1789
1790 settleTimer.start();
1791
1792 ekosLiveClient.get()->message()->processNewProperty(prop);
1793
1794 if (prop.isNameMatch("DEVICE_PORT_SCAN") || prop.isNameMatch("CONNECTION_TYPE"))
1795 {
1796 if (!m_PortSelector)
1797 {
1798 m_PortSelector.reset(new Selector::Dialog(KStars::Instance()));
1799 connect(m_PortSelector.get(), &Selector::Dialog::accepted, this, &Manager::setPortSelectionComplete);
1800 }
1801 m_PortSelectorTimer.start();
1802 portSelectorB->setEnabled(true);
1803 m_PortSelector->addDevice(device);
1804 return;
1805 }
1806
1807 // Check if we need to turn on DEBUG for logging purposes
1808 if (prop.isNameMatch("DEBUG"))
1809 {
1810 uint16_t interface = device->getDriverInterface();
1811 if ( opsLogs->getINDIDebugInterface() & interface )
1812 {
1813 // Check if we need to enable debug logging for the INDI drivers.
1814 auto debugSP = prop.getSwitch();
1815 debugSP->at(0)->setState(ISS_ON);
1816 debugSP->at(1)->setState(ISS_OFF);
1817 device->sendNewProperty(debugSP);
1818 }
1819 return;
1820 }
1821
1822 // Handle debug levels for logging purposes
1823 if (prop.isNameMatch("DEBUG_LEVEL"))
1824 {
1825 uint16_t interface = device->getDriverInterface();
1826 // Check if the logging option for the specific device class is on and if the device interface matches it.
1827 if ( opsLogs->getINDIDebugInterface() & interface )
1828 {
1829 // Turn on everything
1830 auto debugLevel = prop.getSwitch();
1831 for (auto &it : *debugLevel)
1832 it.setState(ISS_ON);
1833
1834 device->sendNewProperty(debugLevel);
1835 }
1836 return;
1837 }
1838
1839 if (prop.isNameMatch("ASTROMETRY_SOLVER"))
1840 {
1841 for (auto &oneDevice : INDIListener::devices())
1842 {
1843 if (oneDevice->getDeviceName() == prop.getDeviceName())
1844 {
1845 initAlign();
1846 alignModule()->setAstrometryDevice(oneDevice);
1847 break;
1848 }
1849 }
1850
1851 return;
1852 }
1853
1854 if (focusModule() != nullptr && strstr(prop.getName(), "FOCUS_"))
1855 {
1856 focusModule()->checkFocuser();
1857 return;
1858 }
1859}
1860
1861void Manager::processTabChange()
1862{
1863 auto currentWidget = toolsWidget->currentWidget();
1864
1865 if (alignProcess && alignModule() == currentWidget)
1866 {
1867 auto alignReady = alignModule()->isEnabled() == false && alignModule()->isParserOK();
1868 auto captureReady = captureProcess && captureModule()->isEnabled();
1869 auto mountReady = mountProcess && mountModule()->isEnabled();
1870 if (alignReady && captureReady && mountReady)
1871 alignModule()->setEnabled(true);
1872
1873 alignModule()->checkCamera();
1874 }
1875 else if (captureProcess && currentWidget == captureModule())
1876 {
1877 captureModule()->process()->checkCamera();
1878 }
1879 else if (focusProcess && currentWidget == focusModule())
1880 {
1881 focusModule()->checkCamera();
1882 }
1883 else if (guideProcess && currentWidget == guideModule())
1884 {
1885 guideModule()->checkCamera();
1886 }
1887
1888 updateLog();
1889}
1890
1891void Manager::updateLog()
1892{
1893 QWidget * currentWidget = toolsWidget->currentWidget();
1894
1895 if (currentWidget == setupTab)
1896 ekosLogOut->setPlainText(m_LogText.join("\n"));
1897 else if (currentWidget == alignModule())
1898 ekosLogOut->setPlainText(alignModule()->getLogText());
1899 else if (currentWidget == captureModule())
1900 ekosLogOut->setPlainText(captureModule()->getLogText());
1901 else if (currentWidget == focusModule())
1902 ekosLogOut->setPlainText(focusModule()->getLogText());
1903 else if (currentWidget == guideModule())
1904 ekosLogOut->setPlainText(guideModule()->getLogText());
1905 else if (currentWidget == mountModule())
1906 ekosLogOut->setPlainText(mountModule()->getLogText());
1907 else if (currentWidget == schedulerModule())
1908 ekosLogOut->setPlainText(schedulerModule()->moduleState()->getLogText());
1909 else if (currentWidget == observatoryProcess.get())
1910 ekosLogOut->setPlainText(observatoryProcess->getLogText());
1911 else if (currentWidget == analyzeProcess.get())
1912 ekosLogOut->setPlainText(analyzeProcess->getLogText());
1913
1914#ifdef Q_OS_OSX
1915 repaint(); //This is a band-aid for a bug in QT 5.10.0
1916#endif
1917}
1918
1919void Manager::appendLogText(const QString &text)
1920{
1921 m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2",
1922 KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), text));
1923
1924 qCInfo(KSTARS_EKOS) << text;
1925
1926 emit newLog(text);
1927
1928 updateLog();
1929}
1930
1931void Manager::clearLog()
1932{
1933 QWidget * currentWidget = toolsWidget->currentWidget();
1934
1935 if (currentWidget == setupTab)
1936 {
1937 m_LogText.clear();
1938 updateLog();
1939 }
1940 else if (currentWidget == alignModule())
1941 alignModule()->clearLog();
1942 else if (currentWidget == captureModule())
1943 captureModule()->clearLog();
1944 else if (currentWidget == focusModule())
1945 focusModule()->clearLog();
1946 else if (currentWidget == guideModule())
1947 guideModule()->clearLog();
1948 else if (currentWidget == mountModule())
1949 mountModule()->clearLog();
1950 else if (currentWidget == schedulerModule())
1951 schedulerModule()->moduleState()->clearLog();
1952 else if (currentWidget == observatoryProcess.get())
1953 observatoryProcess->clearLog();
1954 else if (currentWidget == analyzeProcess.get())
1955 analyzeProcess->clearLog();
1956}
1957
1958void Manager::initCapture()
1959{
1960 if (captureModule() != nullptr)
1961 return;
1962
1963 captureProcess.reset(new Capture());
1964
1965 emit newModule("Capture");
1966
1967 // retrieve the meridian flip state machine from the mount module if the module is already present
1968 if (mountModule() != nullptr)
1969 captureModule()->setMeridianFlipState(mountModule()->getMeridianFlipState());
1970
1971 capturePreview->shareCaptureModule(captureModule());
1972 int index = addModuleTab(EkosModule::Capture, captureModule(), QIcon(":/icons/ekos_ccd.png"));
1973 toolsWidget->tabBar()->setTabToolTip(index, i18nc("Charge-Coupled Device", "CCD"));
1974 if (Options::ekosLeftIcons())
1975 {
1977 trans.rotate(90);
1978 QIcon icon = toolsWidget->tabIcon(index);
1979 QPixmap pix = icon.pixmap(QSize(48, 48));
1980 icon = QIcon(pix.transformed(trans));
1981 toolsWidget->setTabIcon(index, icon);
1982 }
1983 connect(captureModule(), &Ekos::Capture::newLog, this, &Ekos::Manager::updateLog);
1984 connect(captureModule(), &Ekos::Capture::newLog, ekosLiveClient.get()->message(),
1985 [this]()
1986 {
1987 QJsonObject cStatus =
1988 {
1989 {"log", captureModule()->getLogText()}
1990 };
1991
1992 ekosLiveClient.get()->message()->updateCaptureStatus(cStatus);
1993 });
1994 connect(captureModule(), &Ekos::Capture::newStatus, this, &Ekos::Manager::updateCaptureStatus);
1995 connect(captureModule(), &Ekos::Capture::newImage, this, &Ekos::Manager::updateCaptureProgress);
1996 connect(captureModule(), &Ekos::Capture::driverTimedout, this, &Ekos::Manager::restartDriver);
1997 connect(captureModule(), &Ekos::Capture::newExposureProgress, this, &Ekos::Manager::updateExposureProgress);
1998 capturePreview->setEnabled(true);
1999
2000 // display capture status changes
2001 connect(captureModule(), &Ekos::Capture::newFilterStatus, capturePreview->captureStatusWidget,
2002 &LedStatusWidget::setFilterState);
2003
2004 // display target drift
2005 connect(schedulerModule(), &Ekos::Scheduler::targetDistance,
2007 connect(schedulerModule(), &Ekos::Scheduler::targetDistance, this, [this](double distance)
2008 {
2009 capturePreview->updateTargetDistance(distance);
2010 });
2011
2012
2013 connectModules();
2014}
2015
2016void Manager::initAlign()
2017{
2018 if (alignModule() != nullptr)
2019 return;
2020
2021 alignProcess.reset(new Ekos::Align(m_CurrentProfile));
2022
2023 emit newModule("Align");
2024
2025 int index = addModuleTab(EkosModule::Align, alignModule(), QIcon(":/icons/ekos_align.png"));
2026 toolsWidget->tabBar()->setTabToolTip(index, i18n("Align"));
2027 connect(alignModule(), &Ekos::Align::newLog, this, &Ekos::Manager::updateLog);
2028 if (Options::ekosLeftIcons())
2029 {
2031 trans.rotate(90);
2032 QIcon icon = toolsWidget->tabIcon(index);
2033 QPixmap pix = icon.pixmap(QSize(48, 48));
2034 icon = QIcon(pix.transformed(trans));
2035 toolsWidget->setTabIcon(index, icon);
2036 }
2037
2038 connectModules();
2039}
2040
2041void Manager::initFocus()
2042{
2043 if (focusModule() != nullptr)
2044 return;
2045
2046 focusProcess.reset(new Ekos::Focus());
2047
2048 emit newModule("Focus");
2049
2050 int index = addModuleTab(EkosModule::Focus, focusModule(), QIcon(":/icons/ekos_focus.png"));
2051
2052 toolsWidget->tabBar()->setTabToolTip(index, i18n("Focus"));
2053
2054 // Focus <---> Manager connections
2055 connect(focusModule(), &Ekos::Focus::newLog, this, &Ekos::Manager::updateLog);
2056 connect(focusModule(), &Ekos::Focus::newStatus, this, &Ekos::Manager::updateFocusStatus);
2057 connect(focusModule(), &Ekos::Focus::newStarPixmap, focusManager, &Ekos::FocusManager::updateFocusStarPixmap);
2058 connect(focusModule(), &Ekos::Focus::newHFR, this, &Ekos::Manager::updateCurrentHFR);
2059 connect(focusModule(), &Ekos::Focus::focuserTimedout, this, &Ekos::Manager::restartDriver);
2060
2061 // connect HFR plot widget
2062 connect(focusModule(), &Ekos::Focus::initHFRPlot, focusManager->hfrVPlot, &FocusHFRVPlot::init);
2063 connect(focusModule(), &Ekos::Focus::redrawHFRPlot, focusManager->hfrVPlot, &FocusHFRVPlot::redraw);
2064 connect(focusModule(), &Ekos::Focus::newHFRPlotPosition, focusManager->hfrVPlot, &FocusHFRVPlot::addPosition);
2065 connect(focusModule(), &Ekos::Focus::drawPolynomial, focusManager->hfrVPlot, &FocusHFRVPlot::drawPolynomial);
2066 connect(focusModule(), &Ekos::Focus::setTitle, focusManager->hfrVPlot, &FocusHFRVPlot::setTitle);
2067 connect(focusModule(), &Ekos::Focus::finalUpdates, focusManager->hfrVPlot, &FocusHFRVPlot::finalUpdates);
2068 connect(focusModule(), &Ekos::Focus::minimumFound, focusManager->hfrVPlot, &FocusHFRVPlot::drawMinimum);
2069 // setup signal/slots for Linear 1 Pass focus algo
2070 connect(focusModule(), &Ekos::Focus::drawCurve, focusManager->hfrVPlot, &FocusHFRVPlot::drawCurve);
2071 connect(focusModule(), &Ekos::Focus::drawCFZ, focusManager->hfrVPlot, &FocusHFRVPlot::drawCFZ);
2072
2073 if (Options::ekosLeftIcons())
2074 {
2076 trans.rotate(90);
2077 QIcon icon = toolsWidget->tabIcon(index);
2078 QPixmap pix = icon.pixmap(QSize(48, 48));
2079 icon = QIcon(pix.transformed(trans));
2080 toolsWidget->setTabIcon(index, icon);
2081 }
2082
2083 focusManager->init();
2084 focusManager->setEnabled(true);
2085
2086 for (auto &oneDevice : INDIListener::devices())
2087 {
2088 auto prop1 = oneDevice->getProperty("CCD_TEMPERATURE");
2089 auto prop2 = oneDevice->getProperty("FOCUSER_TEMPERATURE");
2090 auto prop3 = oneDevice->getProperty("WEATHER_PARAMETERS");
2091 if (prop1 || prop2 || prop3)
2092 focusModule()->addTemperatureSource(oneDevice);
2093 }
2094
2095 connectModules();
2096}
2097
2098void Manager::updateCurrentHFR(double newHFR, int position, bool inAutofocus)
2099{
2101 focusManager->updateCurrentHFR(newHFR);
2102
2104 {
2105 {"hfr", newHFR},
2106 {"pos", position}
2107 };
2108
2109 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2110}
2111
2112void Manager::updateSigmas(double ra, double de)
2113{
2114 guideManager->updateSigmas(ra, de);
2115
2116 QJsonObject cStatus = { {"rarms", ra}, {"derms", de} };
2117
2118 ekosLiveClient.get()->message()->updateGuideStatus(cStatus);
2119}
2120
2121void Manager::initMount()
2122{
2123 if (mountModule() != nullptr)
2124 return;
2125
2126 mountProcess.reset(new Ekos::Mount());
2127
2128 // share the meridian flip state with capture if the module is already present
2129 if (captureModule() != nullptr)
2130 captureModule()->setMeridianFlipState(mountModule()->getMeridianFlipState());
2131
2132 emit newModule("Mount");
2133
2134 int index = addModuleTab(EkosModule::Mount, mountModule(), QIcon(":/icons/ekos_mount.png"));
2135
2136 toolsWidget->tabBar()->setTabToolTip(index, i18n("Mount"));
2137 connect(mountModule(), &Ekos::Mount::newLog, this, &Ekos::Manager::updateLog);
2138 connect(mountModule(), &Ekos::Mount::newCoords, this, &Ekos::Manager::updateMountCoords);
2139 connect(mountModule(), &Ekos::Mount::newStatus, this, &Ekos::Manager::updateMountStatus);
2140 connect(mountModule(), &Ekos::Mount::newTargetName, this, [this](const QString & name)
2141 {
2142 setTarget(name);
2143 });
2144 connect(mountModule(), &Ekos::Mount::pierSideChanged, this, [&](ISD::Mount::PierSide side)
2145 {
2146 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"pierSide", side}}));
2147 });
2148 connect(mountModule()->getMeridianFlipState().get(),
2149 &Ekos::MeridianFlipState::newMountMFStatus, [&](MeridianFlipState::MeridianFlipMountState status)
2150 {
2151 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject(
2152 {
2153 {"meridianFlipStatus", status},
2154 }));
2155 });
2156 connect(mountModule()->getMeridianFlipState().get(),
2157 &Ekos::MeridianFlipState::newMeridianFlipMountStatusText, [&](const QString & text)
2158 {
2159 // Throttle this down
2160 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject(
2161 {
2162 {"meridianFlipText", text},
2163 }), mountModule()->getMeridianFlipState()->getMeridianFlipMountState() == MeridianFlipState::MOUNT_FLIP_NONE);
2164 meridianFlipStatusWidget->setStatus(text);
2165 });
2166 connect(mountModule(), &Ekos::Mount::autoParkCountdownUpdated, this, [&](const QString & text)
2167 {
2168 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"autoParkCountdown", text}}), true);
2169 });
2170
2171 connect(mountModule(), &Ekos::Mount::trainChanged, ekosLiveClient.get()->message(),
2172 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
2173
2174 connect(mountModule(), &Ekos::Mount::slewRateChanged, this, [&](int slewRate)
2175 {
2176 QJsonObject status = { { "slewRate", slewRate} };
2177 ekosLiveClient.get()->message()->updateMountStatus(status);
2178 });
2179
2180 if (Options::ekosLeftIcons())
2181 {
2183 trans.rotate(90);
2184 QIcon icon = toolsWidget->tabIcon(index);
2185 QPixmap pix = icon.pixmap(QSize(48, 48));
2186 icon = QIcon(pix.transformed(trans));
2187 toolsWidget->setTabIcon(index, icon);
2188 }
2189
2190 mountGroup->setEnabled(true);
2191 capturePreview->shareMountModule(mountModule());
2192
2193 connectModules();
2194}
2195
2196void Manager::initGuide()
2197{
2198 if (guideModule() == nullptr)
2199 {
2200 guideProcess.reset(new Ekos::Guide());
2201
2202 emit newModule("Guide");
2203 }
2204
2205 if (toolsWidget->indexOf(guideModule()) == -1)
2206 {
2207 // if (managedDevices.contains(KSTARS_TELESCOPE) && managedDevices.value(KSTARS_TELESCOPE)->isConnected())
2208 // guideProcess->addMount(managedDevices.value(KSTARS_TELESCOPE));
2209
2210 int index = addModuleTab(EkosModule::Guide, guideModule(), QIcon(":/icons/ekos_guide.png"));
2211 toolsWidget->tabBar()->setTabToolTip(index, i18n("Guide"));
2212 connect(guideModule(), &Ekos::Guide::newLog, this, &Ekos::Manager::updateLog);
2213 connect(guideModule(), &Ekos::Guide::driverTimedout, this, &Ekos::Manager::restartDriver);
2214
2215 guideManager->setEnabled(true);
2216
2217 connect(guideModule(), &Ekos::Guide::newStatus, this, &Ekos::Manager::updateGuideStatus);
2218 connect(guideModule(), &Ekos::Guide::newStarPixmap, guideManager, &Ekos::GuideManager::updateGuideStarPixmap);
2219 connect(guideModule(), &Ekos::Guide::newAxisSigma, this, &Ekos::Manager::updateSigmas);
2220 connect(guideModule(), &Ekos::Guide::newAxisDelta, [&](double ra, double de)
2221 {
2222 QJsonObject status = { { "drift_ra", ra}, {"drift_de", de} };
2223 ekosLiveClient.get()->message()->updateGuideStatus(status);
2224 });
2225
2226 if (Options::ekosLeftIcons())
2227 {
2229 trans.rotate(90);
2230 QIcon icon = toolsWidget->tabIcon(index);
2231 QPixmap pix = icon.pixmap(QSize(48, 48));
2232 icon = QIcon(pix.transformed(trans));
2233 toolsWidget->setTabIcon(index, icon);
2234 }
2235 guideManager->init(guideModule());
2236 }
2237
2238 connectModules();
2239}
2240
2241void Manager::initObservatory()
2242{
2243 if (observatoryProcess.get() == nullptr)
2244 {
2245 // Initialize the Observatory Module
2246 observatoryProcess.reset(new Ekos::Observatory());
2247
2248 emit newModule("Observatory");
2249
2250 int index = addModuleTab(EkosModule::Observatory, observatoryProcess.get(), QIcon(":/icons/ekos_observatory.png"));
2251 toolsWidget->tabBar()->setTabToolTip(index, i18n("Observatory"));
2252 connect(observatoryProcess.get(), &Ekos::Observatory::newLog, this, &Ekos::Manager::updateLog);
2253
2254 if (Options::ekosLeftIcons())
2255 {
2257 trans.rotate(90);
2258 QIcon icon = toolsWidget->tabIcon(index);
2259 QPixmap pix = icon.pixmap(QSize(48, 48));
2260 icon = QIcon(pix.transformed(trans));
2261 toolsWidget->setTabIcon(index, icon);
2262 }
2263 }
2264}
2265
2266void Manager::addGuider(ISD::Guider * device)
2267{
2268 appendLogText(i18n("Guider port from %1 is ready.", device->getDeviceName()));
2269}
2270
2271void Manager::removeTabs()
2272{
2273 disconnect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange);
2274
2275 for (int i = numPermanentTabs; i < toolsWidget->count(); i++)
2276 toolsWidget->removeTab(i);
2277
2278 alignProcess.reset();
2279 captureProcess.reset();
2280 focusProcess.reset();
2281 guideProcess.reset();
2282 mountProcess.reset();
2283 observatoryProcess.reset();
2284
2285 connect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange, Qt::UniqueConnection);
2286}
2287
2288bool Manager::isRunning(const QString &process)
2289{
2290 QProcess ps;
2291#ifdef Q_OS_OSX
2292 ps.start("pgrep", QStringList() << process);
2293 ps.waitForFinished();
2294 QString output = ps.readAllStandardOutput();
2295 return output.length() > 0;
2296#else
2297 ps.start("ps", QStringList() << "-o"
2298 << "comm"
2299 << "--no-headers"
2300 << "-C" << process);
2301 ps.waitForFinished();
2302 QString output = ps.readAllStandardOutput();
2303 return output.contains(process);
2304#endif
2305}
2306
2307void Manager::addObjectToScheduler(SkyObject * object)
2308{
2309 if (schedulerModule() != nullptr)
2310 schedulerModule()->addObject(object);
2311}
2312
2313QString Manager::getCurrentJobName()
2314{
2315 return schedulerModule()->getCurrentJobName();
2316}
2317
2318bool Manager::setProfile(const QString &profileName)
2319{
2320 int index = profileCombo->findText(profileName);
2321
2322 if (index < 0)
2323 return false;
2324
2325 profileCombo->setCurrentIndex(index);
2326
2327 return true;
2328}
2329
2330void Manager::editNamedProfile(const QJsonObject &profileInfo)
2331{
2332 ProfileEditor editor(this);
2333 setProfile(profileInfo["name"].toString());
2334 if (getCurrentProfile(m_CurrentProfile))
2335 {
2336 editor.setPi(m_CurrentProfile);
2337 editor.setSettings(profileInfo);
2338 editor.saveProfile();
2339 }
2340}
2341
2342void Manager::addNamedProfile(const QJsonObject &profileInfo)
2343{
2344 ProfileEditor editor(this);
2345
2346 editor.setSettings(profileInfo);
2347 editor.saveProfile();
2348 profiles.clear();
2349 loadProfiles();
2350 profileCombo->setCurrentIndex(profileCombo->count() - 1);
2351 getCurrentProfile(m_CurrentProfile);
2352}
2353
2354void Manager::deleteNamedProfile(const QString &name)
2355{
2356 if (!getCurrentProfile(m_CurrentProfile))
2357 return;
2358
2359 for (auto &pi : profiles)
2360 {
2361 // Do not delete an actively running profile
2362 // Do not delete simulator profile
2363 if (pi->name == "Simulators" || pi->name != name || (pi.get() == m_CurrentProfile && ekosStatus() != Idle))
2364 continue;
2365
2366 KStarsData::Instance()->userdb()->PurgeProfile(pi);
2367 profiles.clear();
2368 loadProfiles();
2369 getCurrentProfile(m_CurrentProfile);
2370 return;
2371 }
2372}
2373
2374QJsonObject Manager::getNamedProfile(const QString &name)
2375{
2377
2378 // Get current profile
2379 for (auto &pi : profiles)
2380 {
2381 if (name == pi->name)
2382 return pi->toJson();
2383 }
2384
2385 return QJsonObject();
2386}
2387
2388QStringList Manager::getProfiles()
2389{
2390 QStringList profiles;
2391
2392 for (int i = 0; i < profileCombo->count(); i++)
2393 profiles << profileCombo->itemText(i);
2394
2395 return profiles;
2396}
2397
2398void Manager::addProfile()
2399{
2400 ProfileEditor editor(this);
2401
2402 if (editor.exec() == QDialog::Accepted)
2403 {
2404 profiles.clear();
2405 loadProfiles();
2406 profileCombo->setCurrentIndex(profileCombo->count() - 1);
2407 }
2408
2409 getCurrentProfile(m_CurrentProfile);
2410}
2411
2412void Manager::editProfile()
2413{
2414 ProfileEditor editor(this);
2415
2416 if (getCurrentProfile(m_CurrentProfile))
2417 {
2418
2419 editor.setPi(m_CurrentProfile);
2420
2421 if (editor.exec() == QDialog::Accepted)
2422 {
2423 int currentIndex = profileCombo->currentIndex();
2424
2425 profiles.clear();
2426 loadProfiles();
2427 profileCombo->setCurrentIndex(currentIndex);
2428 }
2429
2430 getCurrentProfile(m_CurrentProfile);
2431 }
2432}
2433
2434void Manager::deleteProfile()
2435{
2436 if (!getCurrentProfile(m_CurrentProfile))
2437 return;
2438
2439 if (m_CurrentProfile->name == "Simulators")
2440 return;
2441
2442 auto executeDeleteProfile = [&]()
2443 {
2444 KStarsData::Instance()->userdb()->PurgeProfile(m_CurrentProfile);
2445 profiles.clear();
2446 loadProfiles();
2447 getCurrentProfile(m_CurrentProfile);
2448 };
2449
2450 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, executeDeleteProfile]()
2451 {
2452 //QObject::disconnect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, nullptr);
2453 KSMessageBox::Instance()->disconnect(this);
2455 });
2456
2457 KSMessageBox::Instance()->questionYesNo(i18n("Are you sure you want to delete the profile?"),
2458 i18n("Confirm Delete"));
2459
2460}
2461
2462void Manager::wizardProfile()
2463{
2464 ProfileWizard wz;
2465 if (wz.exec() != QDialog::Accepted)
2466 return;
2467
2468 ProfileEditor editor(this);
2469
2470 editor.setProfileName(wz.profileName);
2471 editor.setAuxDrivers(wz.selectedAuxDrivers());
2472 if (wz.useInternalServer == false)
2473 editor.setHostPort(wz.host, wz.port);
2474 editor.setWebManager(wz.useWebManager);
2475 editor.setGuiderType(wz.selectedExternalGuider());
2476 // Disable connection options
2477 editor.setConnectionOptionsEnabled(false);
2478
2479 if (editor.exec() == QDialog::Accepted)
2480 {
2481 profiles.clear();
2482 loadProfiles();
2483 profileCombo->setCurrentIndex(profileCombo->count() - 1);
2484 }
2485
2486 getCurrentProfile(m_CurrentProfile);
2487}
2488
2489bool Manager::getCurrentProfile(QSharedPointer<ProfileInfo> &profile) const
2490{
2491 // Get current profile
2492 for (auto &pi : profiles)
2493 {
2494 if (profileCombo->currentText() == pi->name)
2495 {
2496 profile = pi;
2497 return true;
2498 }
2499 }
2500
2501 return false;
2502}
2503
2504void Manager::updateProfileLocation(const QSharedPointer<ProfileInfo> &profile)
2505{
2506 if (profile->city.isEmpty() == false)
2507 {
2508 bool cityFound = KStars::Instance()->setGeoLocation(profile->city, profile->province, profile->country);
2509 if (cityFound)
2510 appendLogText(i18n("Site location updated to %1.", KStarsData::Instance()->geo()->fullName()));
2511 else
2512 appendLogText(i18n("Failed to update site location to %1. City not found.",
2513 KStarsData::Instance()->geo()->fullName()));
2514 }
2515}
2516
2517void Manager::updateMountStatus(ISD::Mount::Status status)
2518{
2519 static ISD::Mount::Status lastStatus = ISD::Mount::MOUNT_IDLE;
2520
2521 if (status == lastStatus)
2522 return;
2523
2525
2526 mountStatus->setMountState(mountModule()->statusString(), status);
2527 mountStatus->setStyleSheet(QString());
2528
2530 {
2531 {"status", mountModule()->statusString(false)}
2532 };
2533
2534 ekosLiveClient.get()->message()->updateMountStatus(cStatus);
2535}
2536
2537void Manager::updateMountCoords(const SkyPoint position, ISD::Mount::PierSide pierSide, const dms &ha)
2538{
2539 Q_UNUSED(pierSide)
2540 raOUT->setText(position.ra().toHMSString());
2541 decOUT->setText(position.dec().toDMSString());
2542 azOUT->setText(position.az().toDMSString());
2543 altOUT->setText(position.alt().toDMSString());
2544
2546 {
2547 {"ra", dms::fromString(raOUT->text(), false).Degrees()},
2548 {"de", dms::fromString(decOUT->text(), true).Degrees()},
2549 {"ra0", position.ra0().Degrees()},
2550 {"de0", position.dec0().Degrees()},
2551 {"az", dms::fromString(azOUT->text(), true).Degrees()},
2552 {"at", dms::fromString(altOUT->text(), true).Degrees()},
2553 {"ha", ha.Degrees()},
2554 };
2555
2556 ekosLiveClient.get()->message()->updateMountStatus(cStatus, true);
2557}
2558
2559void Manager::updateCaptureStatus(Ekos::CaptureState status)
2560{
2561 capturePreview->updateCaptureStatus(status);
2562
2563 switch (status)
2564 {
2565 case Ekos::CAPTURE_IDLE:
2566 /* Fall through */
2568 /* Fall through */
2570 m_CountdownTimer.stop();
2571 break;
2573 m_CountdownTimer.start();
2574 break;
2575 default:
2576 break;
2577 }
2578
2580 {
2581 {"status", captureStates[status]},
2582 {"seqt", capturePreview->captureCountsWidget->sequenceRemainingTime->text()},
2583 {"ovt", capturePreview->captureCountsWidget->overallRemainingTime->text()}
2584 };
2585
2586 ekosLiveClient.get()->message()->updateCaptureStatus(cStatus);
2587}
2588
2589void Manager::updateCaptureProgress(Ekos::SequenceJob * job, const QSharedPointer<FITSData> &data)
2590{
2591 capturePreview->updateJobProgress(job, data);
2592
2594 {
2595 {"seqv", job->getCompleted()},
2596 {"seqr", job->getCoreProperty(SequenceJob::SJ_Count).toInt()},
2597 {"seql", capturePreview->captureCountsWidget->sequenceRemainingTime->text()}
2598 };
2599
2600 ekosLiveClient.get()->message()->updateCaptureStatus(status);
2601
2602 //const QString filename = ;
2603 //if (!filename.isEmpty() && job->getStatus() == SequenceJob::JOB_BUSY)
2604 if (data && job->getStatus() == JOB_BUSY)
2605 {
2607 uuid = uuid.remove(QRegularExpression("[-{}]"));
2608
2609 // Normally FITS Viewer would trigger an upload
2610 // If off, then rely on summary view or raw data
2611 if (Options::useFITSViewer() == false)
2612 {
2613 if (Options::useSummaryPreview())
2614 ekosLiveClient.get()->media()->sendView(m_SummaryView, uuid);
2615
2616 else
2617 ekosLiveClient.get()->media()->sendData(data, uuid);
2618 }
2619
2620 if (job->jobType() != SequenceJob::JOBTYPE_PREVIEW)
2621 ekosLiveClient.get()->cloud()->upload(data, uuid);
2622
2623 }
2624}
2625
2626void Manager::updateExposureProgress(Ekos::SequenceJob * job)
2627{
2629 {
2630 {"expv", job->getExposeLeft()},
2631 {"expr", job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble()}
2632 };
2633
2634 ekosLiveClient.get()->message()->updateCaptureStatus(status);
2635}
2636
2637void Manager::updateCaptureCountDown()
2638{
2639 capturePreview->updateCaptureCountDown(-1);
2640
2642 {
2643 {"seqt", capturePreview->captureCountsWidget->sequenceRemainingTime->text()},
2644 {"ovt", capturePreview->captureCountsWidget->overallRemainingTime->text()},
2645 {"ovp", capturePreview->captureCountsWidget->gr_overallProgressBar->value()},
2646 {"ovl", capturePreview->captureCountsWidget->gr_overallLabel->text()}
2647 };
2648
2649 ekosLiveClient.get()->message()->updateCaptureStatus(status);
2650}
2651
2652
2653void Manager::updateFocusStatus(Ekos::FocusState status)
2654{
2655 focusManager->updateFocusStatus(status);
2656
2658 {
2659 {"status", getFocusStatusString(status, false)}
2660 };
2661
2662 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2663}
2664
2665void Manager::updateGuideStatus(Ekos::GuideState status)
2666{
2667 guideManager->updateGuideStatus(status);
2669 {
2670 {"status", getGuideStatusString(status, false)}
2671 };
2672
2673 ekosLiveClient.get()->message()->updateGuideStatus(cStatus);
2674}
2675
2676void Manager::setTarget(const QString &name)
2677{
2678 capturePreview->targetLabel->setVisible(!name.isEmpty());
2679 capturePreview->mountTarget->setVisible(!name.isEmpty());
2680 capturePreview->mountTarget->setText(name);
2681 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"target", name}}));
2682}
2683
2684void Manager::showEkosOptions()
2685{
2686 QWidget * currentWidget = toolsWidget->currentWidget();
2687
2688 if (alignModule() && alignModule() == currentWidget)
2689 {
2691 if (alignSettings)
2692 {
2693 alignSettings->setEnabled(true);
2694 alignSettings->show();
2695 }
2696 return;
2697 }
2698
2699 if (guideModule() && guideModule() == currentWidget)
2700 {
2701 KConfigDialog::showDialog("guidesettings");
2702 return;
2703 }
2704
2705 if (focusModule() && focusModule() == currentWidget)
2706 {
2708 if (focusSettings)
2709 {
2710 focusSettings->show();
2711 focusSettings->raise();
2712 }
2713 return;
2714 }
2715
2716 if ((captureModule() && captureModule() == currentWidget) ||
2717 (schedulerModule() && schedulerModule() == currentWidget))
2718 {
2719 if (opsEkos)
2720 {
2721 // Scheduler is tab 1, Capture is tab 2.
2722 const int index = schedulerModule() == currentWidget ? 1 : 2;
2723 opsEkos->setCurrentIndex(index);
2724 }
2726 if (cDialog)
2727 {
2728 cDialog->setCurrentPage(ekosOptionsWidget);
2729 cDialog->show();
2730 cDialog->raise(); // for MacOS
2731 cDialog->activateWindow(); // for Windows
2732 }
2733 return;
2734 }
2735
2736 if (ekosOptionsWidget == nullptr)
2737 {
2738 optionsB->click();
2739 }
2740 else if (KConfigDialog::showDialog("settings"))
2741 {
2743 if (cDialog)
2744 {
2745 cDialog->setCurrentPage(ekosOptionsWidget);
2746 cDialog->show();
2747 cDialog->raise(); // for MacOS
2748 cDialog->activateWindow(); // for Windows
2749 }
2750 }
2751}
2752
2753void Manager::updateDebugInterfaces()
2754{
2756
2757 for (auto &device : INDIListener::devices())
2758 {
2759 auto debugProp = device->getProperty("DEBUG");
2760 if (!debugProp)
2761 continue;
2762
2763 auto debugSP = debugProp.getSwitch();
2764
2765 // Check if the debug interface matches the driver device class
2766 if ( ( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) &&
2767 debugSP->sp[0].s != ISS_ON)
2768 {
2769 debugSP->at(0)->setState(ISS_ON);
2770 debugSP->at(1)->setState(ISS_OFF);
2771 device->sendNewProperty(debugSP);
2772 appendLogText(i18n("Enabling debug logging for %1...", device->getDeviceName()));
2773 }
2774 else if ( !( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) &&
2775 debugSP->sp[0].s != ISS_OFF)
2776 {
2777 debugSP->at(0)->setState(ISS_OFF);
2778 debugSP->at(1)->setState(ISS_ON);
2779 device->sendNewProperty(debugSP);
2780 appendLogText(i18n("Disabling debug logging for %1...", device->getDeviceName()));
2781 }
2782
2783 if (opsLogs->isINDISettingsChanged())
2784 device->setConfig(SAVE_CONFIG);
2785 }
2786}
2787
2788void Manager::watchDebugProperty(INDI::Property prop)
2789{
2790 if (prop.isNameMatch("DEBUG"))
2791 {
2792 auto svp = prop.getSwitch();
2793
2795
2796 // We don't process pure general interfaces
2797 if (deviceInterface->getDriverInterface() == INDI::BaseDevice::GENERAL_INTERFACE)
2798 return;
2799
2800 // If debug was turned off, but our logging policy requires it then turn it back on.
2801 // We turn on debug logging if AT LEAST one driver interface is selected by the logging settings
2802 if (svp->s == IPS_OK && svp->sp[0].s == ISS_OFF &&
2803 (opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface()))
2804 {
2805 svp->sp[0].s = ISS_ON;
2806 svp->sp[1].s = ISS_OFF;
2807 deviceInterface->sendNewProperty(svp);
2808 appendLogText(i18n("Re-enabling debug logging for %1...", deviceInterface->getDeviceName()));
2809 }
2810 // To turn off debug logging, NONE of the driver interfaces should be enabled in logging settings.
2811 // For example, if we have CCD+FilterWheel device and CCD + Filter Wheel logging was turned on in
2812 // the log settings, then if the user turns off only CCD logging, the debug logging is NOT
2813 // turned off until he turns off Filter Wheel logging as well.
2814 else if (svp->s == IPS_OK && svp->sp[0].s == ISS_ON
2815 && !(opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface()))
2816 {
2817 svp->sp[0].s = ISS_OFF;
2818 svp->sp[1].s = ISS_ON;
2819 deviceInterface->sendNewProperty(svp);
2820 appendLogText(i18n("Re-disabling debug logging for %1...", deviceInterface->getDeviceName()));
2821 }
2822 }
2823}
2824
2825void Manager::announceEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event)
2826{
2827 ekosLiveClient.get()->message()->sendEvent(message, source, event);
2828}
2829
2830void Manager::connectModules()
2831{
2832 // Dark Library
2833 connect(DarkLibrary::Instance(), &DarkLibrary::newImage, ekosLiveClient.get()->media(),
2834 &EkosLive::Media::sendDarkLibraryData, Qt::UniqueConnection);
2835 connect(DarkLibrary::Instance(), &DarkLibrary::trainChanged, ekosLiveClient.get()->message(),
2836 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
2837 connect(DarkLibrary::Instance(), &DarkLibrary::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
2839 connect(DarkLibrary::Instance(), &DarkLibrary::settingsUpdated, ekosLiveClient.get()->message(),
2840 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection);
2841
2842 // Guide <---> Capture connections
2843 if (captureProcess && guideProcess)
2844 {
2845 // captureProcess.get()->disconnect(guideProcess.get());
2846 // guideProcess.get()->disconnect(captureProcess.get());
2847
2848 // Guide Limits
2849 connect(guideModule(), &Ekos::Guide::newStatus, captureModule(), &Ekos::Capture::setGuideStatus,
2851 connect(guideModule(), &Ekos::Guide::newAxisDelta, captureModule(), &Ekos::Capture::setGuideDeviation,
2853
2854 // Dithering
2855 connect(captureModule(), &Ekos::Capture::newStatus, guideModule(), &Ekos::Guide::setCaptureStatus,
2857
2858 // Guide Head
2859 connect(captureModule(), &Ekos::Capture::suspendGuiding, guideModule(), &Ekos::Guide::suspend,
2861 connect(captureModule(), &Ekos::Capture::resumeGuiding, guideModule(), &Ekos::Guide::resume,
2863 connect(guideModule(), &Ekos::Guide::guideChipUpdated, captureModule(), &Ekos::Capture::setGuideChip,
2865
2866 // Meridian Flip
2867 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, guideModule(), &Ekos::Guide::abort,
2869 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, guideModule(),
2870 &Ekos::Guide::guideAfterMeridianFlip, Qt::UniqueConnection);
2871 }
2872
2873 // Guide <---> Mount connections
2874 if (guideProcess && mountProcess)
2875 {
2876 // Parking
2877 connect(mountModule(), &Ekos::Mount::newStatus, guideModule(), &Ekos::Guide::setMountStatus,
2879 connect(mountModule(), &Ekos::Mount::newCoords, guideModule(), &Ekos::Guide::setMountCoords,
2881
2882 }
2883
2884 // Focus <---> Guide connections
2885 if (guideProcess && focusProcess)
2886 {
2887 // Suspend
2888 connect(focusModule(), &Ekos::Focus::suspendGuiding, guideModule(), &Ekos::Guide::suspend, Qt::UniqueConnection);
2889 connect(focusModule(), &Ekos::Focus::resumeGuiding, guideModule(), &Ekos::Guide::resume, Qt::UniqueConnection);
2890 }
2891
2892 // Capture <---> Focus connections
2893 if (captureProcess && focusProcess)
2894 {
2895 // Check focus HFR value and if above threshold parameter, run autoFocus
2896 connect(captureModule(), &Ekos::Capture::checkFocus, focusModule(), &Ekos::Focus::checkFocus,
2898
2899 // Run autoFocus
2900 connect(captureProcess.get(), &Ekos::Capture::runAutoFocus, focusProcess.get(), &Ekos::Focus::runAutoFocus,
2902
2903 // Reset Focus
2904 connect(captureModule(), &Ekos::Capture::resetFocus, focusModule(), &Ekos::Focus::resetFrame,
2906
2907 // Abort Focus
2908 connect(captureModule(), &Ekos::Capture::abortFocus, focusModule(), &Ekos::Focus::abort,
2910
2911 // New Focus Status
2912 connect(focusModule(), &Ekos::Focus::newStatus, captureModule(), &Ekos::Capture::setFocusStatus,
2914
2915 // Perform adaptive focus
2916 connect(captureModule(), &Ekos::Capture::adaptiveFocus, focusModule(), &Ekos::Focus::adaptiveFocus,
2918
2919 // New Adaptive Focus Status
2920 connect(focusModule(), &Ekos::Focus::focusAdaptiveComplete, captureModule(),
2923
2924 // New Focus HFR
2925 connect(focusModule(), &Ekos::Focus::newHFR, captureModule(), &Ekos::Capture::setHFR, Qt::UniqueConnection);
2926
2927 // New Focus temperature delta
2928 connect(focusModule(), &Ekos::Focus::newFocusTemperatureDelta, captureModule(),
2930
2931 // Meridian Flip
2932 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, focusModule(), &Ekos::Focus::meridianFlipStarted,
2934 }
2935
2936 // Capture <---> Align connections
2937 if (captureProcess && alignProcess)
2938 {
2939 // Alignment flag
2940 connect(alignModule(), &Ekos::Align::newStatus, captureModule(), &Ekos::Capture::setAlignStatus,
2942 // Solver data
2943 connect(alignModule(), &Ekos::Align::newSolverResults, captureModule(), &Ekos::Capture::setAlignResults,
2945 // Capture Status
2946 connect(captureModule(), &Ekos::Capture::newStatus, alignModule(), &Ekos::Align::setCaptureStatus,
2948 }
2949
2950 // Capture <---> Mount connections
2951 if (captureProcess && mountProcess)
2952 {
2953 // Register both modules since both are now created and ready
2954 // In case one module misses the DBus signal, then it will be correctly initialized.
2955 captureModule()->registerNewModule("Mount");
2956 mountModule()->registerNewModule("Capture");
2957
2958 // Meridian Flip states
2959 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, mountModule(), &Ekos::Mount::suspendAltLimits,
2961 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, mountModule(), &Ekos::Mount::resumeAltLimits,
2963
2964 // Mount Status
2965 connect(mountModule(), &Ekos::Mount::newStatus, captureModule(), &Ekos::Capture::setMountStatus,
2967 }
2968
2969 // Optical Train Manager ---> EkosLive connections
2970 if (ekosLiveClient)
2971 {
2972 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated, ekosLiveClient->message(),
2973 &EkosLive::Message::sendTrains, Qt::UniqueConnection);
2974 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::configurationRequested, ekosLiveClient->message(),
2975 &EkosLive::Message::requestOpticalTrains, Qt::UniqueConnection);
2976 }
2977
2978 // Capture <---> EkosLive connections
2979 if (captureProcess && ekosLiveClient)
2980 {
2981 //captureProcess.get()->disconnect(ekosLiveClient.get()->message());
2982
2983 connect(captureModule(), &Ekos::Capture::dslrInfoRequested, ekosLiveClient.get()->message(),
2984 &EkosLive::Message::requestDSLRInfo, Qt::UniqueConnection);
2985 connect(captureModule(), &Ekos::Capture::sequenceChanged, ekosLiveClient.get()->message(),
2986 &EkosLive::Message::sendCaptureSequence, Qt::UniqueConnection);
2987 connect(captureModule(), &Ekos::Capture::settingsUpdated, ekosLiveClient.get()->message(),
2988 &EkosLive::Message::sendCaptureSettings, Qt::UniqueConnection);
2989 connect(captureModule(), &Ekos::Capture::newLocalPreview, ekosLiveClient.get()->message(),
2990 &EkosLive::Message::sendPreviewLabel, Qt::UniqueConnection);
2991 connect(captureModule(), &Ekos::Capture::trainChanged, ekosLiveClient.get()->message(),
2992 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
2993 }
2994
2995 // Focus <---> Align connections
2996 if (focusProcess && alignProcess)
2997 {
2998 connect(focusModule(), &Ekos::Focus::newStatus, alignModule(), &Ekos::Align::setFocusStatus,
3000 }
3001
3002 // Focus <---> Mount connections
3003 if (focusProcess && mountProcess)
3004 {
3005 connect(mountModule(), &Ekos::Mount::newStatus, focusModule(), &Ekos::Focus::setMountStatus,
3007 connect(mountModule(), &Ekos::Mount::newCoords, focusModule(), &Ekos::Focus::setMountCoords,
3009 }
3010
3011 // Mount <---> Align connections
3012 if (mountProcess && alignProcess)
3013 {
3014 connect(mountModule(), &Ekos::Mount::newStatus, alignModule(), &Ekos::Align::setMountStatus,
3016 connect(mountModule(), &Ekos::Mount::newTarget, alignModule(), &Ekos::Align::setTarget,
3020 connect(alignModule(), &Ekos::Align::newPAAStage, mountModule(), &Ekos::Mount::paaStageChanged,
3022 }
3023
3024 // Mount <---> Guide connections
3025 if (mountProcess && guideProcess)
3026 {
3027 connect(mountModule(), &Ekos::Mount::pierSideChanged, guideModule(), &Ekos::Guide::setPierSide,
3029 }
3030
3031 // Align <--> EkosLive connections
3032 if (alignProcess && ekosLiveClient)
3033 {
3034 // alignProcess.get()->disconnect(ekosLiveClient.get()->message());
3035 // alignProcess.get()->disconnect(ekosLiveClient.get()->media());
3036
3037 connect(alignModule(), &Ekos::Align::newStatus, ekosLiveClient.get()->message(), &EkosLive::Message::setAlignStatus,
3039 connect(alignModule(), &Ekos::Align::newSolution, ekosLiveClient.get()->message(),
3040 &EkosLive::Message::setAlignSolution, Qt::UniqueConnection);
3041 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHStage,
3042 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHStage,
3044 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHMessage,
3045 ekosLiveClient.get()->message(),
3046 &EkosLive::Message::setPAHMessage, Qt::UniqueConnection);
3047 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::PAHEnabled,
3048 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHEnabled,
3050 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::polarResultUpdated,
3051 ekosLiveClient.get()->message(),
3052 &EkosLive::Message::setPolarResults, Qt::UniqueConnection);
3053 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::updatedErrorsChanged,
3054 ekosLiveClient.get()->message(),
3055 &EkosLive::Message::setUpdatedErrors, Qt::UniqueConnection);
3056 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newCorrectionVector,
3057 ekosLiveClient.get()->media(),
3058 &EkosLive::Media::setCorrectionVector, Qt::UniqueConnection);
3059
3060 connect(alignModule(), &Ekos::Align::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3062 connect(alignModule(), &Ekos::Align::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendUpdatedFrame,
3064
3065 connect(alignModule(), &Ekos::Align::settingsUpdated, ekosLiveClient.get()->message(),
3066 &EkosLive::Message::sendAlignSettings, Qt::UniqueConnection);
3067
3068 connect(alignModule(), &Ekos::Align::trainChanged, ekosLiveClient.get()->message(),
3069 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3070
3071 connect(alignModule(), &Ekos::Align::manualRotatorChanged, ekosLiveClient.get()->message(),
3072 &EkosLive::Message::sendManualRotatorStatus, Qt::UniqueConnection);
3073 }
3074
3075 // Focus <--> EkosLive Connections
3076 if (focusProcess && ekosLiveClient)
3077 {
3078 connect(focusModule(), &Ekos::Focus::settingsUpdated, ekosLiveClient.get()->message(),
3079 &EkosLive::Message::sendFocusSettings, Qt::UniqueConnection);
3080
3081 connect(focusModule(), &Ekos::Focus::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3083
3084 connect(focusModule(), &Ekos::Focus::trainChanged, ekosLiveClient.get()->message(),
3085 &EkosLive::Message::sendTrainProfiles,
3087
3088 connect(focusModule(), &Ekos::Focus::autofocusAborted,
3089 ekosLiveClient.get()->message(), &EkosLive::Message::autofocusAborted, Qt::UniqueConnection);
3090 }
3091
3092 // Guide <--> EkosLive Connections
3093 if (guideProcess && ekosLiveClient)
3094 {
3095 connect(guideModule(), &Ekos::Guide::settingsUpdated, ekosLiveClient.get()->message(),
3096 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection);
3097
3098 connect(guideModule(), &Ekos::Guide::trainChanged, ekosLiveClient.get()->message(),
3099 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3100
3101 connect(guideModule(), &Ekos::Guide::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3103 }
3104
3105 // Analyze connections.
3106 if (analyzeProcess)
3107 {
3108 // Scheduler <---> Analyze
3109 connect(schedulerModule(), &Ekos::Scheduler::jobStarted,
3110 analyzeProcess.get(), &Ekos::Analyze::schedulerJobStarted, Qt::UniqueConnection);
3111 connect(schedulerModule(), &Ekos::Scheduler::jobEnded,
3112 analyzeProcess.get(), &Ekos::Analyze::schedulerJobEnded, Qt::UniqueConnection);
3113 connect(schedulerModule(), &Ekos::Scheduler::targetDistance,
3114 analyzeProcess.get(), &Ekos::Analyze::newTargetDistance, Qt::UniqueConnection);
3115
3116 // Capture <---> Analyze
3117 if (captureProcess)
3118 {
3119 connect(captureModule(), &Ekos::Capture::captureComplete,
3120 analyzeProcess.get(), &Ekos::Analyze::captureComplete, Qt::UniqueConnection);
3121 connect(captureModule(), &Ekos::Capture::captureStarting,
3122 analyzeProcess.get(), &Ekos::Analyze::captureStarting, Qt::UniqueConnection);
3123 connect(captureModule(), &Ekos::Capture::captureAborted,
3124 analyzeProcess.get(), &Ekos::Analyze::captureAborted, Qt::UniqueConnection);
3125#if 0
3126 // Meridian Flip
3127 connect(captureModule(), &Ekos::Capture::meridianFlipStarted,
3128 analyzeProcess.get(), &Ekos::Analyze::meridianFlipStarted, Qt::UniqueConnection);
3129 connect(captureModule(), &Ekos::Capture::meridianFlipCompleted,
3130 analyzeProcess.get(), &Ekos::Analyze::meridianFlipComplete, Qt::UniqueConnection);
3131#endif
3132 }
3133
3134 // Guide <---> Analyze
3135 if (guideProcess)
3136 {
3137 connect(guideModule(), &Ekos::Guide::newStatus,
3138 analyzeProcess.get(), &Ekos::Analyze::guideState, Qt::UniqueConnection);
3139
3140 connect(guideModule(), &Ekos::Guide::guideStats,
3141 analyzeProcess.get(), &Ekos::Analyze::guideStats, Qt::UniqueConnection);
3142 }
3143 }
3144
3145
3146 // Focus <---> Analyze connections
3147 if (focusProcess && analyzeProcess)
3148 {
3149 connect(focusModule(), &Ekos::Focus::autofocusComplete,
3150 analyzeProcess.get(), &Ekos::Analyze::autofocusComplete, Qt::UniqueConnection);
3152 analyzeProcess.get(), &Ekos::Analyze::adaptiveFocusComplete, Qt::UniqueConnection);
3153 connect(focusModule(), &Ekos::Focus::autofocusStarting,
3154 analyzeProcess.get(), &Ekos::Analyze::autofocusStarting, Qt::UniqueConnection);
3155 connect(focusModule(), &Ekos::Focus::autofocusAborted,
3156 analyzeProcess.get(), &Ekos::Analyze::autofocusAborted, Qt::UniqueConnection);
3157 connect(focusModule(), &Ekos::Focus::newFocusTemperatureDelta,
3158 analyzeProcess.get(), &Ekos::Analyze::newTemperature, Qt::UniqueConnection);
3159 }
3160
3161 // Align <---> Analyze connections
3162 if (alignProcess && analyzeProcess)
3163 {
3164 connect(alignModule(), &Ekos::Align::newStatus,
3165 analyzeProcess.get(), &Ekos::Analyze::alignState, Qt::UniqueConnection);
3166
3167 }
3168
3169 // Mount <---> Analyze connections
3170 if (mountProcess && analyzeProcess)
3171 {
3172 connect(mountModule(), &Ekos::Mount::newStatus,
3173 analyzeProcess.get(), &Ekos::Analyze::mountState, Qt::UniqueConnection);
3174 connect(mountModule(), &Ekos::Mount::newCoords,
3175 analyzeProcess.get(), &Ekos::Analyze::mountCoords, Qt::UniqueConnection);
3176 connect(mountModule()->getMeridianFlipState().get(), &Ekos::MeridianFlipState::newMountMFStatus,
3177 analyzeProcess.get(), &Ekos::Analyze::mountFlipStatus, Qt::UniqueConnection);
3178 }
3179}
3180
3181void Manager::setEkosLiveConnected(bool enabled)
3182{
3183 ekosLiveClient.get()->setConnected(enabled);
3184}
3185
3186void Manager::setEkosLiveConfig(bool rememberCredentials, bool autoConnect)
3187{
3188 ekosLiveClient.get()->setConfig(rememberCredentials, autoConnect);
3189}
3190
3191void Manager::setEkosLiveUser(const QString &username, const QString &password)
3192{
3193 ekosLiveClient.get()->setUser(username, password);
3194}
3195
3196bool Manager::ekosLiveStatus()
3197{
3198 return ekosLiveClient.get()->isConnected();
3199}
3200
3201bool Manager::checkUniqueBinaryDriver(const QSharedPointer<DriverInfo> &primaryDriver,
3203{
3205 return false;
3206
3207 return (primaryDriver->getExecutable() == secondaryDriver->getExecutable() &&
3208 primaryDriver->getAuxInfo().value("mdpd", false).toBool() == true);
3209}
3210
3211void Manager::restartDriver(const QString &deviceName)
3212{
3213 qCInfo(KSTARS_EKOS) << "Restarting driver" << deviceName;
3214 if (m_LocalMode)
3215 {
3216 for (auto &oneDevice : INDIListener::devices())
3217 {
3218 if (oneDevice->getDeviceName() == deviceName)
3219 {
3220 DriverManager::Instance()->restartDriver(oneDevice->getDriverInfo());
3221 break;
3222 }
3223 }
3224 }
3225 else
3226 INDI::WebManager::restartDriver(m_CurrentProfile, deviceName);
3227}
3228
3229void Manager::setEkosLoggingEnabled(const QString &name, bool enabled)
3230{
3231 // LOGGING, FILE, DEFAULT are exclusive, so one of them must be SET to TRUE
3232 if (name == "LOGGING")
3233 {
3234 Options::setDisableLogging(!enabled);
3235 if (!enabled)
3237 }
3238 else if (name == "FILE")
3239 {
3240 Options::setLogToFile(enabled);
3241 if (enabled)
3243 }
3244 else if (name == "DEFAULT")
3245 {
3246 Options::setLogToDefault(enabled);
3247 if (enabled)
3249 }
3250 // VERBOSE should be set to TRUE if INDI or Ekos logging is selected.
3251 else if (name == "VERBOSE")
3252 {
3253 Options::setVerboseLogging(enabled);
3255 }
3256 // Toggle INDI Logging
3257 else if (name == "INDI")
3258 {
3259 Options::setINDILogging(enabled);
3261 }
3262 else if (name == "FITS")
3263 {
3264 Options::setFITSLogging(enabled);
3266 }
3267 else if (name == "CAPTURE")
3268 {
3269 Options::setCaptureLogging(enabled);
3270 Options::setINDICCDLogging(enabled);
3271 Options::setINDIFilterWheelLogging(enabled);
3273 }
3274 else if (name == "FOCUS")
3275 {
3276 Options::setFocusLogging(enabled);
3277 Options::setINDIFocuserLogging(enabled);
3279 }
3280 else if (name == "GUIDE")
3281 {
3282 Options::setGuideLogging(enabled);
3283 Options::setINDICCDLogging(enabled);
3285 }
3286 else if (name == "ALIGNMENT")
3287 {
3288 Options::setAlignmentLogging(enabled);
3290 }
3291 else if (name == "MOUNT")
3292 {
3293 Options::setMountLogging(enabled);
3294 Options::setINDIMountLogging(enabled);
3296 }
3297 else if (name == "SCHEDULER")
3298 {
3299 Options::setSchedulerLogging(enabled);
3301 }
3302 else if (name == "OBSERVATORY")
3303 {
3304 Options::setObservatoryLogging(enabled);
3306 }
3307}
3308
3309void Manager::acceptPortSelection()
3310{
3311 if (m_PortSelector)
3312 m_PortSelector->accept();
3313}
3314
3315void Manager::setPortSelectionComplete()
3316{
3317 if (m_CurrentProfile->portSelector)
3318 {
3319 // Turn off port selector
3320 m_CurrentProfile->portSelector = false;
3321 KStarsData::Instance()->userdb()->SaveProfile(m_CurrentProfile);
3322 }
3323
3324 if (m_CurrentProfile->autoConnect)
3325 connectDevices();
3326}
3327
3328void Manager::activateModule(const QString &name, bool popup)
3329{
3330 auto child = toolsWidget->findChild<QWidget *>(name);
3331 if (child)
3332 {
3333 toolsWidget->setCurrentWidget(child);
3334 if (popup)
3335 {
3336 raise();
3337 activateWindow();
3338 showNormal();
3339 }
3340 }
3341}
3342
3343void Manager::createModules(const QSharedPointer<ISD::GenericDevice> &device)
3344{
3345 if (device->isConnected())
3346 {
3347 if (device->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE)
3348 {
3349 initCapture();
3350 initFocus();
3351 initAlign();
3352 initGuide();
3353 }
3354 if (device->getDriverInterface() & INDI::BaseDevice::FILTER_INTERFACE)
3355 {
3356 initCapture();
3357 initFocus();
3358 initAlign();
3359 }
3360 if (device->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE)
3361 initFocus();
3362 if (device->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)
3363 {
3364 initCapture();
3365 initAlign();
3366 initGuide();
3367 initMount();
3368 }
3369 if (device->getDriverInterface() & INDI::BaseDevice::ROTATOR_INTERFACE)
3370 {
3371 initCapture();
3372 initAlign();
3373 }
3374 if (device->getDriverInterface() & INDI::BaseDevice::DOME_INTERFACE)
3375 {
3376 initCapture();
3377 initAlign();
3378 initObservatory();
3379 }
3380 if (device->getDriverInterface() & INDI::BaseDevice::WEATHER_INTERFACE)
3381 {
3382 initFocus();
3383 initObservatory();
3384 }
3385 if (device->getDriverInterface() & INDI::BaseDevice::DUSTCAP_INTERFACE)
3386 {
3387 initCapture();
3388 }
3389 if (device->getDriverInterface() & INDI::BaseDevice::LIGHTBOX_INTERFACE)
3390 {
3391 initCapture();
3392 }
3393 if (device->getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)
3394 {
3395 initMount();
3396 }
3397 }
3398}
3399
3400void Manager::setDeviceReady()
3401{
3402 // Check if ALL our devices are ready.
3403 // Ready indicates that all properties have been defined.
3404 if (isINDIReady() == false)
3405 {
3406 auto device = static_cast<ISD::GenericDevice*>(sender());
3407 if (device)
3408 {
3409
3410 if (device->isConnected() == false && m_CurrentProfile->autoConnect)
3411 {
3412 // Do we have port selector checked?
3413 if (m_CurrentProfile->portSelector)
3414 {
3415 // If port selector was not initialized, kick off the timer
3416 // so we can check if all devices should be connected.
3417 // Otherwise, if port selector is started, then let user
3418 // select ports first and then manually connect time.
3419 if (!m_PortSelector)
3420 m_PortSelectorTimer.start();
3421 }
3422 else
3423 {
3424 qCInfo(KSTARS_EKOS) << "Connecting to" << device->getDeviceName();
3425 device->Connect();
3426 }
3427 }
3428 else
3429 qCInfo(KSTARS_EKOS) << device->getDeviceName() << "is connected and ready.";
3430 }
3431
3432 if (m_ekosStatus != Ekos::Success)
3433 return;
3434 }
3435
3436 // If port selector is active, then do not show optical train dialog unless it is dismissed first.
3437 if (m_DriverDevicesCount <= 0 && (m_CurrentProfile->portSelector == false || !m_PortSelector))
3438 {
3439 for (auto &device : INDIListener::devices())
3440 syncGenericDevice(device);
3441 OpticalTrainManager::Instance()->setProfile(m_CurrentProfile);
3442 }
3443}
3444
3445void Manager::createFilterManager(ISD::FilterWheel *device)
3446{
3447 auto name = device->getDeviceName();
3448 if (m_FilterManagers.contains(name) == false)
3449 {
3450 QSharedPointer<FilterManager> newFM(new FilterManager(this));
3451 newFM->setFilterWheel(device);
3452 m_FilterManagers[name] = newFM;
3453 }
3454 else
3455 m_FilterManagers[name]->setFilterWheel(device);
3456
3457}
3458
3459bool Manager::getFilterManager(const QString &name, QSharedPointer<FilterManager> &fm)
3460{
3461 if (m_FilterManagers.contains(name))
3462 {
3463 fm = m_FilterManagers[name];
3464 return true;
3465 }
3466 return false;
3467}
3468
3469bool Manager::getFilterManager(QSharedPointer<FilterManager> &fm)
3470{
3471 if (m_FilterManagers.size() > 0)
3472 {
3473 fm = m_FilterManagers.values()[0];
3474 return true;
3475 }
3476 return false;
3477}
3478
3479void Manager::createRotatorController(ISD::Rotator *device)
3480{
3481 auto Name = device->getDeviceName();
3482 if (m_RotatorControllers.contains(Name) == false)
3483 {
3484 QSharedPointer<RotatorSettings> newRC(new RotatorSettings(this));
3485 // Properties are fetched in RotatorSettings::initRotator!
3486 m_RotatorControllers[Name] = newRC;
3487 }
3488}
3489
3490bool Manager::getRotatorController(const QString &Name, QSharedPointer<RotatorSettings> &rs)
3491{
3492 if (m_RotatorControllers.contains(Name))
3493 {
3494 rs = m_RotatorControllers[Name];
3495 return true;
3496 }
3497 return false;
3498}
3499
3500bool Manager::existRotatorController()
3501{
3502 return (!m_RotatorControllers.empty());
3503}
3504
3505}
DriverInfo holds all metadata associated with a particular INDI driver.
Definition driverinfo.h:46
DriverManager is the primary class to handle all operations related to starting and stopping INDI dri...
Align class handles plate-solving and polar alignment measurement and correction using astrometry....
Definition align.h:74
Analysis tab for Ekos sessions.
Definition analyze.h:35
void setFocusTemperatureDelta(double focusTemperatureDelta, double absTemperature)
updateAdaptiveFocusStatus Handle new focus state
Definition capture.cpp:2057
void setHFR(double newHFR, int position, bool inAutofocus)
setHFR Receive the measured HFR value of the latest frame
Definition capture.cpp:3704
void updateTargetDistance(double targetDiff)
Slot receiving the update of the current target distance.
Definition capture.cpp:1626
void setFocusStatus(FocusState newstate)
setFocusStatus Forward the new focus state to the capture module state machine
Definition capture.cpp:2075
void setGuideDeviation(double delta_ra, double delta_dec)
setGuideDeviation Set the guiding deviation as measured by the guiding module.
Definition capture.cpp:2066
void focusAdaptiveComplete(bool success)
focusAdaptiveComplete Forward the new focus state to the capture module state machine
Definition capture.h:802
Supports manual focusing and auto focusing using relative and absolute INDI focusers.
Definition focus.h:51
void drawPolynomial(PolynomialFit *poly, bool isVShape, bool activate, bool plot=true)
draw the approximating polynomial into the HFR V-graph
void newHFRPlotPosition(double pos, double hfr, double sigma, bool outlier, int pulseDuration, bool plot=true)
new HFR plot position with sigma
void redrawHFRPlot(PolynomialFit *poly, double solutionPosition, double solutionValue)
redraw the entire HFR plot
void runAutoFocus(const AutofocusReason autofocusReason, const QString &reasonInfo)
Run the autofocus process for the currently selected filter.
Definition focus.cpp:1012
void focuserTimedout(const QString &focuser)
focuserTimedout responding to requests
void initHFRPlot(QString str, double starUnits, bool minimum, bool useWeights, bool showPosition)
initialize the HFR V plot
void adaptiveFocus()
adaptiveFocus moves the focuser between subframes to stay at focus
Definition focus.cpp:986
Q_SCRIPTABLE Q_NOREPLY void resetFrame()
DBUS interface function.
Definition focus.cpp:311
void meridianFlipStarted()
React when a meridian flip has been started.
Definition focus.cpp:1410
void drawCFZ(double minPosition, double minValue, int m_cfzSteps, bool plt)
Draw Critical Focus Zone on graph.
void finalUpdates(const QString &title, bool plot=true)
final updates after focus run comopletes on the focus plot
void minimumFound(double solutionPosition, double solutionValue, bool plot=true)
Focus solution with minimal HFR found.
void setTitle(const QString &title, bool plot=true)
draw a title on the focus plot
void drawCurve(CurveFitting *curve, bool isVShape, bool activate, bool plot=true)
draw the curve into the HFR V-graph
void adaptiveFocusComplete(const QString &filter, double temperature, double tempTicks, double altitude, double altTicks, int prevPosError, int thisPosError, int totalTicks, int position, bool focuserMoved)
Signal Analyze that an Adaptive Focus iteration is complete.
Performs calibration and autoguiding using an ST4 port or directly via the INDI driver.
Definition guide.h:51
Q_SCRIPTABLE bool resume()
DBUS interface function.
Definition guide.cpp:1351
Q_SCRIPTABLE bool suspend()
DBUS interface function.
Definition guide.cpp:1341
Q_SCRIPTABLE bool abort()
DBUS interface function.
Definition guide.cpp:819
Supports controlling INDI telescope devices including setting/retrieving mount properties,...
Definition mount.h:33
void newTarget(SkyPoint &currentCoord)
The mount has finished the slew to a new target.
void paaStageChanged(int stage)
React upon status changes of the polar alignment - mainly to avoid meridian flips happening during po...
Definition mount.cpp:793
void newTargetName(const QString &name)
The mount has finished the slew to a new target.
void newStatus(ISD::Mount::Status status)
Change in the mount status.
void suspendAltLimits()
suspendAltLimits calls enableAltitudeLimits(false).
Definition mount.cpp:929
void newCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha)
Update event with the current telescope position.
void resumeAltLimits()
resumeAltLimits calls enableAltitudeLimits(true).
Definition mount.cpp:921
Enables the user to set logging options.
Definition opslogs.h:23
INDIListener is responsible for creating ISD::GDInterface generic devices as new devices arrive from ...
Camera class controls an INDI Camera device.
Definition indicamera.h:47
void sendNewProperty(INDI::Property prop)
Send new property command to server.
Class handles control of INDI dome devices.
Definition indidome.h:23
Handles operation of a remotely controlled dust cover cap.
Definition indidustcap.h:23
Focuser class handles control of INDI focuser devices.
Definition indifocuser.h:21
GenericDevice is the Generic Device for INDI devices.
Definition indistd.h:117
Handles operation of a remotely controlled light box.
device handle controlling Mounts.
Definition indimount.h:27
Rotator class handles control of INDI Rotator devices.
Definition indirotator.h:20
Focuser class handles control of INDI Weather devices.
Definition indiweather.h:24
static bool showDialog(const QString &name)
KPageWidgetItem * addPage(QWidget *page, const QString &itemName, const QString &pixmapName=QString(), const QString &header=QString(), bool manage=true)
static KConfigDialog * exists(const QString &name)
static void beep(const QString &reason=QString())
QPushButton * button(QDialogButtonBox::StandardButton which) const
void setIcon(const QIcon &icon)
static void UseDefault()
Use the default logging mechanism.
Definition ksutils.cpp:1024
static void SyncFilterRules()
SyncFilterRules Sync QtLogging filter rules from Options.
Definition ksutils.cpp:1036
static void Disable()
Disable logging.
Definition ksutils.cpp:1029
static void UseFile()
Store all logs into the specified file.
Definition ksutils.cpp:927
static KStarsDateTime currentDateTimeUtc()
This is the main window for KStars.
Definition kstars.h:91
static KStars * Instance()
Definition kstars.h:123
Primary class to handle all Ekos modules.
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
The sky coordinates of a point in the sky.
Definition skypoint.h:45
const CachingDms & dec() const
Definition skypoint.h:269
const CachingDms & ra0() const
Definition skypoint.h:251
const CachingDms & ra() const
Definition skypoint.h:263
const dms & az() const
Definition skypoint.h:275
const dms & alt() const
Definition skypoint.h:281
const CachingDms & dec0() const
Definition skypoint.h:257
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition dms.cpp:429
const double & Degrees() const
Definition dms.h:141
void setTarget(const SkyPoint &targetCoord)
Set the alignment target where the mount is expected to point at.
Definition align.cpp:3802
void setTelescopeCoordinates(const SkyPoint &position)
Set the coordinates that the mount reports as its position.
Definition align.h:459
Q_SCRIPTABLE Q_NOREPLY void checkFocus(double requiredHFR)
checkFocus Given the minimum required HFR, check focus and calculate HFR.
Definition focus.cpp:4671
Q_SCRIPTABLE Q_NOREPLY void abort()
DBUS interface function.
Definition focus.cpp:1424
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString fullName(const PartType &type)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:78
CaptureState
Capture states.
Definition ekos.h:92
@ CAPTURE_ABORTED
Definition ekos.h:99
@ CAPTURE_COMPLETE
Definition ekos.h:112
@ CAPTURE_CAPTURING
Definition ekos.h:95
@ CAPTURE_IDLE
Definition ekos.h:93
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
GeoCoordinates geo(const QVariant &location)
QVariant location(const QVariant &res)
KGuiItem reset()
KGuiItem stop()
QString label(StandardShortcut id)
QString name(StandardShortcut id)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
void clicked(bool checked)
void setChecked(bool)
void currentTextChanged(const QString &text)
QCoreApplication * instance()
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
void accepted()
void rejected()
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
QIcon fromTheme(const QString &name)
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
void clear()
QPixmap transformed(const QTransform &transform, Qt::TransformationMode mode) const const
void start(OpenMode mode)
bool waitForFinished(int msecs)
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
AlignVCenter
ApplicationState
CaseInsensitive
UniqueConnection
WA_LayoutUsesWidgetRect
void currentChanged(int index)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
QUuid createUuid()
QString toString(StringFormat mode) const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
void show()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.