ekosmanager.cpp 77.4 KB
Newer Older
Jasem Mutlaq's avatar
Jasem Mutlaq committed
1
/*  Ekos
2
    Copyright (C) 2012 Jasem Mutlaq <mutlaqja@ikartech.com>
Jasem Mutlaq's avatar
Jasem Mutlaq committed
3 4 5 6 7 8 9

    This application is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.
 */

10 11 12 13
#include <QComboBox>

#include <KConfigDialog>
#include <KMessageBox>
14
#include <KActionCollection>
15
#include <KNotifications/KNotification>
Kevin Krammer's avatar
Kevin Krammer committed
16

17
#include <config-kstars.h>
18
#include <basedevice.h>
Jasem Mutlaq's avatar
Jasem Mutlaq committed
19

20 21
#include "ekosmanager.h"

22
#include "Options.h"
23
#include "opsekos.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
24
#include "kstars.h"
25 26
#include "kstarsdata.h"
#include "auxiliary/ksuserdb.h"
27
#include "fitsviewer/fitsviewer.h"
28 29
#include "fitsviewer/fitstab.h"
#include "fitsviewer/fitsview.h"
30
#include "skymap.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
31

32 33
#include "capture/sequencejob.h"
#include "auxiliary/darklibrary.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
34

35 36
#include "profileeditor.h"
#include "profileinfo.h"
37
#include "profilewizard.h"
38
#include "auxiliary/QProgressIndicator.h"
39

Jasem Mutlaq's avatar
Jasem Mutlaq committed
40 41 42 43 44 45 46
#include "indi/clientmanager.h"
#include "indi/indielement.h"
#include "indi/indiproperty.h"
#include "indi/driverinfo.h"
#include "indi/drivermanager.h"
#include "indi/indilistener.h"
#include "indi/guimanager.h"
47
#include "indi/indiwebmanager.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
48

49 50
#include "ekosadaptor.h"

Jasem Mutlaq's avatar
Jasem Mutlaq committed
51
#define MAX_REMOTE_INDI_TIMEOUT 15000
52
#define MAX_LOCAL_INDI_TIMEOUT  5000
53

