indidriver.cpp 29.6 KB
Newer Older
Jasem Mutlaq's avatar
Jasem Mutlaq committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/***************************************************************************
                          INDI Driver
                             -------------------
    begin                : Wed May 7th 2003
    copyright            : (C) 2001 by Jasem Mutlaq
    email                : mutlaqja@ikarustech.com
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/

17 18
#include "indidriver.h"

19 20 21 22 23 24 25
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <QRadioButton>
#include <QFile>
26
#include <QDir>
27 28 29 30
#include <QTextStream>
#include <QTreeWidget>
#include <QIcon>
#include <QDialog>
31
#include <QTcpServer>
32

Jasem Mutlaq's avatar
Jasem Mutlaq committed
33
#include <QMenu>
34
#include <KMessageBox>
Jasem Mutlaq's avatar
Jasem Mutlaq committed
35
#include <QPushButton>
Jasem Mutlaq's avatar
Jasem Mutlaq committed
36
#include <QLineEdit>
37
#include <KProcess>
Jasem Mutlaq's avatar
Jasem Mutlaq committed
38
#include <QAction>
39 40
#include <KActionCollection>
#include <KIconLoader>
41

Jasem Mutlaq's avatar
Jasem Mutlaq committed
42

43

44 45
#include "oal/log.h"
#include "oal/scope.h"
46
#include "indimenu.h"
47
#include "ui_indihostconf.h"
48 49
#include "devicemanager.h"
#include "indidevice.h"
50
#include "Options.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
51 52

#include "kstars.h"
53
#include "kstarsdata.h"
Jasem Mutlaq's avatar
Jasem Mutlaq committed
54 55
#include "ksutils.h"

56
#include <config-kstars.h>
Jasem Mutlaq's avatar
Jasem Mutlaq committed
57
#include <QStandardPaths>
58

59
#define  MAX_RETRIES 3
60
#define  ERRMSG_SIZE 1024
61

62 63
DeviceManagerUI::DeviceManagerUI(QWidget *parent) : QFrame(parent)
{
64

65
#ifdef Q_OS_OSX
66
        setWindowFlags(Qt::Tool| Qt::WindowStaysOnTopHint);
67
#endif
68
    setupUi(this);
69

70 71
    localTreeWidget->setSortingEnabled(false);
    localTreeWidget->setRootIsDecorated(true);
72

73
    clientTreeWidget->setSortingEnabled(false);
74

75 76 77 78
    runningPix = QIcon::fromTheme( "system-run" );
    stopPix    = QIcon::fromTheme( "dialog-cancel" );
    localMode  = QIcon::fromTheme( "computer" );
    serverMode = QIcon::fromTheme( "network-server" );
Pino Toscano's avatar
Pino Toscano committed
79

80 81
    connected           = QIcon::fromTheme( "network-connect" );
    disconnected        = QIcon::fromTheme( "network-disconnect" );
82

83 84 85 86 87 88 89 90
    connect(localTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(makePortEditable(QTreeWidgetItem*,int)));

}

void DeviceManagerUI::makePortEditable(QTreeWidgetItem* selectedItem, int column)
{
    // If it's the port column, then make it user-editable
    if (column == INDIDriver::LOCAL_PORT_COLUMN)
91 92 93
        selectedItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsEnabled);

    localTreeWidget->editItem(selectedItem, INDIDriver::LOCAL_PORT_COLUMN);
94 95
}

96
INDIDriver::INDIDriver( KStars *_ks )
Jasem Mutlaq's avatar
Jasem Mutlaq committed
97
        : QDialog( _ks ),  ksw( _ks )
98
{
Jasem Mutlaq's avatar
Jasem Mutlaq committed
99

100
    currentPort = Options::serverPortStart().toInt()-1;
101 102 103
    lastGroup = NULL;
    lastDevice = NULL;

104 105
    ui = new DeviceManagerUI( this );
    setMainWidget( ui );
Jasem Mutlaq's avatar
Jasem Mutlaq committed
106
    setCaption( xi18n( "Device Manager" ) );
Jasem Mutlaq's avatar
Jasem Mutlaq committed
107
    setButtons( QDialog::Close );
108

109 110
    foreach ( INDIHostsInfo * host, ksw->data()->INDIHostsList )
    {
111 112
        QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget, lastGroup);
        lastGroup = item;
113 114 115
        item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
        item->setText(HOST_NAME_COLUMN, host->name);
        item->setText(HOST_PORT_COLUMN, host->portnumber);
116
    }
117

118
    lastGroup = NULL;
119

120 121
    QObject::connect(ui->addB, SIGNAL(clicked()), this, SLOT(addINDIHost()));
    QObject::connect(ui->modifyB, SIGNAL(clicked()), this, SLOT(modifyINDIHost()));
122
    QObject::connect(ui->removeB, SIGNAL(clicked()), this, SLOT(removeINDIHost()));
123 124
  
  
125
    //QObject::connect(ksw->indiMenu(), SIGNAL(driverDisconnected(int)), this, SLOT(shutdownHost(int)));
126 127 128 129
    QObject::connect(ui->connectHostB, SIGNAL(clicked()), this, SLOT(activateHostConnection()));
    QObject::connect(ui->disconnectHostB, SIGNAL(clicked()), this, SLOT(activateHostDisconnection()));
    QObject::connect(ui->runServiceB, SIGNAL(clicked()), this, SLOT(activateRunService()));
    QObject::connect(ui->stopServiceB, SIGNAL(clicked()), this, SLOT(activateStopService()));
130 131
    QObject::connect(ui->localTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(updateLocalTab()));
    QObject::connect(ui->clientTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(updateClientTab()));
132
    QObject::connect(ui->localTreeWidget, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeDeviceColumn()));
133
  
134 135
    readXMLDrivers();
}
136
  
Jasem Mutlaq's avatar
Jasem Mutlaq committed
137
/*
138
void INDIDriver::enableDevice(INDI_D *indi_device)
139
{
140 141 142 143 144 145 146
   if (indi_device == NULL)
	return;
  
   if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT)
   {
   	foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
    	{
Jasem Mutlaq's avatar
Jasem Mutlaq committed
147

148 149 150
	        if (host->deviceManager == indi_device->deviceManager && host->isConnected == false)
	        {
	    		foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly, 1))
151
                                item->setIcon(HOST_STATUS_COLUMN, ui->connected);
152 153 154 155
	
             		host->isConnected = true;
			updateClientTab();
			updateMenuActions();
156
			ksw->indiMenu()->show();
157 158 159 160 161 162 163 164 165 166 167 168
             		return;
        	}
    	}
    }
    else
      {
	foreach (IDevice *device, devices)
    	{
		if (device->unique_label == indi_device->label)
		{
			foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly |  Qt::MatchRecursive))
			{
169 170
                                item->setIcon(LOCAL_STATUS_COLUMN, ui->runningPix);
                                item->setText(LOCAL_PORT_COLUMN, QString::number(indi_device->deviceManager->port));
171 172 173 174
			}
			
			updateLocalTab();
			updateMenuActions();
175
			ksw->indiMenu()->show();
176 177 178 179 180
			return;
		}
	}
      }
  
Jasem Mutlaq's avatar
Jasem Mutlaq committed
181
}
182 183
  
void INDIDriver::disableDevice(INDI_D *indi_device)
184
{
185 186 187 188 189 190 191 192 193 194
    if (indi_device == NULL)
	return;

   if (indi_device->deviceManager->mode == DeviceManager::M_CLIENT)
   {
   	foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
    	{
	        if (host->deviceManager == indi_device->deviceManager && host->isConnected == true)
	        {
	    		foreach (QTreeWidgetItem *item, ui->clientTreeWidget->findItems(host->name, Qt::MatchExactly,1))
195
                                item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
196 197 198 199 200 201 202 203
	
			host->deviceManager = NULL;
		        host->isConnected = false;
            
          		updateClientTab();
             		return;
        	}
    	}
204
    }
205 206 207 208 209 210 211 212
    else
      {
	foreach (IDevice *device, devices)
    	{
		if (device->unique_label == indi_device->label)
		{
			foreach (QTreeWidgetItem *item, ui->localTreeWidget->findItems(device->tree_label, Qt::MatchExactly |  Qt::MatchRecursive))
			{
213 214
                                item->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
                                item->setIcon(LOCAL_MODE_COLUMN, QIcon());
215
                                item->setText(LOCAL_PORT_COLUMN, device->port);
216 217 218 219 220 221 222 223 224 225
			}
			
			device->clear();
			updateLocalTab();
			
			return;
		}
	}
      }
  }
Jasem Mutlaq's avatar
Jasem Mutlaq committed
226
  */
