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 {
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
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
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;
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 {
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 {
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.
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 {
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 {
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;
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{
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 if (Options::ekosLeftIcons())
2167 {
2168 QTransform trans;
2169 trans.rotate(90);
2170 QIcon icon = toolsWidget->tabIcon(index);
2171 QPixmap pix = icon.pixmap(QSize(48, 48));
2172 icon = QIcon(pix.transformed(trans));
2173 toolsWidget->setTabIcon(index, icon);
2174 }
2175
2176 connectModules();
2177}
2178
2179void Manager::initFocus()
2180{
2181 if (focusModule() != nullptr)
2182 return;
2183
2184 focusProcess.reset(new Ekos::FocusModule());
2185
2186 emit newModule("Focus");
2187
2188 int index = addModuleTab(EkosModule::Focus, focusModule(), QIcon(":/icons/ekos_focus.png"));
2189
2190 toolsWidget->tabBar()->setTabToolTip(index, i18n("Focus"));
2191
2192 // Focus <---> Manager connections (restricted to the main focuser)
2193 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newStatus, this, &Ekos::Manager::updateFocusStatus);
2194 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newStarPixmap, focusProgressWidget,
2195 &Ekos::FocusProgressWidget::updateFocusStarPixmap);
2196 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newHFR, this, &Ekos::Manager::updateCurrentHFR);
2197 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::focuserTimedout, this, &Ekos::Manager::restartDriver);
2198 connect(focusModule(), &Ekos::FocusModule::newLog, this, [this]()
2199 {
2200 // update the logging in the client
2201 updateLog();
2202
2203 QJsonObject cStatus =
2204 {
2205 {"log", focusModule()->getLogText()}
2206 };
2207
2208 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2209 });
2210 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newFocusAdvisorMessage, this, [this](const QString & message)
2211 {
2212 QJsonObject cStatus =
2213 {
2214 {"focusAdvisorMessage", message}
2215 };
2216
2217 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2218 });
2219 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newFocusAdvisorStage, ekosLiveClient.get()->message(),
2220 [this](int stage)
2221 {
2222 QJsonObject cStatus =
2223 {
2224 {"focusAdvisorStage", stage}
2225 };
2226
2227 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2228 });
2229
2230
2231 // connect HFR plot widget
2232 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::initHFRPlot, [this](QString str, double starUnits, bool minimum,
2233 bool useWeights,
2234 bool showPosition)
2235 {
2236 focusProgressWidget->hfrVPlot->init(str, starUnits, minimum, useWeights, showPosition);
2237 QJsonObject cStatus =
2238 {
2239 {"focusinitHFRPlot", true}
2240 };
2241
2242 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2243 });
2244
2245 // Update title
2246 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::setTitle, [this](const QString & title, bool plot)
2247 {
2248 focusProgressWidget->hfrVPlot->setTitle(title, plot);
2249 QJsonObject cStatus =
2250 {
2251 {"title", title}
2252 };
2253
2254 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2255 });
2256 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::setTitle, focusProgressWidget->hfrVPlot,
2257 &FocusHFRVPlot::setTitle);
2258 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::redrawHFRPlot, focusProgressWidget->hfrVPlot,
2259 &FocusHFRVPlot::redraw);
2260 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newHFRPlotPosition, focusProgressWidget->hfrVPlot,
2261 &FocusHFRVPlot::addPosition);
2262 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::drawPolynomial, focusProgressWidget->hfrVPlot,
2263 &FocusHFRVPlot::drawPolynomial);
2264 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::finalUpdates, focusProgressWidget->hfrVPlot,
2265 &FocusHFRVPlot::finalUpdates);
2266 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::minimumFound, focusProgressWidget->hfrVPlot,
2267 &FocusHFRVPlot::drawMinimum);
2268 // setup signal/slots for Linear 1 Pass focus algo
2269 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::drawCurve, focusProgressWidget->hfrVPlot,
2270 &FocusHFRVPlot::drawCurve);
2271 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::drawCFZ, focusProgressWidget->hfrVPlot, &FocusHFRVPlot::drawCFZ);
2272
2273 if (Options::ekosLeftIcons())
2274 {
2275 QTransform trans;
2276 trans.rotate(90);
2277 QIcon icon = toolsWidget->tabIcon(index);
2278 QPixmap pix = icon.pixmap(QSize(48, 48));
2279 icon = QIcon(pix.transformed(trans));
2280 toolsWidget->setTabIcon(index, icon);
2281 }
2282
2283 focusProgressWidget->init();
2284 focusProgressWidget->setEnabled(true);
2285
2286 for (auto &oneDevice : INDIListener::devices())
2287 {
2288 auto prop1 = oneDevice->getProperty("CCD_TEMPERATURE");
2289 auto prop2 = oneDevice->getProperty("FOCUSER_TEMPERATURE");
2290 auto prop3 = oneDevice->getProperty("WEATHER_PARAMETERS");
2291 if (prop1 || prop2 || prop3)
2292 focusModule()->addTemperatureSource(oneDevice);
2293 }
2294
2295 connectModules();
2296}
2297
2298void Manager::updateCurrentHFR(double newHFR, int position, bool inAutofocus)
2299{
2300 Q_UNUSED(inAutofocus);
2301 focusProgressWidget->updateCurrentHFR(newHFR);
2302
2303 QJsonObject cStatus =
2304 {
2305 {"hfr", newHFR},
2306 {"pos", position}
2307 };
2308
2309 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2310}
2311
2312void Manager::updateSigmas(double ra, double de)
2313{
2314 guideManager->updateSigmas(ra, de);
2315
2316 QJsonObject cStatus = { {"rarms", ra}, {"derms", de} };
2317
2318 ekosLiveClient.get()->message()->updateGuideStatus(cStatus);
2319}
2320
2321void Manager::initMount()
2322{
2323 if (mountModule() != nullptr)
2324 return;
2325
2326 mountProcess.reset(new Ekos::Mount());
2327
2328 // share the meridian flip state with capture if the module is already present
2329 if (captureModule() != nullptr)
2330 captureModule()->setMeridianFlipState(mountModule()->getMeridianFlipState());
2331
2332 emit newModule("Mount");
2333
2334 int index = addModuleTab(EkosModule::Mount, mountModule(), QIcon(":/icons/ekos_mount.png"));
2335
2336 toolsWidget->tabBar()->setTabToolTip(index, i18n("Mount"));
2337 connect(mountModule(), &Ekos::Mount::newLog, this, &Ekos::Manager::updateLog);
2338 connect(mountModule(), &Ekos::Mount::newCoords, this, &Ekos::Manager::updateMountCoords);
2339 connect(mountModule(), &Ekos::Mount::newStatus, this, &Ekos::Manager::updateMountStatus);
2340 connect(mountModule(), &Ekos::Mount::newTargetName, this, [this](const QString & name)
2341 {
2342 setTarget(name);
2343 });
2344 connect(mountModule(), &Ekos::Mount::pierSideChanged, this, [&](ISD::Mount::PierSide side)
2345 {
2346 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"pierSide", side}}));
2347 });
2348 connect(mountModule()->getMeridianFlipState().get(),
2349 &Ekos::MeridianFlipState::newMountMFStatus, [&](MeridianFlipState::MeridianFlipMountState status)
2350 {
2351 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject(
2352 {
2353 {"meridianFlipStatus", status},
2354 }));
2355 });
2356 connect(mountModule()->getMeridianFlipState().get(),
2357 &Ekos::MeridianFlipState::newMeridianFlipMountStatusText, [&](const QString & text)
2358 {
2359 // Throttle this down
2360 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject(
2361 {
2362 {"meridianFlipText", text},
2363 }), mountModule()->getMeridianFlipState()->getMeridianFlipMountState() == MeridianFlipState::MOUNT_FLIP_NONE);
2364 meridianFlipStatusWidget->setStatus(text);
2365 });
2366 connect(mountModule(), &Ekos::Mount::autoParkCountdownUpdated, this, [&](const QString & text)
2367 {
2368 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"autoParkCountdown", text}}), true);
2369 });
2370
2371 connect(mountModule(), &Ekos::Mount::trainChanged, ekosLiveClient.get()->message(),
2372 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
2373
2374 connect(mountModule(), &Ekos::Mount::slewRateChanged, this, [&](int slewRate)
2375 {
2376 QJsonObject status = { { "slewRate", slewRate} };
2377 ekosLiveClient.get()->message()->updateMountStatus(status);
2378 });
2379
2380 if (Options::ekosLeftIcons())
2381 {
2382 QTransform trans;
2383 trans.rotate(90);
2384 QIcon icon = toolsWidget->tabIcon(index);
2385 QPixmap pix = icon.pixmap(QSize(48, 48));
2386 icon = QIcon(pix.transformed(trans));
2387 toolsWidget->setTabIcon(index, icon);
2388 }
2389
2390 mountGroup->setEnabled(true);
2391 capturePreview->shareMountModule(mountModule());
2392
2393 connectModules();
2394}
2395
2396void Manager::initGuide()
2397{
2398 if (guideModule() == nullptr)
2399 {
2400 guideProcess.reset(new Ekos::Guide());
2401
2402 emit newModule("Guide");
2403 }
2404
2405 if (toolsWidget->indexOf(guideModule()) == -1)
2406 {
2407 // if (managedDevices.contains(KSTARS_TELESCOPE) && managedDevices.value(KSTARS_TELESCOPE)->isConnected())
2408 // guideProcess->addMount(managedDevices.value(KSTARS_TELESCOPE));
2409
2410 int index = addModuleTab(EkosModule::Guide, guideModule(), QIcon(":/icons/ekos_guide.png"));
2411 toolsWidget->tabBar()->setTabToolTip(index, i18n("Guide"));
2412 connect(guideModule(), &Ekos::Guide::newLog, this, &Ekos::Manager::updateLog);
2413 connect(guideModule(), &Ekos::Guide::driverTimedout, this, &Ekos::Manager::restartDriver);
2414
2415 guideManager->setEnabled(true);
2416
2417 connect(guideModule(), &Ekos::Guide::newStatus, this, &Ekos::Manager::updateGuideStatus);
2418 connect(guideModule(), &Ekos::Guide::newStarPixmap, guideManager, &Ekos::GuideManager::updateGuideStarPixmap);
2419 connect(guideModule(), &Ekos::Guide::newAxisSigma, this, &Ekos::Manager::updateSigmas);
2420 connect(guideModule(), &Ekos::Guide::newAxisDelta, [&](double ra, double de)
2421 {
2422 QJsonObject status = { { "drift_ra", ra}, {"drift_de", de} };
2423 ekosLiveClient.get()->message()->updateGuideStatus(status);
2424 });
2425 connect(guideModule(), &Ekos::Guide::newLog, ekosLiveClient.get()->message(),
2426 [this]()
2427 {
2428 QJsonObject cStatus =
2429 {
2430 {"log", guideModule()->getLogText()}
2431 };
2432
2433 ekosLiveClient.get()->message()->updateGuideStatus(cStatus);
2434 });
2435
2436 if (Options::ekosLeftIcons())
2437 {
2438 QTransform trans;
2439 trans.rotate(90);
2440 QIcon icon = toolsWidget->tabIcon(index);
2441 QPixmap pix = icon.pixmap(QSize(48, 48));
2442 icon = QIcon(pix.transformed(trans));
2443 toolsWidget->setTabIcon(index, icon);
2444 }
2445 guideManager->init(guideModule());
2446 }
2447
2448 connectModules();
2449}
2450
2451void Manager::initObservatory()
2452{
2453 if (observatoryProcess.get() == nullptr)
2454 {
2455 // Initialize the Observatory Module
2456 observatoryProcess.reset(new Ekos::Observatory());
2457
2458 emit newModule("Observatory");
2459
2460 int index = addModuleTab(EkosModule::Observatory, observatoryProcess.get(), QIcon(":/icons/ekos_observatory.png"));
2461 toolsWidget->tabBar()->setTabToolTip(index, i18n("Observatory"));
2462 connect(observatoryProcess.get(), &Ekos::Observatory::newLog, this, &Ekos::Manager::updateLog);
2463
2464 if (Options::ekosLeftIcons())
2465 {
2466 QTransform trans;
2467 trans.rotate(90);
2468 QIcon icon = toolsWidget->tabIcon(index);
2469 QPixmap pix = icon.pixmap(QSize(48, 48));
2470 icon = QIcon(pix.transformed(trans));
2471 toolsWidget->setTabIcon(index, icon);
2472 }
2473 }
2474}
2475
2476void Manager::addGuider(ISD::Guider * device)
2477{
2478 appendLogText(i18n("Guider port from %1 is ready.", device->getDeviceName()));
2479}
2480
2481void Manager::removeTabs()
2482{
2483 disconnect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange);
2484
2485 for (int i = numPermanentTabs; i < toolsWidget->count(); i++)
2486 toolsWidget->removeTab(i);
2487
2488 alignProcess.reset();
2489 captureProcess.reset();
2490 focusProcess.reset();
2491 guideProcess.reset();
2492 mountProcess.reset();
2493 observatoryProcess.reset();
2494
2495 connect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange, Qt::UniqueConnection);
2496}
2497
2498bool Manager::isRunning(const QString &process)
2499{
2500 QProcess ps;
2501#ifdef Q_OS_MACOS
2502 ps.start("pgrep", QStringList() << process);
2503 ps.waitForFinished();
2504 QString output = ps.readAllStandardOutput();
2505 return output.length() > 0;
2506#else
2507 ps.start("ps", QStringList() << "-o"
2508 << "comm"
2509 << "--no-headers"
2510 << "-C" << process);
2511 ps.waitForFinished();
2512 QString output = ps.readAllStandardOutput();
2513 return output.contains(process);
2514#endif
2515}
2516
2517void Manager::addObjectToScheduler(SkyObject * object)
2518{
2519 if (schedulerModule() != nullptr)
2520 schedulerModule()->addObject(object);
2521}
2522
2523QString Manager::getCurrentJobName()
2524{
2525 return schedulerModule()->getCurrentJobName();
2526}
2527
2528bool Manager::setProfile(const QString &profileName)
2529{
2530 int index = profileCombo->findText(profileName);
2531
2532 if (index < 0)
2533 return false;
2534
2535 profileCombo->setCurrentIndex(index);
2536
2537 return true;
2538}
2539
2540void Manager::editNamedProfile(const QJsonObject &profileInfo)
2541{
2542 ProfileEditor editor(this);
2543 setProfile(profileInfo["name"].toString());
2544 if (getCurrentProfile(m_CurrentProfile))
2545 {
2546 editor.setPi(m_CurrentProfile);
2547 editor.setSettings(profileInfo);
2548 editor.saveProfile();
2549 }
2550}
2551
2552void Manager::addNamedProfile(const QJsonObject &profileInfo)
2553{
2554 ProfileEditor editor(this);
2555
2556 editor.setSettings(profileInfo);
2557 editor.saveProfile();
2558 profiles.clear();
2559 loadProfiles();
2560 profileCombo->setCurrentIndex(profileCombo->count() - 1);
2561 getCurrentProfile(m_CurrentProfile);
2562}
2563
2564void Manager::deleteNamedProfile(const QString &name)
2565{
2566 if (!getCurrentProfile(m_CurrentProfile))
2567 return;
2568
2569 for (auto &pi : profiles)
2570 {
2571 // Do not delete an actively running profile
2572 // Do not delete simulator profile
2573 if (pi->name == "Simulators" || pi->name != name || (pi.get() == m_CurrentProfile && ekosStatus() != Idle))
2574 continue;
2575
2576 KStarsData::Instance()->userdb()->PurgeProfile(pi);
2577 profiles.clear();
2578 loadProfiles();
2579 getCurrentProfile(m_CurrentProfile);
2580 return;
2581 }
2582}
2583
2584QJsonObject Manager::getNamedProfile(const QString &name)
2585{
2586 QJsonObject profileInfo;
2587
2588 // Get current profile
2589 for (auto &pi : profiles)
2590 {
2591 if (name == pi->name)
2592 return pi->toJson();
2593 }
2594
2595 return QJsonObject();
2596}
2597
2598QStringList Manager::getProfiles()
2599{
2600 QStringList profiles;
2601
2602 for (int i = 0; i < profileCombo->count(); i++)
2603 profiles << profileCombo->itemText(i);
2604
2605 return profiles;
2606}
2607
2608void Manager::addProfile()
2609{
2610 ProfileEditor editor(this);
2611
2612 if (editor.exec() == QDialog::Accepted)
2613 {
2614 profiles.clear();
2615 loadProfiles();
2616 profileCombo->setCurrentIndex(profileCombo->count() - 1);
2617 }
2618
2619 getCurrentProfile(m_CurrentProfile);
2620}
2621
2622void Manager::editProfile()
2623{
2624 ProfileEditor editor(this);
2625
2626 if (getCurrentProfile(m_CurrentProfile))
2627 {
2628
2629 editor.setPi(m_CurrentProfile);
2630
2631 if (editor.exec() == QDialog::Accepted)
2632 {
2633 int currentIndex = profileCombo->currentIndex();
2634
2635 profiles.clear();
2636 loadProfiles();
2637 profileCombo->setCurrentIndex(currentIndex);
2638 }
2639
2640 getCurrentProfile(m_CurrentProfile);
2641 }
2642}
2643
2644void Manager::deleteProfile()
2645{
2646 if (!getCurrentProfile(m_CurrentProfile))
2647 return;
2648
2649 if (m_CurrentProfile->name == "Simulators")
2650 return;
2651
2652 auto executeDeleteProfile = [&]()
2653 {
2654 KStarsData::Instance()->userdb()->PurgeProfile(m_CurrentProfile);
2655 profiles.clear();
2656 loadProfiles();
2657 getCurrentProfile(m_CurrentProfile);
2658 };
2659
2660 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, executeDeleteProfile]()
2661 {
2662 //QObject::disconnect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, nullptr);
2663 KSMessageBox::Instance()->disconnect(this);
2664 executeDeleteProfile();
2665 });
2666
2667 KSMessageBox::Instance()->questionYesNo(i18n("Are you sure you want to delete the profile?"),
2668 i18n("Confirm Delete"));
2669
2670}
2671
2672void Manager::wizardProfile()
2673{
2674 ProfileWizard wz;
2675 if (wz.exec() != QDialog::Accepted)
2676 return;
2677
2678 ProfileEditor editor(this);
2679
2680 editor.setProfileName(wz.profileName);
2681 editor.setAuxDrivers(wz.selectedAuxDrivers());
2682 if (wz.useInternalServer == false)
2683 editor.setHostPort(wz.host, wz.port);
2684 editor.setWebManager(wz.useWebManager);
2685 editor.setGuiderType(wz.selectedExternalGuider());
2686 // Disable connection options
2687 editor.setConnectionOptionsEnabled(false);
2688
2689 if (editor.exec() == QDialog::Accepted)
2690 {
2691 profiles.clear();
2692 loadProfiles();
2693 profileCombo->setCurrentIndex(profileCombo->count() - 1);
2694 }
2695
2696 getCurrentProfile(m_CurrentProfile);
2697}
2698
2699bool Manager::getCurrentProfile(QSharedPointer<ProfileInfo> &profile) const
2700{
2701 // Get current profile
2702 for (auto &pi : profiles)
2703 {
2704 if (profileCombo->currentText() == pi->name)
2705 {
2706 profile = pi;
2707 return true;
2708 }
2709 }
2710
2711 return false;
2712}
2713
2714void Manager::updateProfileLocation(const QSharedPointer<ProfileInfo> &profile)
2715{
2716 if (profile->city.isEmpty() == false)
2717 {
2718 bool cityFound = KStars::Instance()->setGeoLocation(profile->city, profile->province, profile->country);
2719 if (cityFound)
2720 appendLogText(i18n("Site location updated to %1.", KStarsData::Instance()->geo()->fullName()));
2721 else
2722 appendLogText(i18n("Failed to update site location to %1. City not found.",
2723 KStarsData::Instance()->geo()->fullName()));
2724 }
2725}
2726
2727void Manager::updateMountStatus(ISD::Mount::Status status)
2728{
2729 static ISD::Mount::Status lastStatus = ISD::Mount::MOUNT_IDLE;
2730
2731 if (status == lastStatus)
2732 return;
2733
2734 lastStatus = status;
2735
2736 mountStatus->setMountState(mountModule()->statusString(), status);
2737 mountStatus->setStyleSheet(QString());
2738
2739 QJsonObject cStatus =
2740 {
2741 {"status", mountModule()->statusString(false)}
2742 };
2743
2744 ekosLiveClient.get()->message()->updateMountStatus(cStatus);
2745}
2746
2747void Manager::updateMountCoords(const SkyPoint position, ISD::Mount::PierSide pierSide, const dms &ha)
2748{
2749 Q_UNUSED(pierSide)
2750 raOUT->setText(position.ra().toHMSString());
2751 decOUT->setText(position.dec().toDMSString());
2752 azOUT->setText(position.az().toDMSString());
2753 altOUT->setText(position.alt().toDMSString());
2754
2755 QJsonObject cStatus =
2756 {
2757 {"ra", dms::fromString(raOUT->text(), false).Degrees()},
2758 {"de", dms::fromString(decOUT->text(), true).Degrees()},
2759 {"ra0", position.ra0().Degrees()},
2760 {"de0", position.dec0().Degrees()},
2761 {"az", dms::fromString(azOUT->text(), true).Degrees()},
2762 {"at", dms::fromString(altOUT->text(), true).Degrees()},
2763 {"ha", ha.Degrees()},
2764 };
2765
2766 ekosLiveClient.get()->message()->updateMountStatus(cStatus, true);
2767}
2768
2769void Manager::updateCaptureStatus(Ekos::CaptureState status, const QString &trainname)
2770{
2771 capturePreview->updateCaptureStatus(status, captureModule()->isActiveJobPreview(), trainname);
2772
2773 switch (status)
2774 {
2775 case Ekos::CAPTURE_IDLE:
2776 /* Fall through */
2778 /* Fall through */
2780 m_CountdownTimer.stop();
2781 break;
2783 m_CountdownTimer.start();
2784 break;
2785 default:
2786 break;
2787 }
2788
2789 QJsonObject cStatus =
2790 {
2791 {"status", QString::fromLatin1(captureStates[status].untranslatedText())},
2792 {"seqt", capturePreview->captureCountsWidget->sequenceRemainingTime->text()},
2793 {"ovt", capturePreview->captureCountsWidget->overallRemainingTime->text()},
2794 {"train", trainname}
2795 };
2796
2797 ekosLiveClient.get()->message()->updateCaptureStatus(cStatus);
2798}
2799
2800void Manager::updateCaptureProgress(Ekos::SequenceJob * job, const QSharedPointer<FITSData> &data,
2801 const QString &trainname)
2802{
2803 capturePreview->updateJobProgress(job, data, trainname);
2804
2806 {
2807 {"seqv", job->getCompleted()},
2808 {"seqr", job->getCoreProperty(SequenceJob::SJ_Count).toInt()},
2809 {"seql", capturePreview->captureCountsWidget->sequenceRemainingTime->text()}
2810 };
2811
2812 ekosLiveClient.get()->message()->updateCaptureStatus(status);
2813
2814 if (data && job->getStatus() == JOB_BUSY)
2815 {
2816 // Normally FITS Viewer would trigger an upload
2817 // If off, then rely on summary view or raw data
2818 if (Options::useFITSViewer() == false)
2819 ekosLiveClient.get()->media()->sendData(data, data->objectName());
2820
2821 if (job->jobType() != SequenceJob::JOBTYPE_PREVIEW)
2822 ekosLiveClient.get()->cloud()->sendData(data, data->objectName());
2823 }
2824}
2825
2826void Manager::updateExposureProgress(Ekos::SequenceJob * job, const QString &trainname)
2827{
2829 {
2830 {"expv", job->getExposeLeft()},
2831 {"expr", job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble()},
2832 {"train", trainname}
2833 };
2834
2835 ekosLiveClient.get()->message()->updateCaptureStatus(status);
2836}
2837
2838void Manager::updateCaptureCountDown()
2839{
2840 capturePreview->updateCaptureCountDown(-1);
2841
2843 {
2844 {"seqt", capturePreview->captureCountsWidget->sequenceRemainingTime->text()},
2845 {"ovt", capturePreview->captureCountsWidget->overallRemainingTime->text()},
2846 {"ovp", capturePreview->captureCountsWidget->gr_overallProgressBar->value()},
2847 {"ovl", capturePreview->captureCountsWidget->gr_overallLabel->text()}
2848 };
2849
2850 ekosLiveClient.get()->message()->updateCaptureStatus(status);
2851}
2852
2853
2854void Manager::updateFocusStatus(Ekos::FocusState status)
2855{
2856 focusProgressWidget->updateFocusStatus(status);
2857
2858 QJsonObject cStatus =
2859 {
2860 {"status", getFocusStatusString(status, false)}
2861 };
2862
2863 ekosLiveClient.get()->message()->updateFocusStatus(cStatus);
2864}
2865
2866void Manager::updateGuideStatus(Ekos::GuideState status)
2867{
2868 guideManager->updateGuideStatus(status);
2869 QJsonObject cStatus =
2870 {
2871 {"status", getGuideStatusString(status, false)}
2872 };
2873
2874 ekosLiveClient.get()->message()->updateGuideStatus(cStatus);
2875}
2876
2877void Manager::setTarget(const QString &name)
2878{
2879 capturePreview->targetLabel->setVisible(!name.isEmpty());
2880 capturePreview->mountTarget->setVisible(!name.isEmpty());
2881 capturePreview->mountTarget->setText(name);
2882 ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"target", name}}));
2883 // forward it to the mount tab
2884 if (mountModule())
2885 mountModule()->setTargetName(name);
2886}
2887
2888void Manager::showEkosOptions()
2889{
2890 QWidget * currentWidget = toolsWidget->currentWidget();
2891
2892 if (alignModule() && alignModule() == currentWidget)
2893 {
2894 KConfigDialog * alignSettings = KConfigDialog::exists("alignsettings");
2895 if (alignSettings)
2896 {
2897 alignSettings->setEnabled(true);
2898 alignSettings->show();
2899 }
2900 return;
2901 }
2902
2903 if (guideModule() && guideModule() == currentWidget)
2904 {
2905 KConfigDialog::showDialog("guidesettings");
2906 return;
2907 }
2908
2909 if (focusModule() && focusModule() == currentWidget)
2910 {
2911 KConfigDialog * focusSettings = KConfigDialog::exists("focussettings");
2912 if (focusSettings)
2913 {
2914 focusSettings->show();
2915 focusSettings->raise();
2916 }
2917 return;
2918 }
2919
2920 if (schedulerModule() == currentWidget)
2921 {
2922 KConfigDialog * cDialog = KConfigDialog::exists("schedulersettings");
2923 if(cDialog)
2924 {
2925 cDialog->show();
2926 cDialog->raise(); // for MacOS
2927 cDialog->activateWindow(); // for Windows
2928 }
2929 return;
2930 }
2931
2932 if(captureModule() == currentWidget)
2933 {
2934 KConfigDialog * cDialog = KConfigDialog::exists("capturesettings");
2935 if(cDialog)
2936 {
2937 cDialog->show();
2938 cDialog->raise(); // for MacOS
2939 cDialog->activateWindow(); // for Windows
2940 }
2941 return;
2942 }
2943
2944 const bool isAnalyze = (analyzeProcess.get() && analyzeProcess.get() == currentWidget);
2945 if (isAnalyze)
2946 {
2947 if (opsEkos)
2948 {
2949 int index = 0;
2950 if (isAnalyze) index = 1;
2951 opsEkos->setCurrentIndex(index);
2952 }
2953 KConfigDialog * cDialog = KConfigDialog::exists("settings");
2954 if (cDialog)
2955 {
2956 cDialog->setCurrentPage(ekosOptionsWidget);
2957 cDialog->show();
2958 cDialog->raise(); // for MacOS
2959 cDialog->activateWindow(); // for Windows
2960 }
2961 return;
2962 }
2963
2964 if (ekosOptionsWidget == nullptr)
2965 {
2966 optionsB->click();
2967 }
2968 else if (KConfigDialog::showDialog("settings"))
2969 {
2970 KConfigDialog * cDialog = KConfigDialog::exists("settings");
2971 if (cDialog)
2972 {
2973 cDialog->setCurrentPage(ekosOptionsWidget);
2974 cDialog->show();
2975 cDialog->raise(); // for MacOS
2976 cDialog->activateWindow(); // for Windows
2977 }
2978 }
2979}
2980
2981void Manager::updateDebugInterfaces()
2982{
2984
2985 for (auto &device : INDIListener::devices())
2986 {
2987 auto debugProp = device->getProperty("DEBUG");
2988 if (!debugProp)
2989 continue;
2990
2991 auto debugSP = debugProp.getSwitch();
2992
2993 // Check if the debug interface matches the driver device class
2994 if ( ( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) &&
2995 debugSP->sp[0].s != ISS_ON)
2996 {
2997 debugSP->at(0)->setState(ISS_ON);
2998 debugSP->at(1)->setState(ISS_OFF);
2999 device->sendNewProperty(debugSP);
3000 appendLogText(i18n("Enabling debug logging for %1...", device->getDeviceName()));
3001 }
3002 else if ( !( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) &&
3003 debugSP->sp[0].s != ISS_OFF)
3004 {
3005 debugSP->at(0)->setState(ISS_OFF);
3006 debugSP->at(1)->setState(ISS_ON);
3007 device->sendNewProperty(debugSP);
3008 appendLogText(i18n("Disabling debug logging for %1...", device->getDeviceName()));
3009 }
3010
3011 if (opsLogs->isINDISettingsChanged())
3012 device->setConfig(SAVE_CONFIG);
3013 }
3014}
3015
3016void Manager::watchDebugProperty(INDI::Property prop)
3017{
3018 if (prop.isNameMatch("DEBUG"))
3019 {
3020 auto svp = prop.getSwitch();
3021
3022 ISD::GenericDevice * deviceInterface = qobject_cast<ISD::GenericDevice *>(sender());
3023
3024 // We don't process pure general interfaces
3025 if (deviceInterface->getDriverInterface() == INDI::BaseDevice::GENERAL_INTERFACE)
3026 return;
3027
3028 // If debug was turned off, but our logging policy requires it then turn it back on.
3029 // We turn on debug logging if AT LEAST one driver interface is selected by the logging settings
3030 if (svp->s == IPS_OK && svp->sp[0].s == ISS_OFF &&
3031 (opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface()))
3032 {
3033 svp->sp[0].s = ISS_ON;
3034 svp->sp[1].s = ISS_OFF;
3035 deviceInterface->sendNewProperty(svp);
3036 appendLogText(i18n("Re-enabling debug logging for %1...", deviceInterface->getDeviceName()));
3037 }
3038 // To turn off debug logging, NONE of the driver interfaces should be enabled in logging settings.
3039 // For example, if we have CCD+FilterWheel device and CCD + Filter Wheel logging was turned on in
3040 // the log settings, then if the user turns off only CCD logging, the debug logging is NOT
3041 // turned off until he turns off Filter Wheel logging as well.
3042 else if (svp->s == IPS_OK && svp->sp[0].s == ISS_ON
3043 && !(opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface()))
3044 {
3045 svp->sp[0].s = ISS_OFF;
3046 svp->sp[1].s = ISS_ON;
3047 deviceInterface->sendNewProperty(svp);
3048 appendLogText(i18n("Re-disabling debug logging for %1...", deviceInterface->getDeviceName()));
3049 }
3050 }
3051}
3052
3053void Manager::announceEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event)
3054{
3055 ekosLiveClient.get()->message()->sendEvent(message, source, event);
3056}
3057
3058void Manager::connectModules()
3059{
3060 // Dark Library
3061 connect(DarkLibrary::Instance(), &DarkLibrary::newImage, ekosLiveClient.get()->media(),
3062 &EkosLive::Media::sendDarkLibraryData, Qt::UniqueConnection);
3063 connect(DarkLibrary::Instance(), &DarkLibrary::trainChanged, ekosLiveClient.get()->message(),
3064 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3065 connect(DarkLibrary::Instance(), &DarkLibrary::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3067 connect(DarkLibrary::Instance(), &DarkLibrary::settingsUpdated, ekosLiveClient.get()->message(),
3068 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection);
3069
3070 // Guide <---> Capture connections
3071 if (captureProcess && guideProcess)
3072 {
3073 // captureProcess.get()->disconnect(guideProcess.get());
3074 // guideProcess.get()->disconnect(captureProcess.get());
3075
3076 // Guide Limits
3077 connect(guideModule(), &Ekos::Guide::newStatus, captureModule(), &Ekos::Capture::setGuideStatus,
3079 connect(guideModule(), &Ekos::Guide::newAxisDelta, captureModule(), &Ekos::Capture::setGuideDeviation,
3081
3082 // Dithering
3083 connect(captureModule(), &Ekos::Capture::dither, guideModule(), &Ekos::Guide::dither, Qt::UniqueConnection);
3084 connect(captureModule(), &Ekos::Capture::resetNonGuidedDither, guideModule(), &Ekos::Guide::resetNonGuidedDither,
3086
3087 // Guide Head
3088 connect(captureModule(), &Ekos::Capture::suspendGuiding, guideModule(), &Ekos::Guide::suspend,
3090 connect(captureModule(), &Ekos::Capture::resumeGuiding, guideModule(), &Ekos::Guide::resume,
3092 connect(guideModule(), &Ekos::Guide::guideChipUpdated, captureModule(), &Ekos::Capture::setGuideChip,
3094
3095 // Meridian Flip
3096 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, guideModule(), &Ekos::Guide::abort,
3098 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, guideModule(),
3099 &Ekos::Guide::guideAfterMeridianFlip, Qt::UniqueConnection);
3100 }
3101
3102 // Guide <---> Mount connections
3103 if (guideProcess && mountProcess)
3104 {
3105 // Parking
3106 connect(mountModule(), &Ekos::Mount::newStatus, guideModule(), &Ekos::Guide::setMountStatus,
3108 connect(mountModule(), &Ekos::Mount::newCoords, guideModule(), &Ekos::Guide::setMountCoords,
3110
3111 }
3112
3113 // Focus <---> Guide connections
3114 if (guideProcess && focusProcess)
3115 {
3116 // Suspend
3117 connect(focusModule(), &Ekos::FocusModule::suspendGuiding, guideModule(), &Ekos::Guide::suspend,
3119 connect(focusModule(), &Ekos::FocusModule::resumeGuiding, guideModule(), &Ekos::Guide::resume,
3121 }
3122
3123 // Capture <---> Focus connections
3124 if (captureProcess && focusProcess)
3125 {
3126 // Check focus HFR value and if above threshold parameter, run autoFocus
3127 connect(captureModule(), &Ekos::Capture::checkFocus, focusModule(), &Ekos::FocusModule::checkFocus,
3129
3130 // Run autoFocus
3131 connect(captureProcess.get(), &Ekos::Capture::runAutoFocus, focusModule(), &Ekos::FocusModule::runAutoFocus,
3133
3134 // Reset Frame
3135 connect(captureModule(), &Ekos::Capture::resetFocusFrame, focusModule(), &Ekos::FocusModule::resetFrame,
3137
3138 // Abort Focus
3139 connect(captureModule(), &Ekos::Capture::abortFocus, focusModule(), &Ekos::FocusModule::abort, Qt::UniqueConnection);
3140
3141 // New Focus Status
3142 connect(focusModule(), &Ekos::FocusModule::newStatus, captureModule(), &Ekos::Capture::setFocusStatus,
3144
3145 // Perform adaptive focus
3146 connect(captureModule(), &Ekos::Capture::adaptiveFocus, focusModule(), &Ekos::FocusModule::adaptiveFocus,
3148
3149 // New Adaptive Focus Status
3150 connect(focusModule(), &Ekos::FocusModule::focusAdaptiveComplete, captureModule(), &Ekos::Capture::focusAdaptiveComplete,
3152
3153 // New Focus HFR
3154 connect(focusModule(), &Ekos::FocusModule::newHFR, captureModule(), &Ekos::Capture::setHFR, Qt::UniqueConnection);
3155
3156 // New Focus temperature delta
3157 connect(focusModule(), &Ekos::FocusModule::newFocusTemperatureDelta, captureModule(),
3159
3160 // User requested AF
3161 connect(focusModule(), &Ekos::FocusModule::inSequenceAF, captureModule(),
3163
3164 // Meridian Flip
3165 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, focusModule(), &Ekos::FocusModule::meridianFlipStarted,
3167 }
3168
3169 // Capture <---> Align connections
3170 if (captureProcess && alignProcess)
3171 {
3172 // Alignment flag
3173 connect(alignModule(), &Ekos::Align::newStatus, captureModule(), &Ekos::Capture::setAlignStatus,
3175 // Solver data
3176 connect(alignModule(), &Ekos::Align::newSolverResults, captureModule(), &Ekos::Capture::setAlignResults,
3178 // Capture Status
3179 connect(captureModule(), &Ekos::Capture::newStatus, alignModule(), &Ekos::Align::setCaptureStatus,
3181 }
3182
3183 // Capture <---> Mount connections
3184 if (captureProcess && mountProcess)
3185 {
3186 // Register both modules since both are now created and ready
3187 // In case one module misses the DBus signal, then it will be correctly initialized.
3188 captureModule()->registerNewModule("Mount");
3189 mountModule()->registerNewModule("Capture");
3190
3191 // Meridian Flip states
3192 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, mountModule(), &Ekos::Mount::suspendAltLimits,
3194 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, mountModule(), &Ekos::Mount::resumeAltLimits,
3196
3197 // Mount Status
3198 connect(mountModule(), &Ekos::Mount::newStatus, captureModule(), &Ekos::Capture::setMountStatus,
3200 }
3201
3202 // Optical Train Manager ---> EkosLive connections
3203 if (ekosLiveClient)
3204 {
3205 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated, ekosLiveClient->message(),
3206 &EkosLive::Message::sendTrains, Qt::UniqueConnection);
3207 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::configurationRequested, ekosLiveClient->message(),
3208 &EkosLive::Message::requestOpticalTrains, Qt::UniqueConnection);
3209 }
3210
3211 // Capture <---> EkosLive connections
3212 if (captureProcess && ekosLiveClient)
3213 {
3214 //captureProcess.get()->disconnect(ekosLiveClient.get()->message());
3215
3216 connect(captureModule(), &Ekos::Capture::dslrInfoRequested, ekosLiveClient.get()->message(),
3217 &EkosLive::Message::requestDSLRInfo, Qt::UniqueConnection);
3218 connect(captureModule(), &Ekos::Capture::sequenceChanged, ekosLiveClient.get()->message(),
3219 &EkosLive::Message::sendCaptureSequence, Qt::UniqueConnection);
3220 connect(captureModule(), &Ekos::Capture::settingsUpdated, ekosLiveClient.get()->message(),
3221 &EkosLive::Message::sendCaptureSettings, Qt::UniqueConnection);
3222 connect(captureModule(), &Ekos::Capture::newLocalPreview, ekosLiveClient.get()->message(),
3223 &EkosLive::Message::sendPreviewLabel, Qt::UniqueConnection);
3224 connect(captureModule(), &Ekos::Capture::trainChanged, ekosLiveClient.get()->message(),
3225 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3226 }
3227
3228 // Focus <---> Align connections
3229 if (focusProcess && alignProcess)
3230 {
3231 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newStatus, alignModule(), &Ekos::Align::setFocusStatus,
3233 }
3234
3235 // Focus <---> Mount connections
3236 if (focusProcess && mountProcess)
3237 {
3238 connect(mountModule(), &Ekos::Mount::newStatus, focusModule(), &Ekos::FocusModule::setMountStatus, Qt::UniqueConnection);
3239 connect(mountModule(), &Ekos::Mount::newCoords, focusModule(), &Ekos::FocusModule::setMountCoords, Qt::UniqueConnection);
3240 }
3241
3242 // Mount <---> Align connections
3243 if (mountProcess && alignProcess)
3244 {
3245 connect(mountModule(), &Ekos::Mount::newStatus, alignModule(), &Ekos::Align::setMountStatus,
3247 connect(mountModule(), &Ekos::Mount::newTarget, alignModule(), &Ekos::Align::setTarget,
3251 connect(alignModule(), &Ekos::Align::newPAAStage, mountModule(), &Ekos::Mount::paaStageChanged,
3253 }
3254
3255 // Mount <---> Guide connections
3256 if (mountProcess && guideProcess)
3257 {
3258 connect(mountModule(), &Ekos::Mount::pierSideChanged, guideModule(), &Ekos::Guide::setPierSide,
3260 }
3261
3262 // Align <--> EkosLive connections
3263 if (alignProcess && ekosLiveClient)
3264 {
3265 // alignProcess.get()->disconnect(ekosLiveClient.get()->message());
3266 // alignProcess.get()->disconnect(ekosLiveClient.get()->media());
3267
3268 connect(alignModule(), &Ekos::Align::newStatus, ekosLiveClient.get()->message(), &EkosLive::Message::setAlignStatus,
3270 connect(alignModule(), &Ekos::Align::newSolution, ekosLiveClient.get()->message(),
3271 &EkosLive::Message::setAlignSolution, Qt::UniqueConnection);
3272 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHStage,
3273 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHStage,
3275 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHMessage,
3276 ekosLiveClient.get()->message(),
3277 &EkosLive::Message::setPAHMessage, Qt::UniqueConnection);
3278 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::PAHEnabled,
3279 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHEnabled,
3281 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::polarResultUpdated,
3282 ekosLiveClient.get()->message(),
3283 &EkosLive::Message::setPolarResults, Qt::UniqueConnection);
3284 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::updatedErrorsChanged,
3285 ekosLiveClient.get()->message(),
3286 &EkosLive::Message::setUpdatedErrors, Qt::UniqueConnection);
3287 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newCorrectionVector,
3288 ekosLiveClient.get()->media(),
3289 &EkosLive::Media::setCorrectionVector, Qt::UniqueConnection);
3290
3291 connect(alignModule(), &Ekos::Align::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3293 connect(alignModule(), &Ekos::Align::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendUpdatedFrame,
3295
3296 connect(alignModule(), &Ekos::Align::settingsUpdated, ekosLiveClient.get()->message(),
3297 &EkosLive::Message::sendAlignSettings, Qt::UniqueConnection);
3298
3299 connect(alignModule(), &Ekos::Align::trainChanged, ekosLiveClient.get()->message(),
3300 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3301
3302 connect(alignModule(), &Ekos::Align::manualRotatorChanged, ekosLiveClient.get()->message(),
3303 &EkosLive::Message::sendManualRotatorStatus, Qt::UniqueConnection);
3304 }
3305
3306 // Focus <--> EkosLive Connections
3307 if (focusProcess && ekosLiveClient)
3308 {
3309 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::settingsUpdated, ekosLiveClient.get()->message(),
3310 &EkosLive::Message::sendFocusSettings, Qt::UniqueConnection);
3311
3312 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newImage, ekosLiveClient.get()->media(),
3313 &EkosLive::Media::sendModuleFrame,
3315
3316 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::trainChanged, ekosLiveClient.get()->message(),
3317 &EkosLive::Message::sendTrainProfiles,
3319
3320 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusAborted,
3321 ekosLiveClient.get()->message(), &EkosLive::Message::autofocusAborted, Qt::UniqueConnection);
3322 }
3323
3324 // Guide <--> EkosLive Connections
3325 if (guideProcess && ekosLiveClient)
3326 {
3327 connect(guideModule(), &Ekos::Guide::settingsUpdated, ekosLiveClient.get()->message(),
3328 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection);
3329
3330 connect(guideModule(), &Ekos::Guide::trainChanged, ekosLiveClient.get()->message(),
3331 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3332
3333 connect(guideModule(), &Ekos::Guide::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3335 }
3336
3337 // Analyze connections.
3338 if (analyzeProcess)
3339 {
3340 // Scheduler <---> Analyze
3341 connect(schedulerModule(), &Ekos::Scheduler::jobStarted,
3342 analyzeProcess.get(), &Ekos::Analyze::schedulerJobStarted, Qt::UniqueConnection);
3343 connect(schedulerModule(), &Ekos::Scheduler::jobEnded,
3344 analyzeProcess.get(), &Ekos::Analyze::schedulerJobEnded, Qt::UniqueConnection);
3345 connect(schedulerModule(), &Ekos::Scheduler::targetDistance,
3346 analyzeProcess.get(), &Ekos::Analyze::newTargetDistance, Qt::UniqueConnection);
3347
3348 // Capture <---> Analyze
3349 if (captureProcess)
3350 {
3351 connect(captureModule(), &Ekos::Capture::captureComplete,
3352 analyzeProcess.get(), &Ekos::Analyze::captureComplete, Qt::UniqueConnection);
3353 connect(captureModule(), &Ekos::Capture::captureStarting,
3354 analyzeProcess.get(), &Ekos::Analyze::captureStarting, Qt::UniqueConnection);
3355 connect(captureModule(), &Ekos::Capture::captureAborted,
3356 analyzeProcess.get(), &Ekos::Analyze::captureAborted, Qt::UniqueConnection);
3357#if 0
3358 // Meridian Flip
3359 connect(captureModule(), &Ekos::Capture::meridianFlipStarted,
3360 analyzeProcess.get(), &Ekos::Analyze::meridianFlipStarted, Qt::UniqueConnection);
3361 connect(captureModule(), &Ekos::Capture::meridianFlipCompleted,
3362 analyzeProcess.get(), &Ekos::Analyze::meridianFlipComplete, Qt::UniqueConnection);
3363#endif
3364 }
3365
3366 // Guide <---> Analyze
3367 if (guideProcess)
3368 {
3369 connect(guideModule(), &Ekos::Guide::newStatus,
3370 analyzeProcess.get(), &Ekos::Analyze::guideState, Qt::UniqueConnection);
3371
3372 connect(guideModule(), &Ekos::Guide::guideStats,
3373 analyzeProcess.get(), &Ekos::Analyze::guideStats, Qt::UniqueConnection);
3374 }
3375 }
3376
3377
3378 // Focus <---> Analyze connections
3379 if (focusProcess && analyzeProcess)
3380 {
3381 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusComplete,
3382 analyzeProcess.get(), &Ekos::Analyze::autofocusComplete, Qt::UniqueConnection);
3383 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::adaptiveFocusComplete,
3384 analyzeProcess.get(), &Ekos::Analyze::adaptiveFocusComplete, Qt::UniqueConnection);
3385 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusStarting,
3386 analyzeProcess.get(), &Ekos::Analyze::autofocusStarting, Qt::UniqueConnection);
3387 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusAborted,
3388 analyzeProcess.get(), &Ekos::Analyze::autofocusAborted, Qt::UniqueConnection);
3389 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newFocusTemperatureDelta,
3390 analyzeProcess.get(), &Ekos::Analyze::newTemperature, Qt::UniqueConnection);
3391 }
3392
3393 // Align <---> Analyze connections
3394 if (alignProcess && analyzeProcess)
3395 {
3396 connect(alignModule(), &Ekos::Align::newStatus,
3397 analyzeProcess.get(), &Ekos::Analyze::alignState, Qt::UniqueConnection);
3398
3399 }
3400
3401 // Mount <---> Analyze connections
3402 if (mountProcess && analyzeProcess)
3403 {
3404 connect(mountModule(), &Ekos::Mount::newStatus,
3405 analyzeProcess.get(), &Ekos::Analyze::mountState, Qt::UniqueConnection);
3406 connect(mountModule(), &Ekos::Mount::newCoords,
3407 analyzeProcess.get(), &Ekos::Analyze::mountCoords, Qt::UniqueConnection);
3408 connect(mountModule()->getMeridianFlipState().get(), &Ekos::MeridianFlipState::newMountMFStatus,
3409 analyzeProcess.get(), &Ekos::Analyze::mountFlipStatus, Qt::UniqueConnection);
3410 }
3411}
3412
3413void Manager::setEkosLiveConnected(bool enabled)
3414{
3415 ekosLiveClient.get()->setConnected(enabled);
3416}
3417
3418void Manager::setEkosLiveConfig(bool rememberCredentials, bool autoConnect)
3419{
3420 ekosLiveClient.get()->setConfig(rememberCredentials, autoConnect);
3421}
3422
3423void Manager::setEkosLiveUser(const QString &username, const QString &password)
3424{
3425 ekosLiveClient.get()->setUser(username, password);
3426}
3427
3428bool Manager::ekosLiveStatus()
3429{
3430 return ekosLiveClient.get()->isConnected();
3431}
3432
3433bool Manager::checkUniqueBinaryDriver(const QSharedPointer<DriverInfo> &primaryDriver,
3434 const QSharedPointer<DriverInfo> &secondaryDriver)
3435{
3436 if (!primaryDriver || !secondaryDriver)
3437 return false;
3438
3439 return (primaryDriver->getExecutable() == secondaryDriver->getExecutable() &&
3440 primaryDriver->getAuxInfo().value("mdpd", false).toBool() == true);
3441}
3442
3443void Manager::restartDriver(const QString &deviceName)
3444{
3445 qCInfo(KSTARS_EKOS) << "Restarting driver" << deviceName;
3446 if (m_LocalMode)
3447 {
3448 for (auto &oneDevice : INDIListener::devices())
3449 {
3450 if (oneDevice->getDeviceName() == deviceName)
3451 {
3452 DriverManager::Instance()->restartDriver(oneDevice->getDriverInfo());
3453 break;
3454 }
3455 }
3456 }
3457 else
3458 INDI::WebManager::restartDriver(m_CurrentProfile, deviceName);
3459}
3460
3461void Manager::setEkosLoggingEnabled(const QString &name, bool enabled)
3462{
3463 // LOGGING, FILE, DEFAULT are exclusive, so one of them must be SET to TRUE
3464 if (name == "LOGGING")
3465 {
3466 Options::setDisableLogging(!enabled);
3467 if (!enabled)
3469 }
3470 else if (name == "FILE")
3471 {
3472 Options::setLogToFile(enabled);
3473 if (enabled)
3475 }
3476 else if (name == "DEFAULT")
3477 {
3478 Options::setLogToDefault(enabled);
3479 if (enabled)
3481 }
3482 // VERBOSE should be set to TRUE if INDI or Ekos logging is selected.
3483 else if (name == "VERBOSE")
3484 {
3485 Options::setVerboseLogging(enabled);
3487 }
3488 // Toggle INDI Logging
3489 else if (name == "INDI")
3490 {
3491 Options::setINDILogging(enabled);
3493 }
3494 else if (name == "FITS")
3495 {
3496 Options::setFITSLogging(enabled);
3498 }
3499 else if (name == "CAPTURE")
3500 {
3501 Options::setCaptureLogging(enabled);
3502 Options::setINDICCDLogging(enabled);
3503 Options::setINDIFilterWheelLogging(enabled);
3505 }
3506 else if (name == "FOCUS")
3507 {
3508 Options::setFocusLogging(enabled);
3509 Options::setINDIFocuserLogging(enabled);
3511 }
3512 else if (name == "GUIDE")
3513 {
3514 Options::setGuideLogging(enabled);
3515 Options::setINDICCDLogging(enabled);
3517 }
3518 else if (name == "ALIGNMENT")
3519 {
3520 Options::setAlignmentLogging(enabled);
3522 }
3523 else if (name == "MOUNT")
3524 {
3525 Options::setMountLogging(enabled);
3526 Options::setINDIMountLogging(enabled);
3528 }
3529 else if (name == "SCHEDULER")
3530 {
3531 Options::setSchedulerLogging(enabled);
3533 }
3534 else if (name == "OBSERVATORY")
3535 {
3536 Options::setObservatoryLogging(enabled);
3538 }
3539}
3540
3541void Manager::acceptPortSelection()
3542{
3543 if (m_PortSelector)
3544 m_PortSelector->accept();
3545}
3546
3547void Manager::setPortSelectionComplete()
3548{
3549 if (m_CurrentProfile->portSelector)
3550 {
3551 // Turn off port selector
3552 m_CurrentProfile->portSelector = false;
3553 KStarsData::Instance()->userdb()->SaveProfile(m_CurrentProfile);
3554 }
3555
3556 if (m_CurrentProfile->autoConnect)
3557 connectDevices();
3558}
3559
3560void Manager::activateModule(const QString &name, bool popup)
3561{
3562 auto child = toolsWidget->findChild<QWidget *>(name);
3563 if (child)
3564 {
3565 toolsWidget->setCurrentWidget(child);
3566 if (popup)
3567 {
3568 raise();
3569 activateWindow();
3570 showNormal();
3571 }
3572 }
3573}
3574
3575void Manager::createModules(const QSharedPointer<ISD::GenericDevice> &device)
3576{
3577 if (device->isConnected())
3578 {
3579 if (device->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE)
3580 {
3581 initCapture();
3582 initFocus();
3583 initAlign();
3584 initGuide();
3585 }
3586 if (device->getDriverInterface() & INDI::BaseDevice::FILTER_INTERFACE)
3587 {
3588 initCapture();
3589 initFocus();
3590 initAlign();
3591 }
3592 if (device->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE)
3593 initFocus();
3594 if (device->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)
3595 {
3596 initCapture();
3597 initAlign();
3598 initGuide();
3599 initMount();
3600 }
3601 if (device->getDriverInterface() & INDI::BaseDevice::ROTATOR_INTERFACE)
3602 {
3603 initCapture();
3604 initAlign();
3605 }
3606 if (device->getDriverInterface() & INDI::BaseDevice::DOME_INTERFACE)
3607 {
3608 initCapture();
3609 initAlign();
3610 initObservatory();
3611 }
3612 if (device->getDriverInterface() & INDI::BaseDevice::WEATHER_INTERFACE)
3613 {
3614 initFocus();
3615 initObservatory();
3616 }
3617 if (device->getDriverInterface() & INDI::BaseDevice::DUSTCAP_INTERFACE)
3618 {
3619 initCapture();
3620 }
3621 if (device->getDriverInterface() & INDI::BaseDevice::LIGHTBOX_INTERFACE)
3622 {
3623 initCapture();
3624 }
3625 if (device->getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)
3626 {
3627 initMount();
3628 }
3629 }
3630}
3631
3632void Manager::setDeviceReady()
3633{
3634 // Check if ALL our devices are ready.
3635 // Ready indicates that all properties have been defined.
3636 if (isINDIReady() == false)
3637 {
3638 auto device = static_cast<ISD::GenericDevice*>(sender());
3639 if (device)
3640 {
3641
3642 if (device->isConnected() == false && m_CurrentProfile->autoConnect)
3643 {
3644 // Do we have port selector checked?
3645 if (m_CurrentProfile->portSelector)
3646 {
3647 // If port selector was not initialized, kick off the timer
3648 // so we can check if all devices should be connected.
3649 // Otherwise, if port selector is started, then let user
3650 // select ports first and then manually connect time.
3651 if (!m_PortSelector)
3652 m_PortSelectorTimer.start();
3653 }
3654 else
3655 {
3656 qCInfo(KSTARS_EKOS) << "Connecting to" << device->getDeviceName();
3657 device->Connect();
3658 }
3659 }
3660 else
3661 qCInfo(KSTARS_EKOS) << device->getDeviceName() << "is connected and ready.";
3662 }
3663
3664 if (m_ekosStatus != Ekos::Success)
3665 return;
3666 }
3667
3668 // If port selector is active, then do not show optical train dialog unless it is dismissed first.
3669 if (m_DriverDevicesCount <= 0 && (m_CurrentProfile->portSelector == false || !m_PortSelector))
3670 {
3671 for (auto &device : INDIListener::devices())
3672 syncGenericDevice(device);
3673 OpticalTrainManager::Instance()->setProfile(m_CurrentProfile);
3674 }
3675}
3676
3677void Manager::createFilterManager(ISD::FilterWheel *device)
3678{
3679 auto name = device->getDeviceName();
3680 if (m_FilterManagers.contains(name) == false)
3681 {
3682 QSharedPointer<FilterManager> newFM(new FilterManager(this));
3683 newFM->setFilterWheel(device);
3684 m_FilterManagers[name] = newFM;
3685 }
3686 else
3687 m_FilterManagers[name]->setFilterWheel(device);
3688
3689}
3690
3691bool Manager::getFilterManager(const QString &name, QSharedPointer<FilterManager> &fm)
3692{
3693 if (m_FilterManagers.contains(name))
3694 {
3695 fm = m_FilterManagers[name];
3696 return true;
3697 }
3698 return false;
3699}
3700
3701bool Manager::getFilterManager(QSharedPointer<FilterManager> &fm)
3702{
3703 if (m_FilterManagers.size() > 0)
3704 {
3705 fm = m_FilterManagers.values()[0];
3706 return true;
3707 }
3708 return false;
3709}
3710
3711void Manager::createRotatorController(ISD::Rotator *device)
3712{
3713 auto Name = device->getDeviceName();
3714 if (m_RotatorControllers.contains(Name) == false)
3715 {
3716 QSharedPointer<RotatorSettings> newRC(new RotatorSettings(this));
3717 // Properties are fetched in RotatorSettings::initRotator!
3718 m_RotatorControllers[Name] = newRC;
3719 }
3720}
3721
3722bool Manager::getRotatorController(const QString &Name, QSharedPointer<RotatorSettings> &rs)
3723{
3724 if (m_RotatorControllers.contains(Name))
3725 {
3726 rs = m_RotatorControllers[Name];
3727 return true;
3728 }
3729 return false;
3730}
3731
3732bool Manager::existRotatorController()
3733{
3734 return (!m_RotatorControllers.empty());
3735}
3736
3737void Manager::setFITSfromFile(bool previewFromFile)
3738{
3739 if (previewFromFile && !FITSfromFile)
3740 {
3741 // Prevent preview from Capture module
3742 QObject::disconnect(captureModule(), &Ekos::Capture::newImage, this, &Ekos::Manager::updateCaptureProgress);
3743 FITSfromFile = previewFromFile;
3744 appendLogText(i18n("Preview source set to external"));
3745 }
3746 else if (!previewFromFile && FITSfromFile)
3747 {
3748 // Reset preview from Capture module
3749 QObject::connect(captureModule(), &Ekos::Capture::newImage, this, &Ekos::Manager::updateCaptureProgress);
3750 FITSfromFile = previewFromFile;
3751 appendLogText(i18n("Preview source reset to internal"));
3752 }
3753}
3754
3755void Manager::previewFile(QString filePath)
3756{
3757 capturePreview->updateJobPreview(filePath);
3758 appendLogText(i18n("Received external preview file"));
3759}
3760}
DriverInfo holds all metadata associated with a particular INDI driver.
Definition driverinfo.h:46
Align class handles plate-solving and polar alignment measurement and correction using astrometry....
Definition align.h:77
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:595
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:603
void setFocusStatus(FocusState newstate, const QString &trainname)
setFocusStatus Forward the new focus state to the capture module state machine
Definition capture.cpp:246
void setFocusTemperatureDelta(double focusTemperatureDelta, double absTemperature, const QString &trainname)
setFocusTemperatureDelta update the focuser's temperature delta
Definition capture.cpp:329
void setGuideDeviation(double delta_ra, double delta_dec)
setGuideDeviation Set the guiding deviation as measured by the guiding module.
Definition capture.cpp:338
void focusAdaptiveComplete(bool success, const QString &trainname)
focusAdaptiveComplete Forward the new focus state to the capture module state machine
Definition capture.cpp:254
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.
Performs calibration and autoguiding using an ST4 port or directly via the INDI driver.
Definition guide.h:51
Q_SCRIPTABLE bool resume()
DBUS interface function.
Definition guide.cpp:1429
Q_SCRIPTABLE bool suspend()
DBUS interface function.
Definition guide.cpp:1419
Q_SCRIPTABLE bool dither()
DBUS interface function.
Definition guide.cpp:1383
void resetNonGuidedDither()
Reset non guided dithering properties and initialize the random generator seed if not already done.
Definition guide.cpp:2607
Q_SCRIPTABLE bool abort()
DBUS interface function.
Definition guide.cpp:889
Supports controlling INDI telescope devices including setting/retrieving mount properties,...
Definition mount.h:33
void newTarget(SkyPoint &currentCoord)
The mount has finished the slew to a new target.
void paaStageChanged(int stage)
React upon status changes of the polar alignment - mainly to avoid meridian flips happening during po...
Definition mount.cpp: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
GenericDevice is the Generic Device for INDI devices.
Definition indistd.h:117
Handles operation of a remotely controlled light box.
device handle controlling Mounts.
Definition indimount.h: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:215
Q_INVOKABLE SimClock * clock()
Definition kstarsdata.h:218
static KStars * Instance()
Definition kstars.h:121
Q_SCRIPTABLE bool setGeoLocation(const QString &city, const QString &province, const QString &country)
DBUS interface function.
virtual KActionCollection * actionCollection() const
Primary class to handle all Ekos modules.
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:42
The sky coordinates of a point in the sky.
Definition skypoint.h:45
const CachingDms & dec() const
Definition skypoint.h:269
const CachingDms & ra0() const
Definition skypoint.h:251
const CachingDms & ra() const
Definition skypoint.h:263
const dms & az() const
Definition skypoint.h:275
const dms & alt() const
Definition skypoint.h:281
const CachingDms & dec0() const
Definition skypoint.h:257
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition dms.cpp:429
const 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:3827
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
QString name(GameStandardAction id)
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)
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)
QString arg(Args &&... args) const const
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-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:43 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.