54
EkosManager::EkosManager(QWidget *parent) : QDialog(parent)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
55
{
56
#ifdef Q_OS_OSX
57

58
    if (Options::independentWindowEkos())
59
        setWindowFlags(Qt::Window);
60 61 62
    else
    {
        setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
63 64
        connect(QApplication::instance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this,
                SLOT(changeAlwaysOnTop(Qt::ApplicationState)));
65 66
    }

67
#endif
Jasem Mutlaq's avatar
Jasem Mutlaq committed
68 69
    setupUi(this);

70
    new EkosAdaptor(this);
71
    QDBusConnection::sessionBus().registerObject("/KStars/Ekos", this);
72

73
    setWindowIcon(QIcon::fromTheme("kstars_ekos", QIcon(":/icons/breeze/default/kstars_ekos.svg")));
74

75
    nDevices = 0;
76
    //nConnectedDevices=0;
77 78 79 80 81
    useGuideHead       = false;
    useST4             = false;
    isStarted          = false;
    remoteManagerStart = false;
    localMode          = true;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
82

83 84
    indiConnectionStatus = EKOS_STATUS_IDLE;
    ekosStartingStatus   = EKOS_STATUS_IDLE;
85

86
    profileModel = new QStandardItemModel(0, 4);
87 88 89 90 91 92 93 94 95 96 97
    profileModel->setHorizontalHeaderLabels(QStringList() << "id"
                                                          << "name"
                                                          << "host"
                                                          << "port");

    captureProcess   = nullptr;
    focusProcess     = nullptr;
    guideProcess     = nullptr;
    alignProcess     = nullptr;
    mountProcess     = nullptr;
    domeProcess      = nullptr;
Csaba Kertesz's avatar
Csaba Kertesz committed
98
    schedulerProcess = nullptr;
99 100
    weatherProcess   = nullptr;
    dustCapProcess   = nullptr;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
101

Csaba Kertesz's avatar
Csaba Kertesz committed
102
    ekosOptionsWidget = nullptr;
103

Csaba Kertesz's avatar
Csaba Kertesz committed
104
    focusStarPixmap = guideStarPixmap = nullptr;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
105

Csaba Kertesz's avatar
Csaba Kertesz committed
106
    mountPI = capturePI = focusPI = guidePI = nullptr;
107

108
    captureProgress->setValue(0);
109
    sequenceProgress->setValue(0);
110
    sequenceProgress->setDecimals(0);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
111
    sequenceProgress->setFormat("%v");
112 113 114 115
    imageProgress->setValue(0);
    imageProgress->setDecimals(1);
    imageProgress->setFormat("%v");
    imageProgress->setBarStyle(QRoundProgressBar::StyleLine);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
116 117
    countdownTimer.setInterval(1000);
    connect(&countdownTimer, SIGNAL(timeout()), this, SLOT(updateCaptureCountDown()));
118

119
    toolsWidget->setIconSize(QSize(48, 48));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
120
    connect(toolsWidget, SIGNAL(currentChanged(int)), this, SLOT(processTabChange()));
121

122
    // Enable scheduler Tab
123 124
    toolsWidget->setTabEnabled(1, false);

125
    // Start/Stop INDI Server
126 127
    connect(processINDIB, SIGNAL(clicked()), this, SLOT(processINDI()));

128
    // Connect/Disconnect INDI devices
129 130 131
    connect(connectB, SIGNAL(clicked()), this, SLOT(connectDevices()));
    connect(disconnectB, SIGNAL(clicked()), this, SLOT(disconnectDevices()));

132
    // INDI Control Panel
133 134
    connect(controlPanelB, SIGNAL(clicked()), GUIManager::Instance(), SLOT(show()));
    connect(optionsB, SIGNAL(clicked()), KStars::Instance(), SLOT(slotViewOps()));
135 136
    // Save as above, but it appears in all modules
    connect(ekosOptionsB, SIGNAL(clicked()), SLOT(showEkosOptions()));
137

138
    // Clear Ekos Log
139 140
    connect(clearB, SIGNAL(clicked()), this, SLOT(clearLog()));

Jasem Mutlaq's avatar
Jasem Mutlaq committed
141
    // Summary
142
    // previewPixmap = new QPixmap(QPixmap(":/images/noimage.png"));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
143

144 145 146 147 148 149
    // Profiles
    connect(addProfileB, SIGNAL(clicked()), this, SLOT(addProfile()));
    connect(editProfileB, SIGNAL(clicked()), this, SLOT(editProfile()));
    connect(deleteProfileB, SIGNAL(clicked()), this, SLOT(deleteProfile()));
    connect(profileCombo, SIGNAL(activated(QString)), this, SLOT(saveDefaultProfile(QString)));

150 151 152
    // Ekos Wizard
    connect(wizardProfileB, SIGNAL(clicked()), this, SLOT(wizardProfile()));

Robert Lancaster's avatar
Robert Lancaster committed
153 154 155 156
    addProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
    editProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
    deleteProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect);

157
    // Set Profile icons
158
    addProfileB->setIcon(QIcon::fromTheme("list-add", QIcon(":/icons/breeze/default/list-add.svg")));
Robert Lancaster's avatar
Robert Lancaster committed
159
    addProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
160
    editProfileB->setIcon(QIcon::fromTheme("document-edit", QIcon(":/icons/breeze/default/document-edit.svg")));
Robert Lancaster's avatar
Robert Lancaster committed
161
    editProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
162
    deleteProfileB->setIcon(QIcon::fromTheme("list-remove", QIcon(":/icons/breeze/default/list-remove.svg")));
Robert Lancaster's avatar
Robert Lancaster committed
163
    deleteProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
164
    wizardProfileB->setIcon(QIcon::fromTheme("tools-wizard", QIcon(":/icons/breeze/default/tools-wizard.svg")));
165
    wizardProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
166 167 168

    // Load all drivers
    loadDrivers();
169

170 171 172
    // Load add driver profiles
    loadProfiles();

173
    // INDI Control Panel and Ekos Options
Robert Lancaster's avatar
Robert Lancaster committed
174 175
    controlPanelB->setIcon(QIcon::fromTheme("kstars_indi", QIcon(":/icons/breeze/default/kstars_indi.svg")));
    controlPanelB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
176
    optionsB->setIcon(QIcon::fromTheme("configure", QIcon(":/icons/ekos_setup.png")));
Robert Lancaster's avatar
Robert Lancaster committed
177
    optionsB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
178

179
    // Setup Tab
180
    toolsWidget->tabBar()->setTabIcon(0, QIcon(":/icons/ekos_setup.png"));