227
void INDIDriver::activateRunService()
228 229 230 231
  {
    processLocalTree(IDevice::DEV_START);
  }
  
232
void INDIDriver::activateStopService()
233 234 235 236
  {
    processLocalTree(IDevice::DEV_TERMINATE);
  }
  
237
void INDIDriver::activateHostConnection()
238 239 240 241
  {
    processRemoteTree(IDevice::DEV_START);
  }
  
242
void INDIDriver::activateHostDisconnection()
243 244 245 246 247 248 249 250 251 252 253 254 255
  {
    processRemoteTree(IDevice::DEV_TERMINATE);
  }
  
void INDIDriver::updateLocalTab()
  {
  
      if (ui->localTreeWidget->currentItem() == NULL)
          return;
  
    foreach (IDevice *device, devices)
      {
  
256
        if (ui->localTreeWidget->currentItem()->text(LOCAL_NAME_COLUMN) == device->tree_label)
257 258 259 260 261 262 263 264 265 266
          {
            ui->runServiceB->setEnabled(device->state == IDevice::DEV_TERMINATE);
            ui->stopServiceB->setEnabled(device->state == IDevice::DEV_START);
  
              ui->serverLogText->clear();
            ui->serverLogText->append(device->getServerBuffer());
  
              return;
          }
	}
267 268
}

