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"));
264 connect(logsB, &QPushButton::clicked, dialog, &KConfigDialog::show);
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 const bool isCapture = (captureModule() && captureModule() == currentWidget);
2921 const bool isScheduler = (schedulerModule() && schedulerModule() == currentWidget);
2922 const bool isAnalyze = (analyzeProcess.get() && analyzeProcess.get() == currentWidget);
2923 if (isCapture || isScheduler || isAnalyze)
2924 {
2925 if (opsEkos)
2926 {
2927 int index = 0;
2928 if (isScheduler) index = 1;
2929 else if (isCapture) index = 2;
2930 else if (isAnalyze) index = 3;
2931 opsEkos->setCurrentIndex(index);
2932 }
2933 KConfigDialog * cDialog = KConfigDialog::exists("settings");
2934 if (cDialog)
2935 {
2936 cDialog->setCurrentPage(ekosOptionsWidget);
2937 cDialog->show();
2938 cDialog->raise(); // for MacOS
2939 cDialog->activateWindow(); // for Windows
2940 }
2941 return;
2942 }
2943
2944 if (ekosOptionsWidget == nullptr)
2945 {
2946 optionsB->click();
2947 }
2948 else if (KConfigDialog::showDialog("settings"))
2949 {
2950 KConfigDialog * cDialog = KConfigDialog::exists("settings");
2951 if (cDialog)
2952 {
2953 cDialog->setCurrentPage(ekosOptionsWidget);
2954 cDialog->show();
2955 cDialog->raise(); // for MacOS
2956 cDialog->activateWindow(); // for Windows
2957 }
2958 }
2959}
2960
2961void Manager::updateDebugInterfaces()
2962{
2964
2965 for (auto &device : INDIListener::devices())
2966 {
2967 auto debugProp = device->getProperty("DEBUG");
2968 if (!debugProp)
2969 continue;
2970
2971 auto debugSP = debugProp.getSwitch();
2972
2973 // Check if the debug interface matches the driver device class
2974 if ( ( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) &&
2975 debugSP->sp[0].s != ISS_ON)
2976 {
2977 debugSP->at(0)->setState(ISS_ON);
2978 debugSP->at(1)->setState(ISS_OFF);
2979 device->sendNewProperty(debugSP);
2980 appendLogText(i18n("Enabling debug logging for %1...", device->getDeviceName()));
2981 }
2982 else if ( !( opsLogs->getINDIDebugInterface() & device->getDriverInterface() ) &&
2983 debugSP->sp[0].s != ISS_OFF)
2984 {
2985 debugSP->at(0)->setState(ISS_OFF);
2986 debugSP->at(1)->setState(ISS_ON);
2987 device->sendNewProperty(debugSP);
2988 appendLogText(i18n("Disabling debug logging for %1...", device->getDeviceName()));
2989 }
2990
2991 if (opsLogs->isINDISettingsChanged())
2992 device->setConfig(SAVE_CONFIG);
2993 }
2994}
2995
2996void Manager::watchDebugProperty(INDI::Property prop)
2997{
2998 if (prop.isNameMatch("DEBUG"))
2999 {
3000 auto svp = prop.getSwitch();
3001
3002 ISD::GenericDevice * deviceInterface = qobject_cast<ISD::GenericDevice *>(sender());
3003
3004 // We don't process pure general interfaces
3005 if (deviceInterface->getDriverInterface() == INDI::BaseDevice::GENERAL_INTERFACE)
3006 return;
3007
3008 // If debug was turned off, but our logging policy requires it then turn it back on.
3009 // We turn on debug logging if AT LEAST one driver interface is selected by the logging settings
3010 if (svp->s == IPS_OK && svp->sp[0].s == ISS_OFF &&
3011 (opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface()))
3012 {
3013 svp->sp[0].s = ISS_ON;
3014 svp->sp[1].s = ISS_OFF;
3015 deviceInterface->sendNewProperty(svp);
3016 appendLogText(i18n("Re-enabling debug logging for %1...", deviceInterface->getDeviceName()));
3017 }
3018 // To turn off debug logging, NONE of the driver interfaces should be enabled in logging settings.
3019 // For example, if we have CCD+FilterWheel device and CCD + Filter Wheel logging was turned on in
3020 // the log settings, then if the user turns off only CCD logging, the debug logging is NOT
3021 // turned off until he turns off Filter Wheel logging as well.
3022 else if (svp->s == IPS_OK && svp->sp[0].s == ISS_ON
3023 && !(opsLogs->getINDIDebugInterface() & deviceInterface->getDriverInterface()))
3024 {
3025 svp->sp[0].s = ISS_OFF;
3026 svp->sp[1].s = ISS_ON;
3027 deviceInterface->sendNewProperty(svp);
3028 appendLogText(i18n("Re-disabling debug logging for %1...", deviceInterface->getDeviceName()));
3029 }
3030 }
3031}
3032
3033void Manager::announceEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event)
3034{
3035 ekosLiveClient.get()->message()->sendEvent(message, source, event);
3036}
3037
3038void Manager::connectModules()
3039{
3040 // Dark Library
3041 connect(DarkLibrary::Instance(), &DarkLibrary::newImage, ekosLiveClient.get()->media(),
3042 &EkosLive::Media::sendDarkLibraryData, Qt::UniqueConnection);
3043 connect(DarkLibrary::Instance(), &DarkLibrary::trainChanged, ekosLiveClient.get()->message(),
3044 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3045 connect(DarkLibrary::Instance(), &DarkLibrary::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3047 connect(DarkLibrary::Instance(), &DarkLibrary::settingsUpdated, ekosLiveClient.get()->message(),
3048 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection);
3049
3050 // Guide <---> Capture connections
3051 if (captureProcess && guideProcess)
3052 {
3053 // captureProcess.get()->disconnect(guideProcess.get());
3054 // guideProcess.get()->disconnect(captureProcess.get());
3055
3056 // Guide Limits
3057 connect(guideModule(), &Ekos::Guide::newStatus, captureModule(), &Ekos::Capture::setGuideStatus,
3059 connect(guideModule(), &Ekos::Guide::newAxisDelta, captureModule(), &Ekos::Capture::setGuideDeviation,
3061
3062 // Dithering
3063 connect(captureModule(), &Ekos::Capture::dither, guideModule(), &Ekos::Guide::dither, Qt::UniqueConnection);
3064 connect(captureModule(), &Ekos::Capture::resetNonGuidedDither, guideModule(), &Ekos::Guide::resetNonGuidedDither,
3066
3067 // Guide Head
3068 connect(captureModule(), &Ekos::Capture::suspendGuiding, guideModule(), &Ekos::Guide::suspend,
3070 connect(captureModule(), &Ekos::Capture::resumeGuiding, guideModule(), &Ekos::Guide::resume,
3072 connect(guideModule(), &Ekos::Guide::guideChipUpdated, captureModule(), &Ekos::Capture::setGuideChip,
3074
3075 // Meridian Flip
3076 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, guideModule(), &Ekos::Guide::abort,
3078 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, guideModule(),
3079 &Ekos::Guide::guideAfterMeridianFlip, Qt::UniqueConnection);
3080 }
3081
3082 // Guide <---> Mount connections
3083 if (guideProcess && mountProcess)
3084 {
3085 // Parking
3086 connect(mountModule(), &Ekos::Mount::newStatus, guideModule(), &Ekos::Guide::setMountStatus,
3088 connect(mountModule(), &Ekos::Mount::newCoords, guideModule(), &Ekos::Guide::setMountCoords,
3090
3091 }
3092
3093 // Focus <---> Guide connections
3094 if (guideProcess && focusProcess)
3095 {
3096 // Suspend
3097 connect(focusModule(), &Ekos::FocusModule::suspendGuiding, guideModule(), &Ekos::Guide::suspend,
3099 connect(focusModule(), &Ekos::FocusModule::resumeGuiding, guideModule(), &Ekos::Guide::resume,
3101 }
3102
3103 // Capture <---> Focus connections
3104 if (captureProcess && focusProcess)
3105 {
3106 // Check focus HFR value and if above threshold parameter, run autoFocus
3107 connect(captureModule(), &Ekos::Capture::checkFocus, focusModule(), &Ekos::FocusModule::checkFocus,
3109
3110 // Run autoFocus
3111 connect(captureProcess.get(), &Ekos::Capture::runAutoFocus, focusModule(), &Ekos::FocusModule::runAutoFocus,
3113
3114 // Reset Frame
3115 connect(captureModule(), &Ekos::Capture::resetFocusFrame, focusModule(), &Ekos::FocusModule::resetFrame,
3117
3118 // Abort Focus
3119 connect(captureModule(), &Ekos::Capture::abortFocus, focusModule(), &Ekos::FocusModule::abort, Qt::UniqueConnection);
3120
3121 // New Focus Status
3122 connect(focusModule(), &Ekos::FocusModule::newStatus, captureModule(), &Ekos::Capture::setFocusStatus,
3124
3125 // Perform adaptive focus
3126 connect(captureModule(), &Ekos::Capture::adaptiveFocus, focusModule(), &Ekos::FocusModule::adaptiveFocus,
3128
3129 // New Adaptive Focus Status
3130 connect(focusModule(), &Ekos::FocusModule::focusAdaptiveComplete, captureModule(), &Ekos::Capture::focusAdaptiveComplete,
3132
3133 // New Focus HFR
3134 connect(focusModule(), &Ekos::FocusModule::newHFR, captureModule(), &Ekos::Capture::setHFR, Qt::UniqueConnection);
3135
3136 // New Focus temperature delta
3137 connect(focusModule(), &Ekos::FocusModule::newFocusTemperatureDelta, captureModule(),
3139
3140 // User requested AF
3141 connect(focusModule(), &Ekos::FocusModule::inSequenceAF, captureModule(),
3143
3144 // Meridian Flip
3145 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, focusModule(), &Ekos::FocusModule::meridianFlipStarted,
3147 }
3148
3149 // Capture <---> Align connections
3150 if (captureProcess && alignProcess)
3151 {
3152 // Alignment flag
3153 connect(alignModule(), &Ekos::Align::newStatus, captureModule(), &Ekos::Capture::setAlignStatus,
3155 // Solver data
3156 connect(alignModule(), &Ekos::Align::newSolverResults, captureModule(), &Ekos::Capture::setAlignResults,
3158 // Capture Status
3159 connect(captureModule(), &Ekos::Capture::newStatus, alignModule(), &Ekos::Align::setCaptureStatus,
3161 }
3162
3163 // Capture <---> Mount connections
3164 if (captureProcess && mountProcess)
3165 {
3166 // Register both modules since both are now created and ready
3167 // In case one module misses the DBus signal, then it will be correctly initialized.
3168 captureModule()->registerNewModule("Mount");
3169 mountModule()->registerNewModule("Capture");
3170
3171 // Meridian Flip states
3172 connect(captureModule(), &Ekos::Capture::meridianFlipStarted, mountModule(), &Ekos::Mount::suspendAltLimits,
3174 connect(captureModule(), &Ekos::Capture::guideAfterMeridianFlip, mountModule(), &Ekos::Mount::resumeAltLimits,
3176
3177 // Mount Status
3178 connect(mountModule(), &Ekos::Mount::newStatus, captureModule(), &Ekos::Capture::setMountStatus,
3180 }
3181
3182 // Optical Train Manager ---> EkosLive connections
3183 if (ekosLiveClient)
3184 {
3185 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated, ekosLiveClient->message(),
3186 &EkosLive::Message::sendTrains, Qt::UniqueConnection);
3187 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::configurationRequested, ekosLiveClient->message(),
3188 &EkosLive::Message::requestOpticalTrains, Qt::UniqueConnection);
3189 }
3190
3191 // Capture <---> EkosLive connections
3192 if (captureProcess && ekosLiveClient)
3193 {
3194 //captureProcess.get()->disconnect(ekosLiveClient.get()->message());
3195
3196 connect(captureModule(), &Ekos::Capture::dslrInfoRequested, ekosLiveClient.get()->message(),
3197 &EkosLive::Message::requestDSLRInfo, Qt::UniqueConnection);
3198 connect(captureModule(), &Ekos::Capture::sequenceChanged, ekosLiveClient.get()->message(),
3199 &EkosLive::Message::sendCaptureSequence, Qt::UniqueConnection);
3200 connect(captureModule(), &Ekos::Capture::settingsUpdated, ekosLiveClient.get()->message(),
3201 &EkosLive::Message::sendCaptureSettings, Qt::UniqueConnection);
3202 connect(captureModule(), &Ekos::Capture::newLocalPreview, ekosLiveClient.get()->message(),
3203 &EkosLive::Message::sendPreviewLabel, Qt::UniqueConnection);
3204 connect(captureModule(), &Ekos::Capture::trainChanged, ekosLiveClient.get()->message(),
3205 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3206 }
3207
3208 // Focus <---> Align connections
3209 if (focusProcess && alignProcess)
3210 {
3211 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newStatus, alignModule(), &Ekos::Align::setFocusStatus,
3213 }
3214
3215 // Focus <---> Mount connections
3216 if (focusProcess && mountProcess)
3217 {
3218 connect(mountModule(), &Ekos::Mount::newStatus, focusModule(), &Ekos::FocusModule::setMountStatus, Qt::UniqueConnection);
3219 connect(mountModule(), &Ekos::Mount::newCoords, focusModule(), &Ekos::FocusModule::setMountCoords, Qt::UniqueConnection);
3220 }
3221
3222 // Mount <---> Align connections
3223 if (mountProcess && alignProcess)
3224 {
3225 connect(mountModule(), &Ekos::Mount::newStatus, alignModule(), &Ekos::Align::setMountStatus,
3227 connect(mountModule(), &Ekos::Mount::newTarget, alignModule(), &Ekos::Align::setTarget,
3231 connect(alignModule(), &Ekos::Align::newPAAStage, mountModule(), &Ekos::Mount::paaStageChanged,
3233 }
3234
3235 // Mount <---> Guide connections
3236 if (mountProcess && guideProcess)
3237 {
3238 connect(mountModule(), &Ekos::Mount::pierSideChanged, guideModule(), &Ekos::Guide::setPierSide,
3240 }
3241
3242 // Align <--> EkosLive connections
3243 if (alignProcess && ekosLiveClient)
3244 {
3245 // alignProcess.get()->disconnect(ekosLiveClient.get()->message());
3246 // alignProcess.get()->disconnect(ekosLiveClient.get()->media());
3247
3248 connect(alignModule(), &Ekos::Align::newStatus, ekosLiveClient.get()->message(), &EkosLive::Message::setAlignStatus,
3250 connect(alignModule(), &Ekos::Align::newSolution, ekosLiveClient.get()->message(),
3251 &EkosLive::Message::setAlignSolution, Qt::UniqueConnection);
3252 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHStage,
3253 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHStage,
3255 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newPAHMessage,
3256 ekosLiveClient.get()->message(),
3257 &EkosLive::Message::setPAHMessage, Qt::UniqueConnection);
3258 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::PAHEnabled,
3259 ekosLiveClient.get()->message(), &EkosLive::Message::setPAHEnabled,
3261 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::polarResultUpdated,
3262 ekosLiveClient.get()->message(),
3263 &EkosLive::Message::setPolarResults, Qt::UniqueConnection);
3264 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::updatedErrorsChanged,
3265 ekosLiveClient.get()->message(),
3266 &EkosLive::Message::setUpdatedErrors, Qt::UniqueConnection);
3267 connect(alignModule()->polarAlignmentAssistant(), &Ekos::PolarAlignmentAssistant::newCorrectionVector,
3268 ekosLiveClient.get()->media(),
3269 &EkosLive::Media::setCorrectionVector, Qt::UniqueConnection);
3270
3271 connect(alignModule(), &Ekos::Align::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3273 connect(alignModule(), &Ekos::Align::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendUpdatedFrame,
3275
3276 connect(alignModule(), &Ekos::Align::settingsUpdated, ekosLiveClient.get()->message(),
3277 &EkosLive::Message::sendAlignSettings, Qt::UniqueConnection);
3278
3279 connect(alignModule(), &Ekos::Align::trainChanged, ekosLiveClient.get()->message(),
3280 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3281
3282 connect(alignModule(), &Ekos::Align::manualRotatorChanged, ekosLiveClient.get()->message(),
3283 &EkosLive::Message::sendManualRotatorStatus, Qt::UniqueConnection);
3284 }
3285
3286 // Focus <--> EkosLive Connections
3287 if (focusProcess && ekosLiveClient)
3288 {
3289 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::settingsUpdated, ekosLiveClient.get()->message(),
3290 &EkosLive::Message::sendFocusSettings, Qt::UniqueConnection);
3291
3292 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newImage, ekosLiveClient.get()->media(),
3293 &EkosLive::Media::sendModuleFrame,
3295
3296 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::trainChanged, ekosLiveClient.get()->message(),
3297 &EkosLive::Message::sendTrainProfiles,
3299
3300 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusAborted,
3301 ekosLiveClient.get()->message(), &EkosLive::Message::autofocusAborted, Qt::UniqueConnection);
3302 }
3303
3304 // Guide <--> EkosLive Connections
3305 if (guideProcess && ekosLiveClient)
3306 {
3307 connect(guideModule(), &Ekos::Guide::settingsUpdated, ekosLiveClient.get()->message(),
3308 &EkosLive::Message::sendGuideSettings, Qt::UniqueConnection);
3309
3310 connect(guideModule(), &Ekos::Guide::trainChanged, ekosLiveClient.get()->message(),
3311 &EkosLive::Message::sendTrainProfiles, Qt::UniqueConnection);
3312
3313 connect(guideModule(), &Ekos::Guide::newImage, ekosLiveClient.get()->media(), &EkosLive::Media::sendModuleFrame,
3315 }
3316
3317 // Analyze connections.
3318 if (analyzeProcess)
3319 {
3320 // Scheduler <---> Analyze
3321 connect(schedulerModule(), &Ekos::Scheduler::jobStarted,
3322 analyzeProcess.get(), &Ekos::Analyze::schedulerJobStarted, Qt::UniqueConnection);
3323 connect(schedulerModule(), &Ekos::Scheduler::jobEnded,
3324 analyzeProcess.get(), &Ekos::Analyze::schedulerJobEnded, Qt::UniqueConnection);
3325 connect(schedulerModule(), &Ekos::Scheduler::targetDistance,
3326 analyzeProcess.get(), &Ekos::Analyze::newTargetDistance, Qt::UniqueConnection);
3327
3328 // Capture <---> Analyze
3329 if (captureProcess)
3330 {
3331 connect(captureModule(), &Ekos::Capture::captureComplete,
3332 analyzeProcess.get(), &Ekos::Analyze::captureComplete, Qt::UniqueConnection);
3333 connect(captureModule(), &Ekos::Capture::captureStarting,
3334 analyzeProcess.get(), &Ekos::Analyze::captureStarting, Qt::UniqueConnection);
3335 connect(captureModule(), &Ekos::Capture::captureAborted,
3336 analyzeProcess.get(), &Ekos::Analyze::captureAborted, Qt::UniqueConnection);
3337#if 0
3338 // Meridian Flip
3339 connect(captureModule(), &Ekos::Capture::meridianFlipStarted,
3340 analyzeProcess.get(), &Ekos::Analyze::meridianFlipStarted, Qt::UniqueConnection);
3341 connect(captureModule(), &Ekos::Capture::meridianFlipCompleted,
3342 analyzeProcess.get(), &Ekos::Analyze::meridianFlipComplete, Qt::UniqueConnection);
3343#endif
3344 }
3345
3346 // Guide <---> Analyze
3347 if (guideProcess)
3348 {
3349 connect(guideModule(), &Ekos::Guide::newStatus,
3350 analyzeProcess.get(), &Ekos::Analyze::guideState, Qt::UniqueConnection);
3351
3352 connect(guideModule(), &Ekos::Guide::guideStats,
3353 analyzeProcess.get(), &Ekos::Analyze::guideStats, Qt::UniqueConnection);
3354 }
3355 }
3356
3357
3358 // Focus <---> Analyze connections
3359 if (focusProcess && analyzeProcess)
3360 {
3361 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusComplete,
3362 analyzeProcess.get(), &Ekos::Analyze::autofocusComplete, Qt::UniqueConnection);
3363 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::adaptiveFocusComplete,
3364 analyzeProcess.get(), &Ekos::Analyze::adaptiveFocusComplete, Qt::UniqueConnection);
3365 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusStarting,
3366 analyzeProcess.get(), &Ekos::Analyze::autofocusStarting, Qt::UniqueConnection);
3367 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::autofocusAborted,
3368 analyzeProcess.get(), &Ekos::Analyze::autofocusAborted, Qt::UniqueConnection);
3369 connect(focusModule()->mainFocuser().get(), &Ekos::Focus::newFocusTemperatureDelta,
3370 analyzeProcess.get(), &Ekos::Analyze::newTemperature, Qt::UniqueConnection);
3371 }
3372
3373 // Align <---> Analyze connections
3374 if (alignProcess && analyzeProcess)
3375 {
3376 connect(alignModule(), &Ekos::Align::newStatus,
3377 analyzeProcess.get(), &Ekos::Analyze::alignState, Qt::UniqueConnection);
3378
3379 }
3380
3381 // Mount <---> Analyze connections
3382 if (mountProcess && analyzeProcess)
3383 {
3384 connect(mountModule(), &Ekos::Mount::newStatus,
3385 analyzeProcess.get(), &Ekos::Analyze::mountState, Qt::UniqueConnection);
3386 connect(mountModule(), &Ekos::Mount::newCoords,
3387 analyzeProcess.get(), &Ekos::Analyze::mountCoords, Qt::UniqueConnection);
3388 connect(mountModule()->getMeridianFlipState().get(), &Ekos::MeridianFlipState::newMountMFStatus,
3389 analyzeProcess.get(), &Ekos::Analyze::mountFlipStatus, Qt::UniqueConnection);
3390 }
3391}
3392
3393void Manager::setEkosLiveConnected(bool enabled)
3394{
3395 ekosLiveClient.get()->setConnected(enabled);
3396}
3397
3398void Manager::setEkosLiveConfig(bool rememberCredentials, bool autoConnect)
3399{
3400 ekosLiveClient.get()->setConfig(rememberCredentials, autoConnect);
3401}
3402
3403void Manager::setEkosLiveUser(const QString &username, const QString &password)
3404{
3405 ekosLiveClient.get()->setUser(username, password);
3406}
3407
3408bool Manager::ekosLiveStatus()
3409{
3410 return ekosLiveClient.get()->isConnected();
3411}
3412
3413bool Manager::checkUniqueBinaryDriver(const QSharedPointer<DriverInfo> &primaryDriver,
3414 const QSharedPointer<DriverInfo> &secondaryDriver)
3415{
3416 if (!primaryDriver || !secondaryDriver)
3417 return false;
3418
3419 return (primaryDriver->getExecutable() == secondaryDriver->getExecutable() &&
3420 primaryDriver->getAuxInfo().value("mdpd", false).toBool() == true);
3421}
3422
3423void Manager::restartDriver(const QString &deviceName)
3424{
3425 qCInfo(KSTARS_EKOS) << "Restarting driver" << deviceName;
3426 if (m_LocalMode)
3427 {
3428 for (auto &oneDevice : INDIListener::devices())
3429 {
3430 if (oneDevice->getDeviceName() == deviceName)
3431 {
3432 DriverManager::Instance()->restartDriver(oneDevice->getDriverInfo());
3433 break;
3434 }
3435 }
3436 }
3437 else
3438 INDI::WebManager::restartDriver(m_CurrentProfile, deviceName);
3439}
3440
3441void Manager::setEkosLoggingEnabled(const QString &name, bool enabled)
3442{
3443 // LOGGING, FILE, DEFAULT are exclusive, so one of them must be SET to TRUE
3444 if (name == "LOGGING")
3445 {
3446 Options::setDisableLogging(!enabled);
3447 if (!enabled)
3449 }
3450 else if (name == "FILE")
3451 {
3452 Options::setLogToFile(enabled);
3453 if (enabled)
3455 }
3456 else if (name == "DEFAULT")
3457 {
3458 Options::setLogToDefault(enabled);
3459 if (enabled)
3461 }
3462 // VERBOSE should be set to TRUE if INDI or Ekos logging is selected.
3463 else if (name == "VERBOSE")
3464 {
3465 Options::setVerboseLogging(enabled);
3467 }
3468 // Toggle INDI Logging
3469 else if (name == "INDI")
3470 {
3471 Options::setINDILogging(enabled);
3473 }
3474 else if (name == "FITS")
3475 {
3476 Options::setFITSLogging(enabled);
3478 }
3479 else if (name == "CAPTURE")
3480 {
3481 Options::setCaptureLogging(enabled);
3482 Options::setINDICCDLogging(enabled);
3483 Options::setINDIFilterWheelLogging(enabled);
3485 }
3486 else if (name == "FOCUS")
3487 {
3488 Options::setFocusLogging(enabled);
3489 Options::setINDIFocuserLogging(enabled);
3491 }
3492 else if (name == "GUIDE")
3493 {
3494 Options::setGuideLogging(enabled);
3495 Options::setINDICCDLogging(enabled);
3497 }
3498 else if (name == "ALIGNMENT")
3499 {
3500 Options::setAlignmentLogging(enabled);
3502 }
3503 else if (name == "MOUNT")
3504 {
3505 Options::setMountLogging(enabled);
3506 Options::setINDIMountLogging(enabled);
3508 }
3509 else if (name == "SCHEDULER")
3510 {
3511 Options::setSchedulerLogging(enabled);
3513 }
3514 else if (name == "OBSERVATORY")
3515 {
3516 Options::setObservatoryLogging(enabled);
3518 }
3519}
3520
3521void Manager::acceptPortSelection()
3522{
3523 if (m_PortSelector)
3524 m_PortSelector->accept();
3525}
3526
3527void Manager::setPortSelectionComplete()
3528{
3529 if (m_CurrentProfile->portSelector)
3530 {
3531 // Turn off port selector
3532 m_CurrentProfile->portSelector = false;
3533 KStarsData::Instance()->userdb()->SaveProfile(m_CurrentProfile);
3534 }
3535
3536 if (m_CurrentProfile->autoConnect)
3537 connectDevices();
3538}
3539
3540void Manager::activateModule(const QString &name, bool popup)
3541{
3542 auto child = toolsWidget->findChild<QWidget *>(name);
3543 if (child)
3544 {
3545 toolsWidget->setCurrentWidget(child);
3546 if (popup)
3547 {
3548 raise();
3549 activateWindow();
3550 showNormal();
3551 }
3552 }
3553}
3554
3555void Manager::createModules(const QSharedPointer<ISD::GenericDevice> &device)
3556{
3557 if (device->isConnected())
3558 {
3559 if (device->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE)
3560 {
3561 initCapture();
3562 initFocus();
3563 initAlign();
3564 initGuide();
3565 }
3566 if (device->getDriverInterface() & INDI::BaseDevice::FILTER_INTERFACE)
3567 {
3568 initCapture();
3569 initFocus();
3570 initAlign();
3571 }
3572 if (device->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE)
3573 initFocus();
3574 if (device->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)
3575 {
3576 initCapture();
3577 initAlign();
3578 initGuide();
3579 initMount();
3580 }
3581 if (device->getDriverInterface() & INDI::BaseDevice::ROTATOR_INTERFACE)
3582 {
3583 initCapture();
3584 initAlign();
3585 }
3586 if (device->getDriverInterface() & INDI::BaseDevice::DOME_INTERFACE)
3587 {
3588 initCapture();
3589 initAlign();
3590 initObservatory();
3591 }
3592 if (device->getDriverInterface() & INDI::BaseDevice::WEATHER_INTERFACE)
3593 {
3594 initFocus();
3595 initObservatory();
3596 }
3597 if (device->getDriverInterface() & INDI::BaseDevice::DUSTCAP_INTERFACE)
3598 {
3599 initCapture();
3600 }
3601 if (device->getDriverInterface() & INDI::BaseDevice::LIGHTBOX_INTERFACE)
3602 {
3603 initCapture();
3604 }
3605 if (device->getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)
3606 {
3607 initMount();
3608 }
3609 }
3610}
3611
3612void Manager::setDeviceReady()
3613{
3614 // Check if ALL our devices are ready.
3615 // Ready indicates that all properties have been defined.
3616 if (isINDIReady() == false)
3617 {
3618 auto device = static_cast<ISD::GenericDevice*>(sender());
3619 if (device)
3620 {
3621
3622 if (device->isConnected() == false && m_CurrentProfile->autoConnect)
3623 {
3624 // Do we have port selector checked?
3625 if (m_CurrentProfile->portSelector)
3626 {
3627 // If port selector was not initialized, kick off the timer
3628 // so we can check if all devices should be connected.
3629 // Otherwise, if port selector is started, then let user
3630 // select ports first and then manually connect time.
3631 if (!m_PortSelector)
3632 m_PortSelectorTimer.start();
3633 }
3634 else
3635 {
3636 qCInfo(KSTARS_EKOS) << "Connecting to" << device->getDeviceName();
3637 device->Connect();
3638 }
3639 }
3640 else
3641 qCInfo(KSTARS_EKOS) << device->getDeviceName() << "is connected and ready.";
3642 }
3643
3644 if (m_ekosStatus != Ekos::Success)
3645 return;
3646 }
3647
3648 // If port selector is active, then do not show optical train dialog unless it is dismissed first.
3649 if (m_DriverDevicesCount <= 0 && (m_CurrentProfile->portSelector == false || !m_PortSelector))
3650 {
3651 for (auto &device : INDIListener::devices())
3652 syncGenericDevice(device);
3653 OpticalTrainManager::Instance()->setProfile(m_CurrentProfile);
3654 }
3655}
3656
3657void Manager::createFilterManager(ISD::FilterWheel *device)
3658{
3659 auto name = device->getDeviceName();
3660 if (m_FilterManagers.contains(name) == false)
3661 {
3662 QSharedPointer<FilterManager> newFM(new FilterManager(this));
3663 newFM->setFilterWheel(device);
3664 m_FilterManagers[name] = newFM;
3665 }
3666 else
3667 m_FilterManagers[name]->setFilterWheel(device);
3668
3669}
3670
3671bool Manager::getFilterManager(const QString &name, QSharedPointer<FilterManager> &fm)
3672{
3673 if (m_FilterManagers.contains(name))
3674 {
3675 fm = m_FilterManagers[name];
3676 return true;
3677 }
3678 return false;
3679}
3680
3681bool Manager::getFilterManager(QSharedPointer<FilterManager> &fm)
3682{
3683 if (m_FilterManagers.size() > 0)
3684 {
3685 fm = m_FilterManagers.values()[0];
3686 return true;
3687 }
3688 return false;
3689}
3690
3691void Manager::createRotatorController(ISD::Rotator *device)
3692{
3693 auto Name = device->getDeviceName();
3694 if (m_RotatorControllers.contains(Name) == false)
3695 {
3696 QSharedPointer<RotatorSettings> newRC(new RotatorSettings(this));
3697 // Properties are fetched in RotatorSettings::initRotator!
3698 m_RotatorControllers[Name] = newRC;
3699 }
3700}
3701
3702bool Manager::getRotatorController(const QString &Name, QSharedPointer<RotatorSettings> &rs)
3703{
3704 if (m_RotatorControllers.contains(Name))
3705 {
3706 rs = m_RotatorControllers[Name];
3707 return true;
3708 }
3709 return false;
3710}
3711
3712bool Manager::existRotatorController()
3713{
3714 return (!m_RotatorControllers.empty());
3715}
3716
3717void Manager::setFITSfromFile(bool previewFromFile)
3718{
3719 if (previewFromFile && !FITSfromFile)
3720 {
3721 // Prevent preview from Capture module
3722 QObject::disconnect(captureModule(), &Ekos::Capture::newImage, this, &Ekos::Manager::updateCaptureProgress);
3723 FITSfromFile = previewFromFile;
3724 appendLogText(i18n("Preview source set to external"));
3725 }
3726 else if (!previewFromFile && FITSfromFile)
3727 {
3728 // Reset preview from Capture module
3729 QObject::connect(captureModule(), &Ekos::Capture::newImage, this, &Ekos::Manager::updateCaptureProgress);
3730 FITSfromFile = previewFromFile;
3731 appendLogText(i18n("Preview source reset to internal"));
3732 }
3733}
3734
3735void Manager::previewFile(QString filePath)
3736{
3737 capturePreview->updateJobPreview(filePath);
3738 appendLogText(i18n("Received external preview file"));
3739}
3740}
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:572
void updateTargetDistance(double targetDiff)
Slot receiving the update of the current target distance.
Definition capture.h:615
void inSequenceAFRequested(bool requested, const QString &trainname)
inSequenceAFRequested Focuser informs that the user wishes an AF run as soon as possible.
Definition capture.cpp:580
void setFocusStatus(FocusState newstate, const QString &trainname)
setFocusStatus Forward the new focus state to the capture module state machine
Definition capture.cpp:223
void setFocusTemperatureDelta(double focusTemperatureDelta, double absTemperature, const QString &trainname)
setFocusTemperatureDelta update the focuser's temperature delta
Definition capture.cpp:306
void setGuideDeviation(double delta_ra, double delta_dec)
setGuideDeviation Set the guiding deviation as measured by the guiding module.
Definition capture.cpp:315
void focusAdaptiveComplete(bool success, const QString &trainname)
focusAdaptiveComplete Forward the new focus state to the capture module state machine
Definition capture.cpp:231
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())
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:123
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
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
GeoCoordinates geo(const QVariant &location)
QVariant location(const QVariant &res)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
QString name(StandardAction id)
KGuiItem reset()
KGuiItem stop()
QString label(StandardShortcut id)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
void clicked(bool checked)
void setChecked(bool)
void trigger()
void currentTextChanged(const QString &text)
QCoreApplication * instance()
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
void accepted()
virtual int exec()
void rejected()
T result() const const
void setFuture(const QFuture< T > &future)
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
QIcon fromTheme(const QString &name)
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
bool contains(const AT &value) const const
qsizetype count() const const
T & first()
bool isEmpty() const const
qsizetype length() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
QPixmap transformed(const QTransform &transform, Qt::TransformationMode mode) const const
QByteArray readAllStandardOutput()
void start(OpenMode mode)
bool waitForFinished(int msecs)
T * get() const const
QString & append(QChar ch)
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
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:15:12 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.