181 182
    toolsWidget->tabBar()->setTabToolTip(0, i18n("Setup"));

183
    // Initialize Ekos Scheduler Module
184
    schedulerProcess = new Ekos::Scheduler();
185
    toolsWidget->addTab(schedulerProcess, QIcon(":/icons/ekos_scheduler.png"), "");
186
    toolsWidget->tabBar()->setTabToolTip(1, i18n("Scheduler"));
Daniel Leu's avatar
Daniel Leu committed
187
    connect(schedulerProcess, SIGNAL(newLog()), this, SLOT(updateLog()));
188
    connect(schedulerProcess, SIGNAL(newTarget(QString)), mountTarget, SLOT(setText(QString)));
189

190 191 192
    // Temporary fix. Not sure how to resize Ekos Dialog to fit contents of the various tabs in the QScrollArea which are added
    // dynamically. I used setMinimumSize() but it doesn't appear to make any difference.
    // Also set Layout policy to SetMinAndMaxSize as well. Any idea how to fix this?
193
    // FIXME
194
    //resize(1000,750);
195 196

    previewView = new FITSView(previewWidget, FITS_NORMAL);
197
    previewWidget->setContentsMargins(0, 0, 0, 0);
198 199 200
    previewView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    previewView->setBaseSize(previewWidget->size());
    previewView->createFloatingToolBar();
201
    previewView->setMouseMode(FITSView::dragMouse);
202
    QVBoxLayout *vlayout = new QVBoxLayout();
203 204
    vlayout->addWidget(previewView);
    previewWidget->setLayout(vlayout);
205

206 207 208 209 210 211
    if (Options::ekosLeftIcons())
    {
        toolsWidget->setTabPosition(QTabWidget::West);
        QTransform trans;
        trans.rotate(90);

212 213 214
        QIcon icon  = toolsWidget->tabIcon(0);
        QPixmap pix = icon.pixmap(QSize(48, 48));
        icon        = QIcon(pix.transformed(trans));
215 216 217
        toolsWidget->setTabIcon(0, icon);

        icon = toolsWidget->tabIcon(1);
218
        pix  = icon.pixmap(QSize(48, 48));
219 220 221 222
        icon = QIcon(pix.transformed(trans));
        toolsWidget->setTabIcon(1, icon);
    }

223
    resize(Options::ekosWindowWidth(), Options::ekosWindowHeight());
224 225
}

226 227
void EkosManager::changeAlwaysOnTop(Qt::ApplicationState state)
{
228
    if (isVisible())
229
    {
230 231 232 233 234 235 236 237
        if (state == Qt::ApplicationActive)
            setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
        else
            setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
        show();
    }
}

238 239
EkosManager::~EkosManager()
{
240 241
    toolsWidget->disconnect(this);

242 243 244
    delete captureProcess;
    delete focusProcess;
    delete guideProcess;
245
    delete alignProcess;
246
    delete domeProcess;
247
    delete weatherProcess;
248
    delete mountProcess;
249
    delete schedulerProcess;
250
    delete dustCapProcess;
251 252
    delete profileModel;

253 254
    //delete previewPixmap;
    delete previewView;
255
    delete focusStarPixmap;
256
    delete guideStarPixmap;
257 258

    qDeleteAll(profiles);
259

260
    guideProcess = nullptr;
261 262
}

263 264
void EkosManager::closeEvent(QCloseEvent * /*event*/)
{
265
    QAction *a = KStars::Instance()->actionCollection()->action("show_ekos");
266
    a->setChecked(false);
267 268 269

    Options::setEkosWindowWidth(width());
    Options::setEkosWindowHeight(height());
270 271 272 273
}

void EkosManager::hideEvent(QHideEvent * /*event*/)
{
274
    QAction *a = KStars::Instance()->actionCollection()->action("show_ekos");
275 276 277 278 279
    a->setChecked(false);
}