269
void INDIDriver::updateClientTab()
270
{
271
     if (ui->clientTreeWidget->currentItem() == NULL)
272 273 274 275
        return;

    foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
    {
276
        if (ui->clientTreeWidget->currentItem()->text(HOST_NAME_COLUMN) == host->name && ui->clientTreeWidget->currentItem()->text(HOST_PORT_COLUMN) == host->portnumber)
277 278 279 280 281
        {
            ui->connectHostB->setEnabled(!host->isConnected);
            ui->disconnectHostB->setEnabled(host->isConnected);
            break;
        }
282
    }
283

284 285
}

286
void INDIDriver::processLocalTree(IDevice::DeviceStatus dev_request)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
287
{
288 289
   QList<IDevice *> processed_devices;
   DeviceManager *deviceManager=NULL;
290

291 292 293
   int port=0;
   bool portOK=false;

294 295 296 297 298
   foreach(QTreeWidgetItem *item, ui->localTreeWidget->selectedItems())
   {
      foreach (IDevice *device, devices)
      {
		//device->state = (dev_request == IDevice::DEV_TERMINATE) ? IDevice::DEV_START : IDevice::DEV_TERMINATE;
299 300
                if (item->text(LOCAL_NAME_COLUMN) == device->tree_label && device->state != dev_request)
                {
301
			processed_devices.append(device);
302

303
                        // N.B. If multiple devices are selected to run under one device manager
304 305
                        // then we select the port for the first device that has a valid port
                        // entry, the rest are ignored.
306
                        if (port == 0 && item->text(LOCAL_PORT_COLUMN).isEmpty() == false)
307 308 309 310 311
                        {
                            port = item->text(LOCAL_PORT_COLUMN).toInt(&portOK);
                            // If we encounter conversion error, we abort
                            if (portOK == false)
                            {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
312
                                KMessageBox::error(0, xi18n("Invalid port entry: %1", item->text(LOCAL_PORT_COLUMN)));
313 314 315 316 317
                                return;
                            }
                        }
                }
       }
318
   }
319

320
   if (processed_devices.empty()) return;
321

322
   // Select random port within range is none specified.
323
    port = getINDIPort(port);
324

325 326
   if (dev_request == IDevice::DEV_START)
   {
327

328
        if (port <= 0)
329
        {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
330
	        KMessageBox::error(0, xi18n("Cannot start INDI server: port error."));
331 332 333
        	return;
    	}
   
334 335
	//deviceManager = ksw->indiMenu()->startDeviceManager(processed_devices, ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER, ((uint) port));
        deviceManager = ksw->indiMenu()->initDeviceManager("localhost", ((uint) port), ui->localR->isChecked() ? DeviceManager::M_LOCAL : DeviceManager::M_SERVER);
336 337 338 339 340 341 342 343 344 345 346 347

	if (deviceManager == NULL)
	{
		kWarning() << "Warning: device manager has not been established properly";
		return;
	}

	deviceManager->appendManagedDevices(processed_devices);
	deviceManager->startServer();
	connect(deviceManager, SIGNAL(newServerInput()), this, SLOT(updateLocalTab()));
   }
   else
348
	ksw->indiMenu()->stopDeviceManager(processed_devices);
349

Jasem Mutlaq's avatar
Jasem Mutlaq committed
350 351
}

