ekosmanager.cpp 61.7 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"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
23
#include "kstars.h"
24
25
#include "kstarsdata.h"
#include "auxiliary/ksuserdb.h"
26
#include "fitsviewer/fitsviewer.h"
27
#include "skymap.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
28

29
30
#include "capture/sequencejob.h"
#include "auxiliary/darklibrary.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
31

32
33
#include "profileeditor.h"
#include "profileinfo.h"
34
#include "auxiliary/QProgressIndicator.h"
35

Jasem Mutlaq's avatar
Jasem Mutlaq committed
36
37
38
39
40
41
42
#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"
43
#include "indi/indiwebmanager.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
44

45
46
#include "ekosadaptor.h"

Jasem Mutlaq's avatar
Jasem Mutlaq committed
47
#define MAX_REMOTE_INDI_TIMEOUT 15000
48
#define MAX_LOCAL_INDI_TIMEOUT 5000
49

50
EkosManager::EkosManager(QWidget *parent) : QDialog(parent)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
51
52
53
{
    setupUi(this);

54
55
56
    new EkosAdaptor(this);
    QDBusConnection::sessionBus().registerObject("/KStars/Ekos",  this);

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

Jasem Mutlaq's avatar
Jasem Mutlaq committed
59
    nDevices=0;
60
    //nConnectedDevices=0;
61
    useGuideHead    =false;
62
    useST4          =false;
63
    isStarted       = false;
64
    remoteManagerStart=false;
65
    localMode       = true;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
66

67
68
    indiConnectionStatus = EKOS_STATUS_IDLE;
    ekosStartingStatus   = EKOS_STATUS_IDLE;
69

70
71
    profileModel = new QStandardItemModel(0, 4);
    profileModel->setHorizontalHeaderLabels(QStringList() << "id" << "name" << "host" << "port");
72

Jasem Mutlaq's avatar
Jasem Mutlaq committed
73
74
    captureProcess = NULL;
    focusProcess   = NULL;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
75
    guideProcess   = NULL;
76
    alignProcess   = NULL;
77
    mountProcess   = NULL;
78
    domeProcess    = NULL;
Daniel Leu's avatar
Final    
Daniel Leu committed
79
    schedulerProcess = NULL;
80
    weatherProcess = NULL;
81
    dustCapProcess = NULL;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
82

83
84
    ekosOption     = NULL;

85
    focusStarPixmap=guideStarPixmap=NULL;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
86
87

    mountPI=capturePI=focusPI=guidePI=NULL;
88

89
    captureProgress->setValue(0);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
90
    sequenceProgress->setValue(0);
91
    sequenceProgress->setDecimals(0);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
92
93
94
    sequenceProgress->setFormat("%v");
    countdownTimer.setInterval(1000);
    connect(&countdownTimer, SIGNAL(timeout()), this, SLOT(updateCaptureCountDown()));
95

96
    toolsWidget->setIconSize(QSize(64,64));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
97
    connect(toolsWidget, SIGNAL(currentChanged(int)), this, SLOT(processTabChange()));
98

99
    // Enable scheduler Tab
100
101
    toolsWidget->setTabEnabled(1, false);

102
    // Start/Stop INDI Server
103
104
    connect(processINDIB, SIGNAL(clicked()), this, SLOT(processINDI()));

105
    // Connect/Disconnect INDI devices
106
107
108
    connect(connectB, SIGNAL(clicked()), this, SLOT(connectDevices()));
    connect(disconnectB, SIGNAL(clicked()), this, SLOT(disconnectDevices()));

109
    // INDI Control Panel
110
111
112
    connect(controlPanelB, SIGNAL(clicked()), GUIManager::Instance(), SLOT(show()));
    connect(optionsB, SIGNAL(clicked()), KStars::Instance(), SLOT(slotViewOps()));

113
    // Clear Ekos Log
114
115
    connect(clearB, SIGNAL(clicked()), this, SLOT(clearLog()));

Jasem Mutlaq's avatar
Jasem Mutlaq committed
116
    // Summary
117
    previewPixmap = new QPixmap(QPixmap(":/images/noimage.png"));    
Jasem Mutlaq's avatar
Jasem Mutlaq committed
118

119
120
121
122
123
124
125
    // 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)));

    // Set Profile icons
126
    addProfileB->setIcon(QIcon::fromTheme("list-add", QIcon(":/icons/breeze/default/list-add.svg")));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
127
    editProfileB->setIcon(QIcon::fromTheme("document-edit", QIcon(":/icons/breeze/default/document-edit.svg")));
128
    deleteProfileB->setIcon(QIcon::fromTheme("list-remove", QIcon(":/icons/breeze/default/list-remove.svg")));
129
130
131

    // Load all drivers
    loadDrivers();
132

133
134
135
    // Load add driver profiles
    loadProfiles();

136
137
138
139
    // INDI Control Panel and Ekos Options
    controlPanelB->setIcon(QIcon::fromTheme("kstars_indi", QIcon(":/icons/indi.png")));
    optionsB->setIcon(QIcon::fromTheme("configure", QIcon(":/icons/ekos_setup.png")));

140
    // Setup Tab
141
    toolsWidget->tabBar()->setTabIcon(0, QIcon(":/icons/ekos_setup.png"));
142
143
    toolsWidget->tabBar()->setTabToolTip(0, i18n("Setup"));

144
    // Initialize Ekos Scheduler Module
145
    schedulerProcess = new Ekos::Scheduler();
146
    toolsWidget->addTab( schedulerProcess, QIcon(":/icons/ekos_scheduler.png"), "");
147
    toolsWidget->tabBar()->setTabToolTip(1, i18n("Scheduler"));
Daniel Leu's avatar
Daniel Leu committed
148
    connect(schedulerProcess, SIGNAL(newLog()), this, SLOT(updateLog()));
149
    connect(schedulerProcess, SIGNAL(newTarget(QString)), mountTarget, SLOT(setText(QString)));
150

151
152
153
    // 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?
154
    // FIXME
155
    //resize(1000,750);
156
157
158
159
160
161
162
}