void EkosManager::showEvent(QShowEvent * /*event*/)
{
280
    QAction *a = KStars::Instance()->actionCollection()->action("show_ekos");
281
    a->setChecked(true);
282

283 284 285 286
    // Just show the profile wizard ONCE per session
    if (profileWizardLaunched == false && profiles.count() == 1)
    {
        profileWizardLaunched = true;
287
        wizardProfile();
288
    }
289 290
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
291 292
void EkosManager::resizeEvent(QResizeEvent *)
{
293
    //previewImage->setPixmap(previewPixmap->scaled(previewImage->width(), previewImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
294
    if (focusStarPixmap)
295 296
        focusStarImage->setPixmap(focusStarPixmap->scaled(focusStarImage->width(), focusStarImage->height(),
                                                          Qt::KeepAspectRatio, Qt::SmoothTransformation));
297
    //if (focusProfilePixmap)
298
    //focusProfileImage->setPixmap(focusProfilePixmap->scaled(focusProfileImage->width(), focusProfileImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
299
    if (guideStarPixmap)
300 301
        guideStarImage->setPixmap(guideStarPixmap->scaled(guideStarImage->width(), guideStarImage->height(),
                                                          Qt::KeepAspectRatio, Qt::SmoothTransformation));
302
    //if (guideProfilePixmap)
303
    //guideProfileImage->setPixmap(guideProfilePixmap->scaled(guideProfileImage->width(), guideProfileImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
304 305
}

306 307
void EkosManager::loadProfiles()
{
308 309 310
    qDeleteAll(profiles);
    profiles.clear();
    KStarsData::Instance()->userdb()->GetAllProfiles(profiles);
311

312
    profileModel->clear();
313

314
    foreach (ProfileInfo *pi, profiles)
315
    {
316
        QList<QStandardItem *> info;
317

318 319
        info << new QStandardItem(pi->id) << new QStandardItem(pi->name) << new QStandardItem(pi->host)
             << new QStandardItem(pi->port);
320 321 322 323 324 325
        profileModel->appendRow(info);
    }

    profileModel->sort(0);
    profileCombo->setModel(profileModel);
    profileCombo->setModelColumn(1);
326

327 328 329 330
    // Load last used profile from options
    int index = profileCombo->findText(Options::profile());
    // If not found, set it to first item
    if (index == -1)
331
        index = 0;
332
    profileCombo->setCurrentIndex(index);
333 334
}

335 336
void EkosManager::loadDrivers()
{
337
    foreach (DriverInfo *dv, DriverManager::Instance()->getDrivers())
338 339 340 341 342 343
    {
        if (dv->getDriverSource() != HOST_SOURCE)
            driversList[dv->getTreeLabel()] = dv;
    }
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
344 345
void EkosManager::reset()
{
346
    nDevices = 0;
347
    //nConnectedDevices=0;
348
    nRemoteDevices = 0;
349

350 351
    useGuideHead = false;
    useST4       = false;
352

353
    removeTabs();
354

355 356
    genericDevices.clear();
    managedDevices.clear();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
357

Csaba Kertesz's avatar
Csaba Kertesz committed
358
    captureProcess = nullptr;
359 360 361 362 363
    focusProcess   = nullptr;
    guideProcess   = nullptr;
    domeProcess    = nullptr;
    alignProcess   = nullptr;
    mountProcess   = nullptr;
Csaba Kertesz's avatar
Csaba Kertesz committed
364 365
    weatherProcess = nullptr;
    dustCapProcess = nullptr;
366

367
    ekosStartingStatus   = EKOS_STATUS_IDLE;
Csaba Kertesz's avatar
Csaba Kertesz committed
368
    indiConnectionStatus = EKOS_STATUS_IDLE;
369

370 371
    connectB->setEnabled(false);
    disconnectB->setEnabled(false);
372
    controlPanelB->setEnabled(false);
373 374
    processINDIB->setEnabled(true);

Jasem Mutlaq's avatar
Jasem Mutlaq committed
375 376 377 378 379 380 381 382 383
    mountGroup->setEnabled(false);
    focusGroup->setEnabled(false);
    captureGroup->setEnabled(false);
    guideGroup->setEnabled(false);
    sequenceLabel->setText(i18n("Sequence"));
    sequenceProgress->setValue(0);
    captureProgress->setValue(0);
    overallRemainingTime->setText("--:--:--");
    sequenceRemainingTime->setText("--:--:--");
384
    imageRemainingTime->setText("--:--:--");
Jasem Mutlaq's avatar
Jasem Mutlaq committed
385 386 387 388 389 390 391 392 393 394 395 396 397
    mountStatus->setText(i18n("Idle"));
    captureStatus->setText(i18n("Idle"));
    focusStatus->setText(i18n("Idle"));
    guideStatus->setText(i18n("Idle"));
    if (capturePI)
        capturePI->stopAnimation();
    if (mountPI)
        mountPI->stopAnimation();
    if (focusPI)
        focusPI->stopAnimation();
    if (guidePI)
        guidePI->stopAnimation();

398
    isStarted = false;
399
    processINDIB->setText(i18n("Start INDI"));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
400 401
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
402 403
void EkosManager::processINDI()
{
404
    if (isStarted == false)
405
        start();
406 407
    else
        stop();
408
}
Jasem Mutlaq's avatar
Jasem Mutlaq committed
409

410 411
bool EkosManager::stop()
{
412
    cleanDevices();
413 414 415 416 417 418 419 420

    profileGroup->setEnabled(true);

    return true;
}

bool EkosManager::start()
{
421 422
    if (localMode)
        qDeleteAll(managedDrivers);
423
    managedDrivers.clear();
424

425 426 427
    // If clock was paused, unpaused it and sync time
    if (KStarsData::Instance()->clock()->isActive() == false)
    {
428
        KStarsData::Instance()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
429 430 431
        KStarsData::Instance()->clock()->start();
    }

432 433
    reset();

434
    currentProfile = getCurrentProfile();
435
    localMode      = currentProfile->isLocal();
436

437 438 439
    // Load profile location if one exists
    updateProfileLocation(currentProfile);

440
    bool haveCCD = false, haveGuider = false;
441

442 443
    if (localMode)
    {
444
        DriverInfo *drv = nullptr;
445 446

        drv = driversList.value(currentProfile->mount());
Csaba Kertesz's avatar
Csaba Kertesz committed
447
        if (drv != nullptr)
448
            managedDrivers.append(drv->clone());
449 450

        drv = driversList.value(currentProfile->ccd());
Csaba Kertesz's avatar
Csaba Kertesz committed
451
        if (drv != nullptr)
452 453 454 455
        {
            managedDrivers.append(drv->clone());
            haveCCD = true;
        }
456

457 458 459 460 461
        if (currentProfile->guider() == "PHD2")
            Options::setGuiderType(Ekos::Guide::GUIDE_PHD2);
        else if (currentProfile->guider() == "LinGuider")
            Options::setGuiderType(Ekos::Guide::GUIDE_LINGUIDER);
        else
462
        {
463 464 465
            Options::setGuiderType(Ekos::Guide::GUIDE_INTERNAL);

            drv = driversList.value(currentProfile->guider());
Csaba Kertesz's avatar
Csaba Kertesz committed
466
            if (drv != nullptr)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
467
            {
468 469 470 471 472 473
                haveGuider = true;

                // If the guider and ccd are the same driver, we have two cases:
                // #1 Drivers that only support ONE device per driver (such as sbig)
                // #2 Drivers that supports multiples devices per driver (such as sx)
                // For #1, we modify guider_di to make a unique label for the other device with postfix "Guide"
Csaba Kertesz's avatar
Csaba Kertesz committed
474
                // For #2, we set guider_di to nullptr and we prompt the user to select which device is primary ccd and which is guider
475 476
                // since this is the only way to find out in real time.
                if (haveCCD && currentProfile->guider() == currentProfile->ccd())
477
                {
478 479
                    if (drv->getAuxInfo().value("mdpd", false).toBool() == true)
                    {
Csaba Kertesz's avatar
Csaba Kertesz committed
480
                        drv = nullptr;
481 482 483
                    }
                    else
                    {
484 485
                        drv->setUniqueLabel(drv->getTreeLabel() + " Guide");
                    }
486 487
                }

488 489 490
                if (drv)
                    managedDrivers.append(drv->clone());
            }
491
        }
492 493

        drv = driversList.value(currentProfile->ao());
Csaba Kertesz's avatar
Csaba Kertesz committed
494
        if (drv != nullptr)
495
            managedDrivers.append(drv->clone());
496 497

        drv = driversList.value(currentProfile->filter());
Csaba Kertesz's avatar
Csaba Kertesz committed
498
        if (drv != nullptr)
499
            managedDrivers.append(drv->clone());
500 501

        drv = driversList.value(currentProfile->focuser());
Csaba Kertesz's avatar
Csaba Kertesz committed
502
        if (drv != nullptr)
503
            managedDrivers.append(drv->clone());
504 505

        drv = driversList.value(currentProfile->dome());
Csaba Kertesz's avatar
Csaba Kertesz committed
506
        if (drv != nullptr)
507
            managedDrivers.append(drv->clone());
508 509

        drv = driversList.value(currentProfile->weather());
Csaba Kertesz's avatar
Csaba Kertesz committed
510
        if (drv != nullptr)
511
            managedDrivers.append(drv->clone());
512 513

        drv = driversList.value(currentProfile->aux1());
Csaba Kertesz's avatar
Csaba Kertesz committed
514
        if (drv != nullptr)
515
            managedDrivers.append(drv->clone());
516 517

        drv = driversList.value(currentProfile->aux2());
Csaba Kertesz's avatar
Csaba Kertesz committed
518
        if (drv != nullptr)
519
            managedDrivers.append(drv->clone());
520 521

        drv = driversList.value(currentProfile->aux3());
Csaba Kertesz's avatar
Csaba Kertesz committed
522
        if (drv != nullptr)
523
            managedDrivers.append(drv->clone());
524 525

        drv = driversList.value(currentProfile->aux4());
Csaba Kertesz's avatar
Csaba Kertesz committed
526
        if (drv != nullptr)
527
            managedDrivers.append(drv->clone());
528

529
        if (haveCCD == false && haveGuider == false)
530 531
        {
            KMessageBox::error(this, i18n("Ekos requires at least one CCD or Guider to operate."));
532
            managedDrivers.clear();
533 534 535
            return false;
        }

536
        nDevices = managedDrivers.count();
537 538 539
    }
    else
    {
540
        DriverInfo *remote_indi = new DriverInfo(QString("Ekos Remote Host"));
541 542 543 544 545

        remote_indi->setHostParameters(currentProfile->host, QString::number(currentProfile->port));

        remote_indi->setDriverSource(GENERATED_SOURCE);

546 547 548
        managedDrivers.append(remote_indi);

        haveCCD    = currentProfile->drivers.contains("CCD");
549 550
        haveGuider = (currentProfile->drivers.contains("Guider") && currentProfile->drivers["Guider"] != "PHD2" &&
                      currentProfile->drivers["Guider"] != "LinGuider");
551 552 553 554 555 556 557

        if (currentProfile->guider() == "PHD2")
            Options::setGuiderType(Ekos::Guide::GUIDE_PHD2);
        else if (currentProfile->guider() == "LinGuider")
            Options::setGuiderType(Ekos::Guide::GUIDE_LINGUIDER);
        else
            Options::setGuiderType(Ekos::Guide::GUIDE_INTERNAL);
558 559 560 561 562

        if (haveCCD == false && haveGuider == false)
        {
            KMessageBox::error(this, i18n("Ekos requires at least one CCD or Guider to operate."));
            delete (remote_indi);
563
            nDevices = 0;
564 565 566
            return false;
        }

567
        nDevices = currentProfile->drivers.count();
568

569
        nRemoteDevices = 0;
570 571
    }

572 573 574 575
    connect(INDIListener::Instance(), SIGNAL(newDevice(ISD::GDInterface *)), this,
            SLOT(processNewDevice(ISD::GDInterface *)));
    connect(INDIListener::Instance(), SIGNAL(newTelescope(ISD::GDInterface *)), this,
            SLOT(setTelescope(ISD::GDInterface *)));
576 577
    connect(INDIListener::Instance(), SIGNAL(newCCD(ISD::GDInterface *)), this, SLOT(setCCD(ISD::GDInterface *)));
    connect(INDIListener::Instance(), SIGNAL(newFilter(ISD::GDInterface *)), this, SLOT(setFilter(ISD::GDInterface *)));
578 579
    connect(INDIListener::Instance(), SIGNAL(newFocuser(ISD::GDInterface *)), this,
            SLOT(setFocuser(ISD::GDInterface *)));
580
    connect(INDIListener::Instance(), SIGNAL(newDome(ISD::GDInterface *)), this, SLOT(setDome(ISD::GDInterface *)));
581 582 583 584 585 586
    connect(INDIListener::Instance(), SIGNAL(newWeather(ISD::GDInterface *)), this,
            SLOT(setWeather(ISD::GDInterface *)));
    connect(INDIListener::Instance(), SIGNAL(newDustCap(ISD::GDInterface *)), this,
            SLOT(setDustCap(ISD::GDInterface *)));
    connect(INDIListener::Instance(), SIGNAL(newLightBox(ISD::GDInterface *)), this,
            SLOT(setLightBox(ISD::GDInterface *)));
587
    connect(INDIListener::Instance(), SIGNAL(newST4(ISD::ST4 *)), this, SLOT(setST4(ISD::ST4 *)));
588 589
    connect(INDIListener::Instance(), SIGNAL(deviceRemoved(ISD::GDInterface *)), this,
            SLOT(removeDevice(ISD::GDInterface *)), Qt::DirectConnection);
590 591

    if (localMode)
592
    {
593 594
        if (isRunning("indiserver"))
        {
595 596 597 598 599 600
            if (KMessageBox::Yes ==
                (KMessageBox::questionYesNo(0,
                                            i18n("Ekos detected an instance of INDI server running. Do you wish to "
                                                 "shut down the existing instance before starting a new one?"),
                                            i18n("INDI Server"), KStandardGuiItem::yes(), KStandardGuiItem::no(),
                                            "ekos_shutdown_existing_indiserver")))
601 602 603 604 605 606 607 608 609 610
            {
                //TODO is there a better way to do this.
                QProcess p;
                p.start("pkill indiserver");
                p.waitForFinished();
            }
        }

        appendLogText(i18n("Starting INDI services..."));

611
        if (DriverManager::Instance()->startDevices(managedDrivers) == false)
612 613
        {
            INDIListener::Instance()->disconnect(this);
614 615
            qDeleteAll(managedDrivers);
            managedDrivers.clear();
616
            ekosStartingStatus = EKOS_STATUS_ERROR;
617 618 619
            return false;
        }

620 621
        connect(DriverManager::Instance(), SIGNAL(serverTerminated(QString, QString)), this,
                SLOT(processServerTermination(QString, QString)));
622

623
        ekosStartingStatus = EKOS_STATUS_PENDING;
624

625 626 627
        if (currentProfile->autoConnect)
            appendLogText(i18n("INDI services started on port %1.", managedDrivers.first()->getPort()));
        else
628 629
            appendLogText(
                i18n("INDI services started on port %1. Please connect devices.", managedDrivers.first()->getPort()));
630 631 632 633 634

        QTimer::singleShot(MAX_LOCAL_INDI_TIMEOUT, this, SLOT(checkINDITimeout()));
    }
    else
    {
635
        // If we need to use INDI Web Manager
636
        if (currentProfile->INDIWebManagerPort > 0)
637
        {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
638
            appendLogText(i18n("Establishing communication with remote INDI Web Manager..."));
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
            remoteManagerStart = false;
            if (INDI::WebManager::isOnline(currentProfile))
            {
                if (INDI::WebManager::areDriversRunning(currentProfile) == false)
                {
                    INDI::WebManager::stopProfile(currentProfile);

                    if (INDI::WebManager::startProfile(currentProfile) == false)
                    {
                        appendLogText(i18n("Failed to start profile on remote INDI Web Manager."));
                        return false;
                    }

                    appendLogText(i18n("Starting profile on remote INDI Web Manager..."));
                    remoteManagerStart = true;
                }
            }
            else
                appendLogText(i18n("Warning: INDI Web Manager is not online."));
        }

660 661
        appendLogText(
            i18n("Connecting to remote INDI server at %1 on port %2 ...", currentProfile->host, currentProfile->port));
662 663 664 665
        qApp->processEvents();

        QApplication::setOverrideCursor(Qt::WaitCursor);

666
        if (DriverManager::Instance()->connectRemoteHost(managedDrivers.first()) == false)
667 668 669
        {
            appendLogText(i18n("Failed to connect to remote INDI server!"));
            INDIListener::Instance()->disconnect(this);
670 671
            qDeleteAll(managedDrivers);
            managedDrivers.clear();
672
            ekosStartingStatus = EKOS_STATUS_ERROR;
673 674 675 676
            QApplication::restoreOverrideCursor();
            return false;
        }

677 678
        connect(DriverManager::Instance(), SIGNAL(serverTerminated(QString, QString)), this,
                SLOT(processServerTermination(QString, QString)));
679

680
        QApplication::restoreOverrideCursor();
681
        ekosStartingStatus = EKOS_STATUS_PENDING;
682

683 684
        appendLogText(
            i18n("INDI services started. Connection to remote INDI server is successful. Waiting for devices..."));
685 686 687 688 689 690 691 692 693 694

        QTimer::singleShot(MAX_REMOTE_INDI_TIMEOUT, this, SLOT(checkINDITimeout()));
    }

    connectB->setEnabled(false);
    disconnectB->setEnabled(false);
    controlPanelB->setEnabled(false);

    profileGroup->setEnabled(false);

695
    isStarted = true;
696 697
    processINDIB->setText(i18n("Stop INDI"));

698
    return true;
699 700 701 702
}

void EkosManager::checkINDITimeout()
{
703
    // Don't check anything unless we're still pending
704
    if (ekosStartingStatus != EKOS_STATUS_PENDING)
705 706
        return;

707
    if (nDevices <= 0)
708
    {
709
        ekosStartingStatus = EKOS_STATUS_SUCCESS;
710
        return;
711
    }
712

713
    if (localMode)
714 715
    {
        QStringList remainingDevices;
716
        foreach (DriverInfo *drv, managedDrivers)
717 718
        {
            if (drv->getDevices().count() == 0)
719 720
                remainingDevices << QString("+ %1").arg(
                    drv->getUniqueLabel().isEmpty() == false ? drv->getUniqueLabel() : drv->getName());
721
        }
722 723

        if (remainingDevices.count() == 1)
724
        {
725 726
            appendLogText(i18n("Unable to establish:\n%1\nPlease ensure the device is connected and powered on.",
                               remainingDevices.at(0)));
727 728
            KNotification::beep(i18n("Ekos startup error"));
        }
729
        else
730
        {
731 732 733
            appendLogText(i18n("Unable to establish the following devices:\n%1\nPlease ensure each device is connected "
                               "and powered on.",
                               remainingDevices.join("\n")));
734 735
            KNotification::beep(i18n("Ekos startup error"));
        }
736
    }
737
    else
738 739
    {
        QStringList remainingDevices;
740

741
        foreach (QString driver, currentProfile->drivers.values())
742
        {
743
            bool driverFound = false;
744

745
            foreach (ISD::GDInterface *device, genericDevices)
746 747 748 749 750 751 752 753 754 755 756
            {
                if (device->getBaseDevice()->getDriverName() == driver)
                {
                    driverFound = true;
                    break;
                }
            }

            if (driverFound == false)
                remainingDevices << QString("+ %1").arg(driver);
        }
757 758

        if (remainingDevices.count() == 1)
759
        {
760 761 762
            appendLogText(i18n("Unable to establish remote device:\n%1\nPlease ensure remote device name corresponds "
                               "to actual device name.",
                               remainingDevices.at(0)));
763 764
            KNotification::beep(i18n("Ekos startup error"));
        }
765
        else
766
        {
767 768 769
            appendLogText(i18n("Unable to establish remote devices:\n%1\nPlease ensure remote device name corresponds "
                               "to actual device name.",
                               remainingDevices.join("\n")));
770 771
            KNotification::beep(i18n("Ekos startup error"));
        }
772
    }
773

774
    ekosStartingStatus = EKOS_STATUS_ERROR;
775 776
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
777 778
void EkosManager::connectDevices()
{
779
    // Check if already connected
780 781
    int nConnected = 0;
    foreach (ISD::GDInterface *device, genericDevices)
782 783 784 785 786 787 788 789 790 791
    {
        if (device->isConnected())
            nConnected++;
    }
    if (genericDevices.count() == nConnected)
    {
        indiConnectionStatus = EKOS_STATUS_SUCCESS;
        return;
    }

792
    indiConnectionStatus = EKOS_STATUS_PENDING;
793

794
    foreach (ISD::GDInterface *device, genericDevices)
795
        device->Connect();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
796

797 798
    connectB->setEnabled(false);
    disconnectB->setEnabled(true);
799

800
    appendLogText(i18n("Connecting INDI devices..."));
801
}
802

803 804
void EkosManager::disconnectDevices()
{
805
    foreach (ISD::GDInterface *device, genericDevices)
806
        device->Disconnect();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
807

808
    appendLogText(i18n("Disconnecting INDI devices..."));
809
}
Jasem Mutlaq's avatar
Jasem Mutlaq committed
810

811
void EkosManager::processServerTermination(const QString &host, const QString &port)
812
{
813 814
    if ((localMode && managedDrivers.first()->getPort() == port) ||
        (currentProfile->host == host && currentProfile->port == port.toInt()))
815 816 817 818 819 820 821
    {
        cleanDevices(false);
    }
}

void EkosManager::cleanDevices(bool stopDrivers)
<