352
void INDIDriver::processRemoteTree(IDevice::DeviceStatus dev_request)
353
{
354 355 356 357
      DeviceManager* deviceManager=NULL;
      QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();
      if (!currentItem) return;
     bool toConnect = (dev_request == IDevice::DEV_START);
358 359

    foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
360
     {
361
        if (currentItem->text(HOST_NAME_COLUMN) == host->name && currentItem->text(HOST_PORT_COLUMN) == host->portnumber)
362 363 364 365 366 367 368 369
        {
            // Nothing changed, return
            if (host->isConnected == toConnect)
                return;

            // connect to host
            if (toConnect)
            {
370
		deviceManager = ksw->indiMenu()->initDeviceManager(host->hostname, host->portnumber.toUInt(), DeviceManager::M_CLIENT);
371 372 373 374 375 376 377 378 379 380 381 382
  
	        if (deviceManager == NULL)
		{
			// msgbox after freeze
			kWarning() << "Warning: device manager has not been established properly";
		        return;
		}
  
		host->deviceManager = deviceManager;
		deviceManager->connectToServer();
	     }
             else
383
             	ksw->indiMenu()->removeDeviceManager(host->deviceManager);
384 385 386
  
		return;
	  }
Jasem Mutlaq's avatar
Jasem Mutlaq committed
387
    }
388 389
}

390 391 392
void INDIDriver::newTelescopeDiscovered()
{

393 394
    emit newTelescope();

395 396 397 398 399
}

void INDIDriver::newCCDDiscovered()
{

400
    emit newCCD();
401 402
}

403 404 405 406
void INDIDriver::resizeDeviceColumn()
{
  ui->localTreeWidget->resizeColumnToContents(0);
}
407

408 409
void INDIDriver::updateMenuActions()
{
410 411
    // We iterate over devices, we enable INDI Control Panel if we have any active device
    // We enable capture image sequence if we have any imaging device
412

413
    QAction *tmpAction = NULL;
414
    INDIMenu *devMenu = ksw->indiMenu();
415 416 417
    bool activeDevice = false;
    bool activeImaging = false;
    INDI_P *imgProp = NULL;
418

419 420
    if (devMenu == NULL)
        return;
421

422
    if (devMenu->managers.count() > 0)
423
        activeDevice = true;
424

425
    foreach(DeviceManager *dev_managers, devMenu->managers)
426
    {
427
        foreach (INDI_D *device, dev_managers->indi_dev)
428
        {
429

Jasem Mutlaq's avatar
Jasem Mutlaq committed
430
            imgProp = device->findProp("CCD_EXPOSURE");
431 432 433 434 435 436 437
            if (imgProp && device->isOn())
            {
                activeImaging = true;
                break;
            }
        }
    }
438

439
    tmpAction = ksw->actionCollection()->action("capture_sequence");
440

441 442
    if (tmpAction != NULL)
        tmpAction->setEnabled(activeImaging);
443

444 445
    tmpAction = ksw->actionCollection()->action("indi_cpl");
    if (tmpAction != NULL)
446
       tmpAction->setEnabled(activeDevice);
447

448 449
}