EkosManager::~EkosManager()
{
    delete captureProcess;
    delete focusProcess;
    delete guideProcess;
163
    delete alignProcess;
164
    delete domeProcess;
165
    delete weatherProcess;
166
    delete mountProcess;
167
    delete schedulerProcess;
168
    delete dustCapProcess;
169
170
171
    delete profileModel;

    delete previewPixmap;
172
    delete focusStarPixmap;    
173
    delete guideStarPixmap;
174
175
}

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
void EkosManager::closeEvent(QCloseEvent * /*event*/)
{
    QAction *a = KStars::Instance()->actionCollection()->action( "show_ekos" );
    a->setChecked(false);
}

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

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

Jasem Mutlaq's avatar
Jasem Mutlaq committed
194
195
196
197
198
void EkosManager::resizeEvent(QResizeEvent *)
{
    previewImage->setPixmap(previewPixmap->scaled(previewImage->width(), previewImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    if (focusStarPixmap)
        focusStarImage->setPixmap(focusStarPixmap->scaled(focusStarImage->width(), focusStarImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
199
200
    //if (focusProfilePixmap)
        //focusProfileImage->setPixmap(focusProfilePixmap->scaled(focusProfileImage->width(), focusProfileImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
201
202
    if (guideStarPixmap)
        guideStarImage->setPixmap(guideStarPixmap->scaled(guideStarImage->width(), guideStarImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
203
204
    //if (guideProfilePixmap)
        //guideProfileImage->setPixmap(guideProfilePixmap->scaled(guideProfileImage->width(), guideProfileImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
205
206
207

}

208
209
void EkosManager::loadProfiles()
{
210
211
212
    qDeleteAll(profiles);
    profiles.clear();
    KStarsData::Instance()->userdb()->GetAllProfiles(profiles);
213

214
    profileModel->clear();
215
216
217
218
219
220
221
222
223
224
225
226

    foreach(ProfileInfo *pi, profiles)
    {
        QList<QStandardItem*> info;

        info << new QStandardItem(pi->id) << new QStandardItem(pi->name) << new QStandardItem(pi->host) << new QStandardItem(pi->port);
        profileModel->appendRow(info);
    }

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

228
229
230
231
232
233
    // Load last used profile from options
    int index = profileCombo->findText(Options::profile());
    // If not found, set it to first item
    if (index == -1)
        index=0;
    profileCombo->setCurrentIndex(index);
234
235
}

236
237
238
239
240
241
242
243
244
void EkosManager::loadDrivers()
{
    foreach(DriverInfo *dv, DriverManager::Instance()->getDrivers())
    {
        if (dv->getDriverSource() != HOST_SOURCE)
            driversList[dv->getTreeLabel()] = dv;
    }
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
245
246
247
void EkosManager::reset()
{
    nDevices=0;
248
    //nConnectedDevices=0;
249
250
    nRemoteDevices=0;

251
252
    useGuideHead           = false;
    useST4                 = false;
253

254
    removeTabs();
255

256
257
    genericDevices.clear();
    managedDevices.clear();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
258
259
260
261

    captureProcess = NULL;
    focusProcess   = NULL;
    guideProcess   = NULL;
262
    domeProcess    = NULL;
263
    alignProcess   = NULL;
264
    mountProcess   = NULL;
265
    weatherProcess = NULL;
266
    dustCapProcess = NULL;
267

268
269
    ekosStartingStatus  = EKOS_STATUS_IDLE;
    indiConnectionStatus= EKOS_STATUS_IDLE;
270

271
272
    connectB->setEnabled(false);
    disconnectB->setEnabled(false);
273
    controlPanelB->setEnabled(false);
274
275
    processINDIB->setEnabled(true);

Jasem Mutlaq's avatar
Jasem Mutlaq committed
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    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("--:--:--");
    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();

298
    isStarted = false;
299
    processINDIB->setText(i18n("Start INDI"));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
300
301
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
302
303
void EkosManager::processINDI()
{
304
    if (isStarted == false)
305
        start();
306
307
    else
        stop();
308
}
Jasem Mutlaq's avatar
Jasem Mutlaq committed
309

310
311
bool EkosManager::stop()
{
312
    cleanDevices();
313
314
315
316
317
318
319
320

    profileGroup->setEnabled(true);

    return true;
}

bool EkosManager::start()
{
321
322
    if (localMode)
        qDeleteAll(managedDrivers);
323
    managedDrivers.clear();
324

325
326
327
328
329
    reset();

    ProfileInfo *currentProfile = getCurrentProfile();
    localMode = currentProfile->isLocal();

330
331
332
    // Load profile location if one exists
    updateProfileLocation(currentProfile);

333
334
    bool haveCCD=false, haveGuider=false;

335
336
    if (localMode)
    {
337
        DriverInfo *drv = NULL;
338
339
340

        drv = driversList.value(currentProfile->mount());
        if (drv != NULL)
341
            managedDrivers.append(drv->clone());
342
343
344

        drv = driversList.value(currentProfile->ccd());
        if (drv != NULL)
345
346
347
348
        {
            managedDrivers.append(drv->clone());
            haveCCD = true;
        }
349
350
351

        drv = driversList.value(currentProfile->guider());
        if (drv != NULL)
352
353
354
        {
            haveGuider = true;

Jasem Mutlaq's avatar
Jasem Mutlaq committed
355
356
357
358
359
360
361
362
363
364
365
            // 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"
            // For #2, we set guider_di to NULL and we prompt the user to select which device is primary ccd and which is guider
            // since this is the only way to find out in real time.
            if (haveCCD && currentProfile->guider() == currentProfile->ccd())
            {
                if (drv->getAuxInfo().value("mdpd", false).toBool() == true)
                    drv = NULL;
                else
366
                {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
367
                    drv->setUniqueLabel(drv->getTreeLabel() + " Guide");
368
                }
Jasem Mutlaq's avatar
Jasem Mutlaq committed
369
            }
370
371
372
373

            if (drv)
                managedDrivers.append(drv->clone());
        }
374
375
376

        drv = driversList.value(currentProfile->ao());
        if (drv != NULL)
377
            managedDrivers.append(drv->clone());
378
379
380

        drv = driversList.value(currentProfile->filter());
        if (drv != NULL)
381
            managedDrivers.append(drv->clone());
382
383
384

        drv = driversList.value(currentProfile->focuser());
        if (drv != NULL)
385
            managedDrivers.append(drv->clone());
386
387
388

        drv = driversList.value(currentProfile->dome());
        if (drv != NULL)
389
            managedDrivers.append(drv->clone());
390
391
392

        drv = driversList.value(currentProfile->weather());
        if (drv != NULL)
393
            managedDrivers.append(drv->clone());
394
395
396

        drv = driversList.value(currentProfile->aux1());
        if (drv != NULL)
397
            managedDrivers.append(drv->clone());
398
399
400

        drv = driversList.value(currentProfile->aux2());
        if (drv != NULL)
401
            managedDrivers.append(drv->clone());
402
403
404

        drv = driversList.value(currentProfile->aux3());
        if (drv != NULL)
405
            managedDrivers.append(drv->clone());
406
407
408

        drv = driversList.value(currentProfile->aux4());
        if (drv != NULL)
409
            managedDrivers.append(drv->clone());
410

411
        if (haveCCD == false && haveGuider == false)
412
413
        {
            KMessageBox::error(this, i18n("Ekos requires at least one CCD or Guider to operate."));
414
            managedDrivers.clear();
415
416
417
            return false;
        }

418
        nDevices = managedDrivers.count();
419
420
421
    }
    else
    {
422
        DriverInfo *remote_indi = new DriverInfo(QString("Ekos Remote Host"));
423
424
425
426
427

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

        remote_indi->setDriverSource(GENERATED_SOURCE);

428
429
430
431
        managedDrivers.append(remote_indi);

        haveCCD    = currentProfile->drivers.contains("CCD");
        haveGuider = currentProfile->drivers.contains("Guider");
432
433
434
435
436
437
438
439
440

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

441
        nDevices = currentProfile->drivers.count();
442
443
444
445
446
447
448
449
450
451
452
453
454
455

        nRemoteDevices=0;
    }

    connect(INDIListener::Instance(), SIGNAL(newDevice(ISD::GDInterface*)), this, SLOT(processNewDevice(ISD::GDInterface*)));
    connect(INDIListener::Instance(), SIGNAL(newTelescope(ISD::GDInterface*)), this, SLOT(setTelescope(ISD::GDInterface*)));
    connect(INDIListener::Instance(), SIGNAL(newCCD(ISD::GDInterface*)), this, SLOT(setCCD(ISD::GDInterface*)));
    connect(INDIListener::Instance(), SIGNAL(newFilter(ISD::GDInterface*)), this, SLOT(setFilter(ISD::GDInterface*)));
    connect(INDIListener::Instance(), SIGNAL(newFocuser(ISD::GDInterface*)), this, SLOT(setFocuser(ISD::GDInterface*)));
    connect(INDIListener::Instance(), SIGNAL(newDome(ISD::GDInterface*)), this, SLOT(setDome(ISD::GDInterface*)));
    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*)));
    connect(INDIListener::Instance(), SIGNAL(newST4(ISD::ST4*)), this, SLOT(setST4(ISD::ST4*)));
456
    connect(INDIListener::Instance(), SIGNAL(deviceRemoved(ISD::GDInterface*)), this, SLOT(removeDevice(ISD::GDInterface*)), Qt::DirectConnection);
457
458

    if (localMode)
459
    {
460
461
462
463
464
465
466
467
468
469
470
471
472
473
        if (isRunning("indiserver"))
        {
            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")))
            {
                //TODO is there a better way to do this.
                QProcess p;
                p.start("pkill indiserver");
                p.waitForFinished();
            }
        }

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

474
        if (DriverManager::Instance()->startDevices(managedDrivers) == false)
475
476
        {
            INDIListener::Instance()->disconnect(this);
477
478
            qDeleteAll(managedDrivers);
            managedDrivers.clear();
479
            ekosStartingStatus = EKOS_STATUS_ERROR;
480
481
482
            return false;
        }

483
        connect(DriverManager::Instance(), SIGNAL(serverTerminated(QString,QString)), this, SLOT(processServerTermination(QString, QString)));
484

485
        ekosStartingStatus = EKOS_STATUS_PENDING;
486

487
        appendLogText(i18n("INDI services started on port %1. Please connect devices.", managedDrivers.first()->getPort()));
488
489
490
491
492

        QTimer::singleShot(MAX_LOCAL_INDI_TIMEOUT, this, SLOT(checkINDITimeout()));
    }
    else
    {
493
        // If we need to use INDI Web Manager
494
        if (currentProfile->INDIWebManagerPort > 0)
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
        {
            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."));
        }

517
518
519
520
521
        appendLogText(i18n("Connecting to remote INDI server at %1 on port %2 ...", currentProfile->host, currentProfile->port));
        qApp->processEvents();

        QApplication::setOverrideCursor(Qt::WaitCursor);

522
        if (DriverManager::Instance()->connectRemoteHost(managedDrivers.first()) == false)
523
524
525
        {
            appendLogText(i18n("Failed to connect to remote INDI server!"));
            INDIListener::Instance()->disconnect(this);
526
527
            qDeleteAll(managedDrivers);
            managedDrivers.clear();
528
            ekosStartingStatus = EKOS_STATUS_ERROR;
529
530
531
532
            QApplication::restoreOverrideCursor();
            return false;
        }

533
534
        connect(DriverManager::Instance(), SIGNAL(serverTerminated(QString,QString)), this, SLOT(processServerTermination(QString, QString)));

535
        QApplication::restoreOverrideCursor();
536
        ekosStartingStatus = EKOS_STATUS_PENDING;
537

538
        appendLogText(i18n("INDI services started. Connection to remote INDI server is successful. Waiting for devices..."));
539
540
541
542
543
544
545
546
547
548

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

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

    profileGroup->setEnabled(false);

549
    isStarted = true;
550
551
    processINDIB->setText(i18n("Stop INDI"));

552
    return true;
553
554
555
556
}

void EkosManager::checkINDITimeout()
{
557
    // Don't check anything unless we're still pending
558
    if (ekosStartingStatus != EKOS_STATUS_PENDING)
559
560
        return;

561
    if (nDevices <= 0)
562
    {
563
        ekosStartingStatus = EKOS_STATUS_SUCCESS;
564
        return;
565
    }
566

567
    if (localMode)
568
569
    {
        QStringList remainingDevices;
570
571
572
573
574
        foreach(DriverInfo *drv, managedDrivers)
        {
            if (drv->getDevices().count() == 0)
                remainingDevices << QString("+ %1").arg(drv->getUniqueLabel().isEmpty() == false ? drv->getUniqueLabel() : drv->getName());
        }
575
576

        if (remainingDevices.count() == 1)
577
578
579
580
        {
            appendLogText(i18n("Unable to establish:\n%1\nPlease ensure the device is connected and powered on.", remainingDevices.at(0)));
            KNotification::beep(i18n("Ekos startup error"));
        }
581
        else
582
583
584
585
        {
            appendLogText(i18n("Unable to establish the following devices:\n%1\nPlease ensure each device is connected and powered on.", remainingDevices.join("\n")));
            KNotification::beep(i18n("Ekos startup error"));
        }
586
    }
587
    else
588
    {
589
590
        ProfileInfo *currentProfile = getCurrentProfile();

591
        QStringList remainingDevices;
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608

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

            foreach(ISD::GDInterface *device, genericDevices)
            {
                if (device->getBaseDevice()->getDriverName() == driver)
                {
                    driverFound = true;
                    break;
                }
            }

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

        if (remainingDevices.count() == 1)
611
612
613
614
        {
            appendLogText(i18n("Unable to establish remote device:\n%1\nPlease ensure remote device name corresponds to actual device name.", remainingDevices.at(0)));
            KNotification::beep(i18n("Ekos startup error"));
        }
615
        else
616
617
618
619
        {
            appendLogText(i18n("Unable to establish remote devices:\n%1\nPlease ensure remote device name corresponds to actual device name.", remainingDevices.join("\n")));
            KNotification::beep(i18n("Ekos startup error"));
        }
620
    }
621

622
    ekosStartingStatus = EKOS_STATUS_ERROR;
623
624
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
625
626
void EkosManager::connectDevices()
{
627
    indiConnectionStatus = EKOS_STATUS_PENDING;
628

629
630
    foreach(ISD::GDInterface *device, genericDevices)
        device->Connect();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
631

632
633
    connectB->setEnabled(false);
    disconnectB->setEnabled(true);
634

635
    appendLogText(i18n("Connecting INDI devices..."));
636
}
637

638
639
640
641
void EkosManager::disconnectDevices()
{
    foreach(ISD::GDInterface *device, genericDevices)
        device->Disconnect();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
642

643
    appendLogText(i18n("Disconnecting INDI devices..."));
644

645
}
Jasem Mutlaq's avatar
Jasem Mutlaq committed
646

647
void EkosManager::processServerTermination(const QString &host, const QString &port)
648
{
649
650
651
652
653
654
655
656
657
658
    ProfileInfo *pi = getCurrentProfile();

    if ( (localMode && managedDrivers.first()->getPort() == port) || (pi->host == host && pi->port == port.toInt()))
    {
        cleanDevices(false);
    }
}

void EkosManager::cleanDevices(bool stopDrivers)
{
659
    if (ekosStartingStatus == EKOS_STATUS_IDLE)
660
        return;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
661

662
    INDIListener::Instance()->disconnect(this);
663
    DriverManager::Instance()->disconnect(this);
664

665
    if (managedDrivers.isEmpty() == false)
666
    {
667
668
        if (localMode)
        {
669
670
            if (stopDrivers)
                DriverManager::Instance()->stopDevices(managedDrivers);
671
672
673
        }
        else
        {
674
675
            if (stopDrivers)
                DriverManager::Instance()->disconnectRemoteHost(managedDrivers.first());
676
677
678
679
680
681
682

            ProfileInfo *pi = getCurrentProfile();
            if (remoteManagerStart && pi->INDIWebManagerPort != -1)
            {
                INDI::WebManager::stopProfile(pi);
                remoteManagerStart = false;
            }
683
        }
684
    }
685

686
687
    reset();

Jasem Mutlaq's avatar
Jasem Mutlaq committed
688
    profileGroup->setEnabled(true);
689

690
    appendLogText(i18n("INDI services stopped."));
Jasem Mutlaq's avatar
Jasem Mutlaq committed
691
692
693
694
}

void EkosManager::processNewDevice(ISD::GDInterface *devInterface)
{
695
696
697
    if (Options::verboseLogging())
        qDebug() << "Ekos received a new device: " << devInterface->getDeviceName();

698
    genericDevices.append(devInterface);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
699

Jasem Mutlaq's avatar
Jasem Mutlaq committed
700
    nDevices--;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
701

702
703
    connect(devInterface, SIGNAL(Connected()), this, SLOT(deviceConnected()));
    connect(devInterface, SIGNAL(Disconnected()), this, SLOT(deviceDisconnected()));
704
    connect(devInterface, SIGNAL(propertyDefined(INDI::Property*)), this, SLOT(processNewProperty(INDI::Property*)));
705

706
    if (nDevices <= 0)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
707
    {
708
        ekosStartingStatus = EKOS_STATUS_SUCCESS;
709

710
        if (localMode == false && nDevices == 0)
711
            appendLogText(i18n("Remote devices established. Please connect devices."));
712

713
714
715
716
717
718
        connectB->setEnabled(true);
        disconnectB->setEnabled(false);
        controlPanelB->setEnabled(true);
    }
}

719
720
721
722
void EkosManager::deviceConnected()
{
    connectB->setEnabled(false);
    disconnectB->setEnabled(true);
723
724

    processINDIB->setEnabled(false);
725

726
727
728
    if (Options::verboseLogging())
    {
        ISD::GDInterface *device = (ISD::GDInterface *) sender();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
729
        qDebug() << "Ekos: " << device->getDeviceName() << "is connected.";
730
731
        //qDebug() << "Managed Devices: " << managedDrivers.count() << " Remote Devices: " << nRemoteDevices;
        //qDebug() << "Connected Devices: " << nConnectedDevices << " nDevices: " << nDevices;
732
733
    }

734
735
    ProfileInfo *pi = getCurrentProfile();
    //if (nConnectedDevices == managedDrivers.count() || (nDevices <=0 && nConnectedDevices == nRemoteDevices))
736
737
738
739
740
741
742
743

    int nConnectedDevices=0;
    foreach(ISD::GDInterface *device, genericDevices)
    {
        if (device->isConnected())
            nConnectedDevices++;
    }

744
    if (nConnectedDevices >= pi->drivers.count())
745
        indiConnectionStatus = EKOS_STATUS_SUCCESS;
746

747
748
    qDebug() << "Ekos: " << nConnectedDevices << " devices connected out of " << genericDevices.count();

749
    ISD::GDInterface *dev = static_cast<ISD::GDInterface *> (sender());
750

751
    if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)
752
    {
753
754
        if (mountProcess)
            mountProcess->setEnabled(true);
755
    }
756
    else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE)
757
    {
758
759
760
761
762
763
764
765
        if (captureProcess)
            captureProcess->setEnabled(true);
        if (focusProcess)
            focusProcess->setEnabled(true);
        if (alignProcess)
            alignProcess->setEnabled(true);
        if (guideProcess)
            guideProcess->setEnabled(true);
766
    }
767
    else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE)
768
    {
769
770
        if (focusProcess)
            focusProcess->setEnabled(true);
771
772
    }

773
774
    if (Options::neverLoadConfig())
        return;
775

776
    INDIConfig tConfig = Options::loadConfigOnConnection() ? LOAD_LAST_CONFIG : LOAD_DEFAULT_CONFIG;
777

778
    foreach(ISD::GDInterface *device, genericDevices)
779
    {
780
781
782
783
        if (device == dev)
        {
            ISwitchVectorProperty *configProp = device->getBaseDevice()->getSwitch("CONFIG_PROCESS");
            if (configProp && configProp->s == IPS_IDLE)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
784
                device->setConfig(tConfig);
785
786
            break;
        }
787
    }
788
789
790
791
}

void EkosManager::deviceDisconnected()
{
792
793
794
795
796
    ISD::GDInterface *dev = static_cast<ISD::GDInterface *> (sender());

    if (dev)
    {
        if (dev->getState("CONNECTION") == IPS_ALERT)
797
            indiConnectionStatus = EKOS_STATUS_ERROR;
798
        else if (dev->getState("CONNECTION") == IPS_BUSY)
799
            indiConnectionStatus = EKOS_STATUS_PENDING;
800
        else
801
            indiConnectionStatus = EKOS_STATUS_IDLE;
802
803
804
805
806
807

        if (Options::verboseLogging())
        {
            qDebug() << "Ekos: " << dev->getDeviceName() << " is disconnected.";
            //qDebug() << "Connected Devices: " << nConnectedDevices << " nDevices: " << nDevices;
        }
808
809
    }
    else
810
        indiConnectionStatus = EKOS_STATUS_IDLE;
811

812
813
    //if (indiConnectionStatus == EKOS_STATUS_IDLE)
        //nConnectedDevices--;
814

815
816
    //if (nConnectedDevices < 0)
        //nConnectedDevices = 0;
817

818
819
820
821
    connectB->setEnabled(true);
    disconnectB->setEnabled(false);
    processINDIB->setEnabled(true);

822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
    if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)
    {
        if (mountProcess)
            mountProcess->setEnabled(false);
    }
    else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE)
    {
        if (captureProcess)
            captureProcess->setEnabled(false);
        if (focusProcess)
            focusProcess->setEnabled(false);
        if (alignProcess)
            alignProcess->setEnabled(false);
        if (guideProcess)
            guideProcess->setEnabled(false);
    }
    else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE)
    {
        if (focusProcess)
            focusProcess->setEnabled(false);
    }
843
844
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
845
846
void EkosManager::setTelescope(ISD::GDInterface *scopeDevice)
{
847
    //mount = scopeDevice;
848

849
    managedDevices[KSTARS_TELESCOPE] = scopeDevice;
850

851
    appendLogText(i18n("%1 is online.", scopeDevice->getDeviceName()));
852

853
854
    connect(scopeDevice, SIGNAL(numberUpdated(INumberVectorProperty *)), this, SLOT(processNewNumber(INumberVectorProperty*)));

855
856
    initMount();

857
    mountProcess->setTelescope(scopeDevice);
858

859
    if (guideProcess)
860
        guideProcess->setTelescope(scopeDevice);
861
862

    if (alignProcess)
863
        alignProcess->setTelescope(scopeDevice);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
864
865
866
}

void EkosManager::setCCD(ISD::GDInterface *ccdDevice)
867
{
868
    managedDevices.insertMulti(KSTARS_CCD, ccdDevice);
869
870
871

    initCapture();

872
    captureProcess->addCCD(ccdDevice);
873

874
    ProfileInfo *pi = getCurrentProfile();
875
    QString primaryCCD, guiderCCD;
876

877
878
879
    // Only look for primary & guider CCDs if we can tell a difference between them
    // otherwise rely on saved options
    if (pi->ccd() != pi->guider())
880
    {
881
        foreach(ISD::GDInterface *device, findDevices(KSTARS_CCD))
882
        {
883
884
885
886
            if (QString(device->getDeviceName()).startsWith(pi->ccd(), Qt::CaseInsensitive))
                primaryCCD = QString(device->getDeviceName());
            else if (QString(device->getDeviceName()).startsWith(pi->guider(), Qt::CaseInsensitive))
                guiderCCD = QString(device->getDeviceName());
887
        }
888
    }
889

890
891
892
893
    bool rc=false;
    if (Options::defaultCaptureCCD().isEmpty() == false)
        rc = captureProcess->setCCD(Options::defaultCaptureCCD());
    if (rc == false && primaryCCD.isEmpty() == false)
894
        captureProcess->setCCD(primaryCCD);
895
896
897

    initFocus();

898
    focusProcess->addCCD(ccdDevice);
899

900
901
902
903
    rc=false;
    if (Options::defaultFocusCCD().isEmpty() == false)
        rc = focusProcess->setCCD(Options::defaultFocusCCD());
    if (rc == false && primaryCCD.isEmpty() == false)
904
        focusProcess->setCCD(primaryCCD);
905

906

907
    initAlign();
908

909
    alignProcess->addCCD(ccdDevice);
910

911
912
913
914
    rc=false;
    if (Options::defaultAlignCCD().isEmpty() == false)
        rc = alignProcess->setCCD(Options::defaultAlignCCD());
    if (rc == false && primaryCCD.isEmpty() == false)
915
        alignProcess->setCCD(primaryCCD);
916

917
    initGuide();
918

919
    guideProcess->addCCD(ccdDevice);
920

921
922
923
924
    rc=false;
    if (Options::defaultGuideCCD().isEmpty() == false)
        rc = guideProcess->setCCD(Options::defaultGuideCCD());
    if (rc == false && guiderCCD.isEmpty() == false)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
925
        guideProcess->setCCD(guiderCCD);
Jasem Mutlaq's avatar