1 /***************************************************************************
4 begin : Wed May 7th 2003
5 copyright : (C) 2001 by Jasem Mutlaq
6 email : mutlaqja@ikarustech.com
7 ***************************************************************************/
8 /***************************************************************************
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
15 ***************************************************************************/
17 #include "indidriver.h"
19 #include "indihostconf.h"
20 #include "devicemanager.h"
21 #include "indidevice.h"
22 #include "indi/indicom.h"
25 #include "kstarsdata.h"
29 #include <qvaluelist.h>
31 #include <qradiobutton.h>
32 #include <qtextedit.h>
34 #include <kiconloader.h>
35 #include <klistview.h>
36 #include <kpopupmenu.h>
38 #include <kmessagebox.h>
39 #include <kpushbutton.h>
40 #include <klineedit.h>
41 #include <kstandarddirs.h>
48 * The dialog will by default be modeless, unless you set 'modal' to
49 * TRUE to construct a modal dialog.
51 INDIDriver::INDIDriver(QWidget
*parent
) : devManager( parent
)
59 ksw
= (KStars
*) parent
;
61 //FormLayout = makeVBoxMainWidget();
63 localListView
->setSorting(-1);
64 clientListView
->setSorting(-1);
66 KIconLoader
*icons
= KGlobal::iconLoader();
67 runningPix
= icons
->loadIcon( "exec", KIcon::Small
);
68 stopPix
= icons
->loadIcon( "button_cancel", KIcon::Small
);
69 localMode
= icons
->loadIcon( "network_local", KIcon::Small
);
70 serverMode
= icons
->loadIcon( "network", KIcon::Small
);
72 LocalpopMenu
= new KPopupMenu(localListView
);
73 LocalpopMenu
->insertItem( runningPix
, i18n("Run Service") , 0);
74 LocalpopMenu
->insertItem( stopPix
, i18n("Stop Service"), 1);
76 localListView
->setRootIsDecorated(true);
78 connected
= icons
->loadIcon( "connect_established", KIcon::Small
);
79 disconnected
= icons
->loadIcon( "connect_no", KIcon::Small
);
80 establishConnection
= icons
->loadIcon( "connect_creating", KIcon::Small
);
82 ClientpopMenu
= new KPopupMenu(clientListView
);
83 ClientpopMenu
->insertItem( establishConnection
, i18n("Connect") , 0);
84 ClientpopMenu
->insertItem( disconnected
, i18n("Disconnect"), 1);
87 for (uint i
= 0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
89 QListViewItem
*item
= new QListViewItem(clientListView
, lastGroup
);
91 item
->setPixmap(0, disconnected
);
92 item
->setText(1, ksw
->data()->INDIHostsList
.at(i
)->name
);
93 item
->setText(2, ksw
->data()->INDIHostsList
.at(i
)->portnumber
);
99 QObject::connect(addB
, SIGNAL(clicked()), this, SLOT(addINDIHost()));
100 QObject::connect(modifyB
, SIGNAL(clicked()), this, SLOT(modifyINDIHost()));
101 QObject::connect(removeB
, SIGNAL(clicked()), this, SLOT(removeINDIHost()));
103 QObject::connect(clientListView
, SIGNAL(rightButtonPressed ( QListViewItem
*, const QPoint
&, int )), this, SLOT(ClientprocessRightButton( QListViewItem
*, const QPoint
&, int )));
105 QObject::connect(ClientpopMenu
, SIGNAL(activated(int)), this, SLOT(processHostStatus(int)));
107 QObject::connect(localListView
, SIGNAL(rightButtonPressed ( QListViewItem
*, const QPoint
&, int )), this, SLOT(LocalprocessRightButton( QListViewItem
*, const QPoint
&, int )));
109 QObject::connect(LocalpopMenu
, SIGNAL(activated(int)), this, SLOT(processDeviceStatus(int)));
111 QObject::connect(ksw
->getINDIMenu(), SIGNAL(driverDisconnected(int)), this, SLOT(shutdownHost(int)));
113 QObject::connect(connectHostB
, SIGNAL(clicked()), this, SLOT(activateHostConnection()));
114 QObject::connect(disconnectHostB
, SIGNAL(clicked()), this, SLOT(activateHostDisconnection()));
116 QObject::connect(runServiceB
, SIGNAL(clicked()), this, SLOT(activateRunService()));
117 QObject::connect(stopServiceB
, SIGNAL(clicked()), this, SLOT(activateStopService()));
119 QObject::connect(localListView
, SIGNAL(selectionChanged()), this, SLOT(updateLocalButtons()));
120 QObject::connect(clientListView
, SIGNAL(selectionChanged()), this, SLOT(updateClientButtons()));
127 void INDIDriver::shutdownHost(int mgrID
)
129 QListViewItem
*affectedItem
;
131 for (uint i
=0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
133 if (ksw
->data()->INDIHostsList
.at(i
)->mgrID
== mgrID
)
135 affectedItem
= clientListView
->itemAtIndex(i
);
136 ksw
->data()->INDIHostsList
.at(i
)->mgrID
= -1;
137 ksw
->data()->INDIHostsList
.at(i
)->isConnected
= false;
138 affectedItem
->setPixmap(0, disconnected
);
139 connectHostB
->setEnabled(true);
140 disconnectHostB
->setEnabled(false);
145 for (uint i
=0; i
< devices
.size(); i
++)
147 if (devices
[i
]->mgrID
== mgrID
)
149 affectedItem
= localListView
->findItem(devices
[i
]->label
, 0);
150 if (!affectedItem
) return;
151 affectedItem
->setPixmap(1, stopPix
);
152 affectedItem
->setPixmap(2, NULL
);
153 affectedItem
->setText(4, QString(""));
154 runServiceB
->setEnabled(true);
155 stopServiceB
->setEnabled(false);
156 devices
[i
]->managed
= false;
157 devices
[i
]->restart();
166 void INDIDriver::ClientprocessRightButton( QListViewItem
*item
, const QPoint
&p
, int column
)
171 if (item
&& item
->childCount() == 0)
172 ClientpopMenu
->popup(p
);
175 void INDIDriver::LocalprocessRightButton( QListViewItem
*item
, const QPoint
&p
, int column
)
180 if (item
&& item
->childCount() == 0)
181 LocalpopMenu
->popup(p
);
184 void INDIDriver::activateRunService()
186 processDeviceStatus(0);
189 void INDIDriver::activateStopService()
191 processDeviceStatus(1);
194 void INDIDriver::activateHostConnection()
196 processHostStatus(0);
199 void INDIDriver::activateHostDisconnection()
201 processHostStatus(1);
204 void INDIDriver::updateLocalButtons()
207 if (localListView
->selectedItem() == NULL
)
210 for (uint i
=0; i
< devices
.size(); i
++)
211 if (localListView
->selectedItem()->text(0) == devices
[i
]->label
)
213 runServiceB
->setEnabled(devices
[i
]->state
== 0);
214 stopServiceB
->setEnabled(devices
[i
]->state
== 1);
216 serverLogText
->clear();
218 for ( QStringList::Iterator it
= devices
[i
]->serverBuffer
.begin(); it
!= devices
[i
]->serverBuffer
.end(); ++it
)
219 serverLogText
->insert(*it
);
226 void INDIDriver::updateClientButtons()
228 INDIHostsInfo
*hostInfo
;
229 if (clientListView
->currentItem() == NULL
)
233 for (uint i
=0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
235 hostInfo
= ksw
->data()->INDIHostsList
.at(i
);
236 if (clientListView
->currentItem()->text(1) == hostInfo
->name
&& clientListView
->currentItem()->text(2) == hostInfo
->portnumber
)
238 connectHostB
->setEnabled(!hostInfo
->isConnected
);
239 disconnectHostB
->setEnabled(hostInfo
->isConnected
);
247 void INDIDriver::processDeviceStatus(int id
)
249 if (localListView
->selectedItem() == NULL
)
252 for (uint i
=0; i
< devices
.size(); i
++)
253 if (localListView
->selectedItem()->text(0) == devices
[i
]->label
)
255 devices
[i
]->state
= (id
== 0) ? 1 : 0;
256 if (devices
[i
]->state
)
259 ksw
->getINDIMenu()->setCustomLabel(devices
[i
]->label
);
260 devices
[i
]->label
= ksw
->getINDIMenu()->currentLabel
;
262 devices
[i
]->serverBuffer
.clear();
264 if (!runDevice(devices
[i
]))
266 devices
[i
]->restart();
270 if (devices
[i
]->mode
== IDevice::M_LOCAL
)
272 //Allow time for the INDI server to listen
275 if (!ksw
->getINDIMenu()->processServer())
277 devices
[i
]->restart();
282 localListView
->selectedItem()->setPixmap(1, runningPix
);
283 localListView
->selectedItem()->setText(4, QString("%1").arg(devices
[i
]->indiPort
));
284 runServiceB
->setEnabled(false);
285 stopServiceB
->setEnabled(true);
290 if (devices
[i
]->mode
== IDevice::M_LOCAL
)
291 ksw
->getINDIMenu()->processServer();
293 localListView
->selectedItem()->setPixmap(1, stopPix
);
294 localListView
->selectedItem()->setPixmap(2, NULL
);
295 localListView
->selectedItem()->setText(4, QString(""));
296 runServiceB
->setEnabled(true);
297 stopServiceB
->setEnabled(false);
298 devices
[i
]->restart();
304 void INDIDriver::processHostStatus(int id
)
307 bool toConnect
= (id
== 0);
308 QListViewItem
*currentItem
= clientListView
->selectedItem();
309 if (!currentItem
) return;
310 INDIHostsInfo
*hostInfo
;
312 for (uint i
=0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
314 hostInfo
= ksw
->data()->INDIHostsList
.at(i
);
315 if (currentItem
->text(1) == hostInfo
->name
&& currentItem
->text(2) == hostInfo
->portnumber
)
317 // Nothing changed, return
318 if (hostInfo
->isConnected
== toConnect
)
324 // if connection successful
325 if ( (mgrID
= ksw
->getINDIMenu()->processClient(hostInfo
->hostname
, hostInfo
->portnumber
)) >= 0)
327 currentItem
->setPixmap(0, connected
);
328 hostInfo
->isConnected
= true;
329 hostInfo
->mgrID
= mgrID
;
330 connectHostB
->setEnabled(false);
331 disconnectHostB
->setEnabled(true);
336 ksw
->getINDIMenu()->removeDeviceMgr(hostInfo
->mgrID
);
337 hostInfo
->mgrID
= mgrID
= -1;
338 hostInfo
->isConnected
= false;
339 currentItem
->setPixmap(0, disconnected
);
340 connectHostB
->setEnabled(true);
341 disconnectHostB
->setEnabled(false);
351 void INDIDriver::updateMenuActions()
353 // We iterate over devices, we enable INDI Control Panel if we have any active device
354 // We enable capture image sequence if we have any imaging device
357 INDIMenu
*devMenu
= ksw
->getINDIMenu();
358 bool activeDevice
= false;
359 bool activeImaging
= false;
360 INDI_P
*imgProp
= NULL
;
365 if (devMenu
->mgr
.count() > 0)
368 for (uint i
=0; i
< devMenu
->mgr
.count(); i
++)
370 for (uint j
=0; j
< devMenu
->mgr
.at(i
)->indi_dev
.count(); j
++)
372 imgProp
= devMenu
->mgr
.at(i
)->indi_dev
.at(j
)->findProp("EXPOSE_DURATION");
373 if (imgProp
&& devMenu
->mgr
.at(i
)->indi_dev
.at(j
)->isOn())
375 activeImaging
= true;
381 tmpAction
= ksw
->actionCollection()->action("indi_control_panel");
383 kdDebug() << "Warning: indi_control_panel action not found" << endl
;
385 tmpAction
->setEnabled(activeDevice
);
387 tmpAction
= ksw
->actionCollection()->action("capture_sequence");
389 kdDebug() << "Warning: capture_sequence action not found" << endl
;
391 tmpAction
->setEnabled(activeImaging
);
395 bool INDIDriver::runDevice(IDevice
*dev
)
397 dev
->indiPort
= getINDIPort();
399 if (dev
->indiPort
< 0)
401 KMessageBox::error(0, i18n("Cannot start INDI server: port error."));
405 dev
->proc
= new KProcess
;
407 *dev
->proc
<< "indiserver";
408 *dev
->proc
<< "-v" << "-r" << "0" << "-p" << QString("%1").arg(dev
->indiPort
) << dev
->exec
;
411 dev
->mode
= localR
->isChecked() ? IDevice::M_LOCAL
: IDevice::M_SERVER
;
413 if (dev
->mode
== IDevice::M_LOCAL
)
414 localListView
->selectedItem()->setPixmap(2, localMode
);
416 localListView
->selectedItem()->setPixmap(2, serverMode
);
418 connect(dev
->proc
, SIGNAL(receivedStderr (KProcess
*, char *, int)), dev
, SLOT(processstd(KProcess
*, char*, int)));
420 dev
->proc
->start(KProcess::NotifyOnExit
, KProcess::Stderr
);
421 //dev->proc->start();
423 return (dev
->proc
->isRunning());
426 void INDIDriver::removeDevice(IDevice
*dev
)
429 for (unsigned int i
=0 ; i
< devices
.size(); i
++)
430 if (dev
->label
== devices
[i
]->label
)
431 devices
[i
]->restart();
434 void INDIDriver::removeDevice(QString deviceLabel
)
436 for (unsigned int i
=0 ; i
< devices
.size(); i
++)
437 if (deviceLabel
== devices
[i
]->label
)
438 devices
[i
]->restart();
442 bool INDIDriver::isDeviceRunning(QString deviceLabel
)
445 for (unsigned int i
=0 ; i
< devices
.size(); i
++)
446 if (deviceLabel
== devices
[i
]->label
)
448 if (!devices
[i
]->proc
)
450 else return (devices
[i
]->proc
->isRunning());
458 int INDIDriver::getINDIPort()
463 KExtendedSocket
ks(QString::null
, lastPort
, KExtendedSocket::passiveSocket
| KExtendedSocket::noResolve
);
465 for (uint i
=0 ; i
< 10; i
++)
470 ks
.setPort(lastPort
);
479 bool INDIDriver::readXMLDriver()
481 QString
indiFile("drivers.xml");
485 if ( !KSUtils::openDataFile( file
, indiFile
) )
487 KMessageBox::error(0, i18n("Unable to find device driver file 'drivers.xml'. Please locate the file and place it in one of the following locations:\n\n \t$(KDEDIR)/share/apps/kstars/%1 \n\t~/.kde/share/apps/kstars/%1"));
493 LilXML
*xmlParser
= newLilXML();
496 while ( (c
= (signed char) file
.getch()) != -1)
498 root
= readXMLEle(xmlParser
, c
, errmsg
);
502 if (!buildDeviceGroup(root
, errmsg
))
503 prXMLEle(stderr
, root
, 0);
509 kdDebug() << QString(errmsg
);
514 delLilXML(xmlParser
);
519 bool INDIDriver::buildDeviceGroup(XMLEle
*root
, char errmsg
[])
525 int groupType
= KSTARS_TELESCOPE
;
527 // Get device grouping name
528 ap
= findXMLAtt(root
, "group");
531 if (strlen(tagXMLEle(root
)) > 1024)
536 snprintf(errmsg
, ERRMSG_SIZE
, "Tag %.64s does not have a group attribute", tagXMLEle(root
));
540 groupName
= valuXMLAtt(ap
);
542 if (groupName
.find("Telescopes") != -1)
543 groupType
= KSTARS_TELESCOPE
;
544 else if (groupName
.find("CCDs") != -1)
545 groupType
= KSTARS_CCD
;
546 else if (groupName
.find("Filter") != -1)
547 groupType
= KSTARS_FILTER
;
548 else if (groupName
.find("Video") != -1)
549 groupType
= KSTARS_VIDEO
;
550 else if (groupName
.find("Focusers") != -1)
551 groupType
= KSTARS_FOCUSER
;
552 else if (groupName
.find("Domes") != -1)
553 groupType
= KSTARS_DOME
;
554 else if (groupName
.find("GPS") != -1)
555 groupType
= KSTARS_GPS
;
558 //KListViewItem *group = new KListViewItem(topItem, lastGroup);
559 QListViewItem
*group
= new QListViewItem(localListView
, lastGroup
);
560 group
->setText(0, groupName
);
562 //group->setOpen(true);
565 for (ep
= nextXMLEle(root
, 1) ; ep
!= NULL
; ep
= nextXMLEle(root
, 0))
566 /*for (int i = 0; i < root->nel; i++)*/
567 if (!buildDriverElement(ep
, group
, groupType
, errmsg
))
573 bool INDIDriver::buildDriverElement(XMLEle
*root
, QListViewItem
*DGroup
, int groupType
, char errmsg
[])
583 ap
= findXMLAtt(root
, "label");
586 snprintf(errmsg
, ERRMSG_SIZE
, "Tag %.64s does not have a label attribute", tagXMLEle(root
));
590 label
= new char[strlen(valuXMLAtt(ap
)) + 1];
591 strcpy(label
, valuXMLAtt(ap
));
593 ap
= findXMLAtt(root
, "driver");
596 snprintf(errmsg
, ERRMSG_SIZE
, "Tag %.64s does not have a driver attribute", tagXMLEle(root
));
600 driver
= new char[strlen(valuXMLAtt(ap
)) + 1];
601 strcpy(driver
, valuXMLAtt(ap
));
603 el
= findXMLEle(root
, "Exec");
608 exec
= new char[strlen(valuXMLAtt(ap
)) + 1];
609 strcpy(exec
, pcdataXMLEle(el
));
611 el
= findXMLEle(root
, "Version");
616 version
= new char[strlen(valuXMLAtt(ap
)) + 1];
617 strcpy(version
, pcdataXMLEle(el
));
619 QListViewItem
*device
= new QListViewItem(DGroup
, lastDevice
);
621 device
->setText(0, QString(label
));
622 device
->setPixmap(1, stopPix
);
623 device
->setText(3, QString(version
));
627 dv
= new IDevice(QString(label
), QString(driver
), QString(exec
), QString(version
));
628 dv
->deviceType
= groupType
;
629 connect(dv
, SIGNAL(newServerInput()), this, SLOT(updateLocalButtons()));
631 devices
.push_back(dv
);
638 // SLOTS/SIGNAL, pop menu, indi server logic
642 int INDIDriver::activeDriverCount()
646 for (uint i
=0; i
< devices
.size(); i
++)
647 if (devices
[i
]->state
&& devices
[i
]->mode
== IDevice::M_LOCAL
)
650 for (uint i
=0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
651 if (ksw
->data()->INDIHostsList
.at(i
)->isConnected
)
659 void INDIDriver::addINDIHost()
661 INDIHostConf
hostConf(this);
662 hostConf
.setCaption(i18n("Add Host"));
665 if (hostConf
.exec() == QDialog::Accepted
)
667 INDIHostsInfo
*hostItem
= new INDIHostsInfo
;
668 hostItem
->name
= hostConf
.nameIN
->text();
669 hostItem
->hostname
= hostConf
.hostname
->text();
670 hostItem
->portnumber
= hostConf
.portnumber
->text();
671 hostItem
->isConnected
= false;
672 hostItem
->mgrID
= -1;
674 hostItem
->portnumber
.toInt(&portOk
);
678 KMessageBox::error(0, i18n("Error: the port number is invalid."));
682 //search for duplicates
683 for (uint i
=0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
684 if (hostItem
->name
== ksw
->data()->INDIHostsList
.at(i
)->name
&&
685 hostItem
->portnumber
== ksw
->data()->INDIHostsList
.at(i
)->portnumber
)
687 KMessageBox::error(0, i18n("Host: %1 Port: %2 already exists.").arg(hostItem
->name
).arg(hostItem
->portnumber
));
691 ksw
->data()->INDIHostsList
.append(hostItem
);
693 QListViewItem
*item
= new QListViewItem(clientListView
);
694 item
->setPixmap(0, disconnected
);
695 item
->setText(1, hostConf
.nameIN
->text());
696 item
->setText(2, hostConf
.portnumber
->text());
705 void INDIDriver::modifyINDIHost()
708 INDIHostConf
hostConf(this);
709 hostConf
.setCaption(i18n("Modify Host"));
711 QListViewItem
*currentItem
= clientListView
->currentItem();
713 if (currentItem
== NULL
)
716 for (uint i
=0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
717 if (currentItem
->text(1) == ksw
->data()->INDIHostsList
.at(i
)->name
&&
718 currentItem
->text(2) == ksw
->data()->INDIHostsList
.at(i
)->portnumber
)
721 hostConf
.nameIN
->setText(ksw
->data()->INDIHostsList
.at(i
)->name
);
722 hostConf
.hostname
->setText(ksw
->data()->INDIHostsList
.at(i
)->hostname
);
723 hostConf
.portnumber
->setText(ksw
->data()->INDIHostsList
.at(i
)->portnumber
);
725 if (hostConf
.exec() == QDialog::Accepted
)
727 INDIHostsInfo
*hostItem
= new INDIHostsInfo
;
728 hostItem
->name
= hostConf
.nameIN
->text();
729 hostItem
->hostname
= hostConf
.hostname
->text();
730 hostItem
->portnumber
= hostConf
.portnumber
->text();
732 currentItem
->setText(1, hostConf
.nameIN
->text());
733 currentItem
->setText(2, hostConf
.portnumber
->text());
735 ksw
->data()->INDIHostsList
.replace(clientListView
->itemIndex(currentItem
), hostItem
);
743 void INDIDriver::removeINDIHost()
746 if (clientListView
->currentItem() == NULL
)
749 for (uint i
=0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
750 if (clientListView
->currentItem()->text(1) == ksw
->data()->INDIHostsList
.at(i
)->name
&&
751 clientListView
->currentItem()->text(2) == ksw
->data()->INDIHostsList
.at(i
)->portnumber
)
753 if (ksw
->data()->INDIHostsList
.at(i
)->isConnected
)
755 KMessageBox::error( 0, i18n("You need to disconnect the client before removing it."));
759 if (KMessageBox::questionYesNoCancel( 0, i18n("Are you sure you want to remove the %1 client?").arg(clientListView
->currentItem()->text(1)), i18n("Delete Confirmation"))!=KMessageBox::Yes
)
762 ksw
->data()->INDIHostsList
.remove(i
);
763 clientListView
->takeItem(clientListView
->currentItem());
773 void INDIDriver::saveHosts()
779 file
.setName( locateLocal( "appdata", "indihosts.xml" ) ); //determine filename in local user KDE directory tree.
781 if ( !file
.open( IO_WriteOnly
))
783 QString message
= i18n( "unable to write to file 'indihosts.xml'\nAny changes to INDI hosts configurations will not be saved." );
784 KMessageBox::sorry( 0, message
, i18n( "Could Not Open File" ) );
788 QTextStream
outstream(&file
);
790 for (uint i
= 0; i
< ksw
->data()->INDIHostsList
.count(); i
++)
793 hostData
= "<INDIHost name='";
794 hostData
+= ksw
->data()->INDIHostsList
.at(i
)->name
;
795 hostData
+= "' hostname='";
796 hostData
+= ksw
->data()->INDIHostsList
.at(i
)->hostname
;
797 hostData
+= "' port='";
798 hostData
+= ksw
->data()->INDIHostsList
.at(i
)->portnumber
;
799 hostData
+= "' />\n";
801 outstream
<< hostData
;
809 INDIDriver::~INDIDriver()
812 for (uint i
=0; i
< devices
.size(); i
++)
817 IDevice::IDevice(QString inLabel
, QString inDriver
, QString inExec
, QString inVersion
)
830 // not yet managed by DeviceManager
839 void IDevice::processstd(KProcess
* /*proc*/, char* buffer
, int /*buflen*/)
841 serverBuffer
.append(buffer
);
842 emit
newServerInput();
853 void IDevice::restart()
869 #include "indidriver.moc"