450
bool INDIDriver::isDeviceRunning(const QString &deviceLabel)
451
{
452 453 454
    foreach(IDevice *dev, devices) {
        if (deviceLabel == dev->tree_label)
            return dev->state == IDevice::DEV_START;
455
    }
Jason Harris's avatar
Jason Harris committed
456
    return false;
457 458
}

459
int INDIDriver::getINDIPort(int customPort)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
460 461
{

462
    int lastPort  = Options::serverPortEnd().toInt();;
463
    bool success = false;
464 465 466
    currentPort++;

    // recycle
467
    if (currentPort > lastPort) currentPort = Options::serverPortStart().toInt();
468

469
    QTcpServer temp_server;
470 471 472 473 474 475 476 477 478 479 480 481 482

    if (customPort != 0)
    {
        success = temp_server.listen(QHostAddress::LocalHost, customPort);
        if (success)
        {
            temp_server.close();
            return customPort;
        }
        else
            return -1;
    }

483 484
    for(; currentPort <= lastPort; currentPort++)
    {
485
            success = temp_server.listen(QHostAddress::LocalHost, currentPort);
486
        if(success)
487
        {
488
            temp_server.close();
489 490 491 492
            return currentPort;
        }
    }
    return -1;
493
}
Jasem Mutlaq's avatar
Jasem Mutlaq committed
494

495
bool INDIDriver::readXMLDrivers()
Jasem Mutlaq's avatar
Jasem Mutlaq committed
496
{
497 498
    QDir indiDir;
    QString driverName;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
499

Robert Lancaster's avatar
Robert Lancaster committed
500 501
    QString driversDir=Options::indiDriversDir();
     #ifdef Q_OS_OSX
Robert Lancaster's avatar
Robert Lancaster committed
502
    if(Options::indiDriversAreInternal())
Robert Lancaster's avatar
Robert Lancaster committed
503 504 505 506
        driversDir=QCoreApplication::applicationDirPath()+"/indi";
    #endif

    if (indiDir.cd(driversDir) == false)
507
    {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
508
        KMessageBox::error(0, xi18n("Unable to find INDI Drivers directory: %1\nPlease make sure to set the correct path in KStars configuration", Options::indiDriversDir()));
509 510
          return false;
     }
Jasem Mutlaq's avatar
Jasem Mutlaq committed
511

512
    indiDir.setNameFilters(QStringList("*.xml"));
513 514 515 516 517
    indiDir.setFilter(QDir::Files | QDir::NoSymLinks);
    QFileInfoList list = indiDir.entryInfoList();

    foreach (QFileInfo fileInfo, list)
    {
518 519 520 521
        // libindi 0.7.1: Skip skeleton files
        if (fileInfo.fileName().endsWith("_sk.xml"))
            continue;

522 523 524
	if (fileInfo.fileName() == "drivers.xml")
	{ 
	    // Let first attempt to load the local version of drivers.xml
525
	    driverName = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "drivers.xml";
526 527 528 529 530 531 532 533 534

	    // If found, we continue, otherwise, we load the system file
	    if (driverName.isEmpty() == false && QFile(driverName).exists())
	    {
	        processXMLDriver(driverName);
		continue;
	    }
	}

Robert Lancaster's avatar
Robert Lancaster committed
535
            driverName = QString("%1/%2").arg(driversDir).arg(fileInfo.fileName());
536 537
	        processXMLDriver(driverName);

538
    }
Jasem Mutlaq's avatar
Jasem Mutlaq committed
539

540 541 542 543 544 545 546 547 548
return true;

}

