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

KDE's Doxygen guidelines are available online.