void INDIDriver::processXMLDriver(QString & driverName)
{
    QFile file(driverName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
549
        KMessageBox::error(0, xi18n("Failed to open INDI Driver file: %1", driverName));
550 551 552
         return;
    }

553
    char errmsg[ERRMSG_SIZE];
554 555 556
    char c;
    LilXML *xmlParser = newLilXML();
    XMLEle *root = NULL;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
557

558 559 560 561
    if (driverName.endsWith("drivers.xml"))
        xmlSource = IDevice::PRIMARY_XML;
    else
        xmlSource = IDevice::THIRD_PARTY_XML;
562

563
    while ( file.getChar(&c))
Jasem Mutlaq's avatar
Jasem Mutlaq committed
564
    {
565
        root = readXMLEle(xmlParser, c, errmsg);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
566

567 568 569 570 571 572 573 574 575
        if (root)
        {
            if (!buildDeviceGroup(root, errmsg))
                prXMLEle(stderr, root, 0);

            delXMLEle(root);
        }
        else if (errmsg[0])
        {
576
            kDebug() << QString(errmsg) << endl;
577 578
            delLilXML(xmlParser);
            return;
579
        }
Jasem Mutlaq's avatar
Jasem Mutlaq committed
580 581
    }

582
    delLilXML(xmlParser);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
583

584 585
}

Jasem Mutlaq's avatar
Jasem Mutlaq committed
586 587 588
bool INDIDriver::buildDeviceGroup(XMLEle *root, char errmsg[])
{

589 590 591
    XMLAtt *ap;
    XMLEle *ep;
    QString groupName;
592
    QTreeWidgetItem *group;
593
    int groupType = KSTARS_TELESCOPE;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
594

595 596 597
    // avoid overflow
    if (strlen(tagXMLEle(root)) > 1024)
        return false;
598

599 600
    // Get device grouping name
    ap = findXMLAtt(root, "group");
601

602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
    if (!ap)
    {
        snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute", tagXMLEle(root));
        return false;
    }

    groupName = valuXMLAtt(ap);

    if (groupName.indexOf("Telescopes") != -1)
        groupType = KSTARS_TELESCOPE;
    else if (groupName.indexOf("CCDs") != -1)
        groupType = KSTARS_CCD;
    else if (groupName.indexOf("Filter") != -1)
        groupType = KSTARS_FILTER;
    else if (groupName.indexOf("Video") != -1)
        groupType = KSTARS_VIDEO;
    else if (groupName.indexOf("Focusers") != -1)
        groupType = KSTARS_FOCUSER;
    else if (groupName.indexOf("Domes") != -1)
        groupType = KSTARS_DOME;
622 623
    else if (groupName.indexOf("Receivers") != -1)
        groupType = KSTARS_RECEIVERS;
624 625
    else if (groupName.indexOf("GPS") != -1)
        groupType = KSTARS_GPS;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
626

627
#ifndef HAVE_CFITSIO_H
628 629 630
    // We do not create these groups if we don't have CFITSIO support
    if (groupType == KSTARS_CCD || groupType == KSTARS_VIDEO)
        return true;
631
#endif
Jasem Mutlaq's avatar
Jasem Mutlaq committed
632

633 634 635 636 637 638 639
    // Find if the group already exists
    QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems(groupName, Qt::MatchExactly);
    if (!treeList.isEmpty())
        group = treeList[0];
    else
        group = new QTreeWidgetItem(ui->localTreeWidget, lastGroup);

640 641
    group->setText(0, groupName);
    lastGroup = group;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
642

643 644 645
    for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0))
        if (!buildDriverElement(ep, group, groupType, errmsg))
            return false;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
646

647
    return true;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
648 649
}

650

651
bool INDIDriver::buildDriverElement(XMLEle *root, QTreeWidgetItem *DGroup, int groupType, char errmsg[])
Jasem Mutlaq's avatar
Jasem Mutlaq committed
652
{
653 654 655 656 657 658
    XMLAtt *ap;
    XMLEle *el;
    IDevice *dv;
    QString label;
    QString driver;
    QString version;
659
    QString name;
660
    QString port;
661 662
    double focal_length (-1), aperture (-1);

663

664 665 666 667 668 669
        ap = findXMLAtt(root, "label");
        if (!ap)
        {
            snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute", tagXMLEle(root));
            return false;
        }
670

671
        label = valuXMLAtt(ap);
672

673 674 675 676 677
        // Search for optional port attribute
        ap = findXMLAtt(root, "port");
        if (ap)
            port = valuXMLAtt(ap);

678 679 680 681
        // Let's look for telescope-specific attributes: focal length and aperture
        ap = findXMLAtt(root, "focal_length");
        if (ap)
            focal_length = QString(valuXMLAtt(ap)).toDouble();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
682

683 684 685
        ap = findXMLAtt(root, "aperture");
        if (ap)
            aperture = QString(valuXMLAtt(ap)).toDouble();
Jasem Mutlaq's avatar
Jasem Mutlaq committed
686

687
        el = findXMLEle(root, "driver");
Jasem Mutlaq's avatar
Jasem Mutlaq committed
688

689 690
        if (!el)
            return false;
691

692
        driver = pcdataXMLEle(el);
693

694 695 696 697 698 699
        ap = findXMLAtt(el, "name");
        if (!ap)
        {
            snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a name attribute", tagXMLEle(el));
            return false;
        }
700

701
        name = valuXMLAtt(ap);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
702

703
        el = findXMLEle(root, "version");
Jasem Mutlaq's avatar
Jasem Mutlaq committed
704

705 706 707 708 709 710
        if (!el)
            return false;

        version = pcdataXMLEle(el);

        QTreeWidgetItem *device = new QTreeWidgetItem(DGroup, lastDevice);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
711

712
    device->setText(LOCAL_NAME_COLUMN, label);
713
    device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
714 715
    device->setText(LOCAL_VERSION_COLUMN, version);
    device->setText(LOCAL_PORT_COLUMN, port);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
716

717
    lastDevice = device;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
718

719 720
    if ((xmlSource == IDevice::PRIMARY_XML) && driversList.contains(driver) == false)
        driversList.insert(driver, name);
721

722
    dv = new IDevice(name, label, driver, version);
723
    dv->type = groupType;
724
    dv->xmlSource = xmlSource;
725
    //connect(dv, SIGNAL(newServerInput()), this, SLOT(updateLocalTab()));
726 727 728 729
    if (focal_length > 0)
        dv->focal_length = focal_length;
    if (aperture > 0)
        dv->aperture = aperture;
730
    dv->port = port;
731

732
    devices.append(dv);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
733

734 735
    // SLOTS/SIGNAL, pop menu, indi server logic
    return true;
Jasem Mutlaq's avatar
Jasem Mutlaq committed
736 737
}

738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
void INDIDriver::updateCustomDrivers()
{
    QString label;
    QString driver;
    QString version;
    QString name;
    IDevice *dv;
    QTreeWidgetItem *group, *widgetDev;
    double focal_length (-1), aperture (-1);

    // Find if the group already exists
    QList<QTreeWidgetItem *> treeList = ui->localTreeWidget->findItems("Telescopes", Qt::MatchExactly);
    if (!treeList.isEmpty())
        group = treeList[0];
    else return;


        // Find custom telescope to ADD
    foreach(OAL::Scope *s, *(KStarsData::Instance()->logObject()->scopeList()))
        {
            label = s->vendor() + " " + s->model();

Jasem Mutlaq's avatar
Jasem Mutlaq committed
760
            if (s->driver() == xi18n("None") || findDeviceByLabel(label))
761 762
                continue;

763 764
            QHash<QString, QString>::const_iterator i = driversList.constFind(s->driver());
            if (i != driversList.constEnd() && i.key() == s->driver())
765 766 767 768 769 770 771 772 773
                name  = i.value();
            else
                return;
            focal_length = s->focalLength();
            aperture = s->aperture();
            driver = s->driver();
            version = QString("1.0");

            QTreeWidgetItem *device = new QTreeWidgetItem(group, lastDevice);
774 775 776
            device->setText(LOCAL_NAME_COLUMN, QString(label));
            device->setIcon(LOCAL_STATUS_COLUMN, ui->stopPix);
            device->setText(LOCAL_VERSION_COLUMN, QString(version));
777 778 779 780

            lastDevice = device;

            dv = new IDevice(name, label, driver, version);
781
            dv->type = KSTARS_TELESCOPE;
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
            dv->xmlSource = IDevice::EM_XML;
            dv->focal_length = focal_length;
            dv->aperture = aperture;
            dv->id = s->id();
            devices.append(dv);
        }

        // Find custom telescope to REMOVE
        foreach(IDevice *dev, devices)
        {
            // If it's from primary xml file or it is in a running state, continue.
            if (dev->xmlSource != IDevice::EM_XML || dev->state == IDevice::DEV_START)
                continue;

            // See if log object has the device, if not delete it
            if (KStarsData::Instance()->logObject()->findScopeById(dev->id))
                continue;

            // Find if the group already exists
            QList<QTreeWidgetItem *> devList = ui->localTreeWidget->findItems(dev->tree_label, Qt::MatchExactly  | Qt::MatchRecursive);
            if (!devList.isEmpty())
            {
                widgetDev = devList[0];
                group->removeChild(widgetDev);
            }
            else return;

            devices.removeOne(dev);
            delete (dev);
        }

}

815 816
void INDIDriver::addINDIHost()
{
817 818 819
    QDialog hostConfDialog;
    Ui::INDIHostConf hostConf;
    hostConf.setupUi(&hostConfDialog);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
820
    hostConfDialog.setWindowTitle(xi18n("Add Host"));
821 822 823
    bool portOk = false;

    if (hostConfDialog.exec() == QDialog::Accepted)
824
    {
825
        INDIHostsInfo *hostItem = new INDIHostsInfo;
826 827 828 829 830
        hostItem->name        	= hostConf.nameIN->text();
        hostItem->hostname    	= hostConf.hostname->text();
        hostItem->portnumber  	= hostConf.portnumber->text();
        hostItem->isConnected 	= false;
        hostItem->deviceManager	= NULL;
831

832
        hostItem->portnumber.toInt(&portOk);
833

834 835
        if (portOk == false)
        {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
836
            KMessageBox::error(0, xi18n("Error: the port number is invalid."));
837 838 839
            delete hostItem;
            return;
        }
840

841 842 843 844 845
        //search for duplicates
        //for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++)
        foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
        if (hostItem->name   == host->name &&  hostItem->portnumber == host->portnumber)
        {
Jasem Mutlaq's avatar
Jasem Mutlaq committed
846
            KMessageBox::error(0, xi18n("Host: %1 Port: %2 already exists.", hostItem->name, hostItem->portnumber));
847 848 849
            delete hostItem;
            return;
        }
850

851 852 853
        ksw->data()->INDIHostsList.append(hostItem);

        QTreeWidgetItem *item = new QTreeWidgetItem(ui->clientTreeWidget);
854 855 856
        item->setIcon(HOST_STATUS_COLUMN, ui->disconnected);
        item->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
        item->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
857 858

    }
859 860 861 862 863 864 865 866 867

    saveHosts();
}



void INDIDriver::modifyINDIHost()
{

868 869 870
    QDialog hostConfDialog;
    Ui::INDIHostConf hostConf;
    hostConf.setupUi(&hostConfDialog);
Jasem Mutlaq's avatar
Jasem Mutlaq committed
871
    hostConfDialog.setWindowTitle(xi18n("Modify Host"));
872 873 874 875 876 877 878 879

    QTreeWidgetItem *currentItem = ui->clientTreeWidget->currentItem();

    if (currentItem == NULL)
        return;

    foreach (INDIHostsInfo * host, ksw->data()->INDIHostsList)
    {
880
        if (currentItem->text(HOST_NAME_COLUMN) == host->name && currentItem->text(HOST_PORT_COLUMN) == host->portnumber)
881 882 883 884 885 886 887 888 889 890 891 892
        {
            hostConf.nameIN->setText(host->name);
            hostConf.hostname->setText(host->hostname);
            hostConf.portnumber->setText(host->portnumber);

            if (hostConfDialog.exec() == QDialog::Accepted)
            {
                //INDIHostsInfo *hostItem = new INDIHostsInfo;
                host->name       = hostConf.nameIN->text();
                host->hostname   = hostConf.hostname->text();
                host->portnumber = hostConf.portnumber->text();

893 894
                currentItem->setText(HOST_NAME_COLUMN, hostConf.nameIN->text());
                currentItem->setText(HOST_PORT_COLUMN, hostConf.portnumber->text());
895 896 897 898 899 900 901 902

                //ksw->data()->INDIHostsList.replace(i, hostItem);

                saveHosts();
                return;
            }
        }
    }