|
|
|
/* Device Manager
|
|
|
|
Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
JM Changelog
|
|
|
|
2004-16-1: Start
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Options.h"
|
|
|
|
#include "devicemanager.h"
|
|
|
|
#include "indimenu.h"
|
|
|
|
#include "indiproperty.h"
|
|
|
|
#include "indigroup.h"
|
|
|
|
#include "indidevice.h"
|
|
|
|
#include "indi/indicom.h"
|
|
|
|
#include "kstars.h"
|
|
|
|
#include "kstarsdatetime.h"
|
|
|
|
|
|
|
|
#include <tqsocketnotifier.h>
|
|
|
|
#include <tqtextedit.h>
|
|
|
|
|
|
|
|
#include <tdelocale.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
|
|
#include <kstatusbar.h>
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <termios.h>
|
|
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
** The device manager contain devices running from one indiserver
|
|
|
|
** This allow KStars to control multiple devices distributed acorss
|
|
|
|
** multiple servers seemingly in a way that is completely transparent
|
|
|
|
** to devices and drivers alike.
|
|
|
|
** The device Manager can be thought of as the 'networking' parent
|
|
|
|
** of devices, while indimenu is 'GUI' parent of devices
|
|
|
|
*******************************************************************/
|
|
|
|
|
|
|
|
DeviceManager::DeviceManager(INDIMenu *INDIparent, int inID)
|
|
|
|
{
|
|
|
|
|
|
|
|
parent = INDIparent;
|
|
|
|
mgrID = inID;
|
|
|
|
|
|
|
|
indi_dev.setAutoDelete(true);
|
|
|
|
|
|
|
|
serverFD = -1;
|
|
|
|
serverFP = NULL;
|
|
|
|
XMLParser = NULL;
|
|
|
|
sNotifier = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceManager::~DeviceManager()
|
|
|
|
{
|
|
|
|
|
|
|
|
if (serverFP)
|
|
|
|
fclose(serverFP);
|
|
|
|
|
|
|
|
if (serverFD >= 0)
|
|
|
|
close(serverFD);
|
|
|
|
|
|
|
|
if (XMLParser)
|
|
|
|
{
|
|
|
|
delLilXML(XMLParser);
|
|
|
|
XMLParser = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
indi_dev.clear();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeviceManager::indiConnect(TQString inHost, TQString inPort)
|
|
|
|
{
|
|
|
|
host = inHost;
|
|
|
|
port = inPort;
|
|
|
|
TQString errMsg;
|
|
|
|
struct sockaddr_in pin;
|
|
|
|
struct hostent *serverHostName = gethostbyname(host.ascii());
|
|
|
|
errMsg = TQString("Connection to INDI host at %1 on port %2 failed.").arg(host).arg(port);
|
|
|
|
|
|
|
|
memset(&pin, 0, sizeof(pin));
|
|
|
|
pin.sin_family = AF_INET;
|
|
|
|
pin.sin_addr.s_addr = ((struct in_addr *) (serverHostName->h_addr))->s_addr;
|
|
|
|
pin.sin_port = htons(port.toInt());
|
|
|
|
|
|
|
|
if ( (serverFD = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
|
|
|
{
|
|
|
|
KMessageBox::error(0, i18n("Cannot create socket"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ::connect(serverFD, (struct sockaddr*) &pin, sizeof(pin)) == -1)
|
|
|
|
{
|
|
|
|
KMessageBox::error(0, errMsg);
|
|
|
|
serverFD = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// callback notified
|
|
|
|
sNotifier = new TQSocketNotifier( serverFD, TQSocketNotifier::Read, this);
|
|
|
|
TQObject::connect( sNotifier, TQT_SIGNAL(activated(int)), this, TQT_SLOT(dataReceived()));
|
|
|
|
|
|
|
|
if (XMLParser)
|
|
|
|
delLilXML(XMLParser);
|
|
|
|
XMLParser = newLilXML();
|
|
|
|
|
|
|
|
// ready for fprintf
|
|
|
|
serverFP = fdopen(serverFD, "w");
|
|
|
|
|
|
|
|
if (serverFP == NULL)
|
|
|
|
{
|
|
|
|
KMessageBox::error(0, i18n("Cannot read server file descriptor"));
|
|
|
|
serverFD = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setbuf (serverFP, NULL);
|
|
|
|
|
|
|
|
fprintf(serverFP, "<enableBLOB>Also</enableBLOB>\n");
|
|
|
|
fprintf(serverFP, "<getProperties version='%g'/>\n", INDIVERSION);
|
|
|
|
|
|
|
|
// We made it!
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DeviceManager::dataReceived()
|
|
|
|
{
|
|
|
|
char ibuf[32]; /* not so much user input lags */
|
|
|
|
char errmsg[ERRMSG_SIZE];
|
|
|
|
int i, nr;
|
|
|
|
|
|
|
|
/* read INDI command */
|
|
|
|
nr = read (serverFD, ibuf, sizeof(ibuf)-1);
|
|
|
|
if (nr <= 0)
|
|
|
|
{
|
|
|
|
if (nr < 0)
|
|
|
|
strcpy (errmsg, "INDI: input error.");
|
|
|
|
else
|
|
|
|
strcpy (errmsg, "INDI: agent closed connection.");
|
|
|
|
|
|
|
|
|
|
|
|
tcflush(serverFD, TCIFLUSH);
|
|
|
|
sNotifier->disconnect();
|
|
|
|
close(serverFD);
|
|
|
|
parent->removeDeviceMgr(mgrID);
|
|
|
|
KMessageBox::error(0, TQString::fromLatin1(errmsg));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ibuf[ sizeof( ibuf )-1 ] = '\0';
|
|
|
|
|
|
|
|
/* process each char */
|
|
|
|
for (i = 0; i < nr; i++)
|
|
|
|
{
|
|
|
|
if (!XMLParser)
|
|
|
|
return;
|
|
|
|
|
|
|
|
XMLEle *root = readXMLEle (XMLParser, (int)ibuf[i], errmsg);
|
|
|
|
if (root)
|
|
|
|
{
|
|
|
|
//prXMLEle (stdout, root, 0);
|
|
|
|
if (dispatchCommand(root, errmsg) < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s", errmsg);
|
|
|
|
prXMLEle (stdout, root, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
delXMLEle (root);
|
|
|
|
}
|
|
|
|
else if (*errmsg)
|
|
|
|
{
|
|
|
|
kdDebug() << errmsg << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int DeviceManager::dispatchCommand(XMLEle *root, char errmsg[])
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!strcmp (tagXMLEle(root), "message"))
|
|
|
|
return messageCmd(root, errmsg);
|
|
|
|
else if (!strcmp (tagXMLEle(root), "delProperty"))
|
|
|
|
return delPropertyCmd(root, errmsg);
|
|
|
|
|
|
|
|
/* Get the device, if not available, create it */
|
|
|
|
INDI_D *dp = findDev (root, 1, errmsg);
|
|
|
|
if (dp == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!strcmp (tagXMLEle(root), "defTextVector"))
|
|
|
|
return dp->buildTextGUI(root, errmsg);
|
|
|
|
else if (!strcmp (tagXMLEle(root), "defNumberVector"))
|
|
|
|
return dp->buildNumberGUI(root, errmsg);
|
|
|
|
else if (!strcmp (tagXMLEle(root), "defSwitchVector"))
|
|
|
|
return dp->buildSwitchesGUI(root, errmsg);
|
|
|
|
else if (!strcmp (tagXMLEle(root), "defLightVector"))
|
|
|
|
return dp->buildLightsGUI(root, errmsg);
|
|
|
|
else if (!strcmp (tagXMLEle(root), "defBLOBVector"))
|
|
|
|
return dp->buildBLOBGUI(root, errmsg);
|
|
|
|
else if (!strcmp (tagXMLEle(root), "setTextVector") ||
|
|
|
|
!strcmp (tagXMLEle(root), "setNumberVector") ||
|
|
|
|
!strcmp (tagXMLEle(root), "setSwitchVector") ||
|
|
|
|
!strcmp (tagXMLEle(root), "setLightVector") ||
|
|
|
|
!strcmp (tagXMLEle(root), "setBLOBVector"))
|
|
|
|
return dp->setAnyCmd(root, errmsg);
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* delete the property in the given device, including widgets and data structs.
|
|
|
|
* when last property is deleted, delete the device too.
|
|
|
|
* if no property name attribute at all, delete the whole device regardless.
|
|
|
|
* return 0 if ok, else -1 with reason in errmsg[].
|
|
|
|
*/
|
|
|
|
int DeviceManager::delPropertyCmd (XMLEle *root, char errmsg[])
|
|
|
|
{
|
|
|
|
|
|
|
|
XMLAtt *ap;
|
|
|
|
INDI_D *dp;
|
|
|
|
INDI_P *pp;
|
|
|
|
|
|
|
|
/* dig out device and optional property name */
|
|
|
|
dp = findDev (root, 0, errmsg);
|
|
|
|
if (!dp)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
checkMsg(root, dp);
|
|
|
|
|
|
|
|
ap = findXMLAtt (root, "name");
|
|
|
|
|
|
|
|
/* Delete property if it exists, otherwise, delete the whole device */
|
|
|
|
if (ap)
|
|
|
|
{
|
|
|
|
pp = dp->findProp(TQString(valuXMLAtt(ap)));
|
|
|
|
|
|
|
|
if(pp)
|
|
|
|
return dp->removeProperty(pp);
|
|
|
|
else
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
// delete the whole device
|
|
|
|
else
|
|
|
|
return removeDevice(dp->name, errmsg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int DeviceManager::removeDevice(TQString devName, char errmsg[])
|
|
|
|
{
|
|
|
|
|
|
|
|
// remove all devices if devName == NULL
|
|
|
|
if (devName == NULL)
|
|
|
|
{
|
|
|
|
indi_dev.clear();
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned int i=0; i < indi_dev.count(); i++)
|
|
|
|
{
|
|
|
|
if (indi_dev.at(i)->name == devName)
|
|
|
|
{
|
|
|
|
kdDebug() << "Device Manager: Device found, deleting " << devName << endl;
|
|
|
|
indi_dev.remove(i);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(errmsg, ERRMSG_SIZE, "Device %.32s not found" , devName.ascii());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
INDI_D * DeviceManager::findDev (TQString devName, char errmsg[])
|
|
|
|
{
|
|
|
|
/* search for existing */
|
|
|
|
for (unsigned int i = 0; i < indi_dev.count(); i++)
|
|
|
|
{
|
|
|
|
if (indi_dev.at(i)->name == devName)
|
|
|
|
return (indi_dev.at(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: no such device %.32s", devName.ascii());
|
|
|
|
kdDebug() << errmsg;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add new device to mainrc_w using info in dep.
|
|
|
|
- * if trouble return NULL with reason in errmsg[]
|
|
|
|
- */
|
|
|
|
INDI_D * DeviceManager::addDevice (XMLEle *dep, char errmsg[])
|
|
|
|
{
|
|
|
|
INDI_D *dp;
|
|
|
|
XMLAtt *ap;
|
|
|
|
|
|
|
|
/* allocate new INDI_D on indi_dev */
|
|
|
|
ap = findAtt (dep, "device", errmsg);
|
|
|
|
if (!ap)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (parent->currentLabel.isEmpty())
|
|
|
|
parent->setCustomLabel(valuXMLAtt(ap));
|
|
|
|
|
|
|
|
dp = new INDI_D(parent, this, TQString(valuXMLAtt(ap)), parent->currentLabel);
|
|
|
|
|
|
|
|
indi_dev.append(dp);
|
|
|
|
|
|
|
|
emit newDevice();
|
|
|
|
|
|
|
|
// Reset label
|
|
|
|
parent->currentLabel = "";
|
|
|
|
|
|
|
|
/* ok */
|
|
|
|
return dp;
|
|
|
|
}
|
|
|
|
|
|
|
|
INDI_D * DeviceManager::findDev (XMLEle *root, int create, char errmsg[])
|
|
|
|
{
|
|
|
|
XMLAtt *ap;
|
|
|
|
char *dn;
|
|
|
|
|
|
|
|
/* get device name */
|
|
|
|
ap = findAtt (root, "device", errmsg);
|
|
|
|
if (!ap)
|
|
|
|
return (NULL);
|
|
|
|
dn = valuXMLAtt(ap);
|
|
|
|
|
|
|
|
/* search for existing */
|
|
|
|
for (uint i = 0; i < indi_dev.count(); i++)
|
|
|
|
{
|
|
|
|
if (indi_dev.at(i)->name == TQString(dn))
|
|
|
|
return (indi_dev.at(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not found, create if ok */
|
|
|
|
if (create)
|
|
|
|
return (addDevice (root, errmsg));
|
|
|
|
|
|
|
|
|
|
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.32s> no such device %.32s", tagXMLEle(root), dn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* a general message command received from the device.
|
|
|
|
* return 0 if ok, else -1 with reason in errmsg[].
|
|
|
|
*/
|
|
|
|
int DeviceManager::messageCmd (XMLEle *root, char errmsg[])
|
|
|
|
{
|
|
|
|
checkMsg (root, findDev (root, 0, errmsg));
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* display message attribute.
|
|
|
|
* N.B. don't put carriage control in msg, we take care of that.
|
|
|
|
*/
|
|
|
|
void DeviceManager::checkMsg (XMLEle *root, INDI_D *dp)
|
|
|
|
{
|
|
|
|
XMLAtt *ap;
|
|
|
|
ap = findXMLAtt(root, "message");
|
|
|
|
|
|
|
|
if (ap)
|
|
|
|
doMsg(root, dp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* display valu of message and timestamp in dp's scrolled area, if any, else general.
|
|
|
|
* prefix our time stamp if not included.
|
|
|
|
* N.B. don't put carriage control in msg, we take care of that.
|
|
|
|
*/
|
|
|
|
void DeviceManager::doMsg (XMLEle *msg, INDI_D *dp)
|
|
|
|
{
|
|
|
|
TQTextEdit *txt_w;
|
|
|
|
XMLAtt *message;
|
|
|
|
XMLAtt *timestamp;
|
|
|
|
|
|
|
|
if (dp == NULL)
|
|
|
|
{
|
|
|
|
kdDebug() << "Warning: dp is null." << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
txt_w = dp->msgST_w;
|
|
|
|
|
|
|
|
/* prefix our timestamp if not with msg */
|
|
|
|
timestamp = findXMLAtt (msg, "timestamp");
|
|
|
|
|
|
|
|
if (timestamp)
|
|
|
|
txt_w->insert(TQString(valuXMLAtt(timestamp)) + TQString(" "));
|
|
|
|
else
|
|
|
|
txt_w->insert( KStarsDateTime::currentDateTime().toString("yyyy/mm/dd - h:m:s ap "));
|
|
|
|
|
|
|
|
/* finally! the msg */
|
|
|
|
message = findXMLAtt(msg, "message");
|
|
|
|
|
|
|
|
txt_w->insert( TQString(valuXMLAtt(message)) + TQString("\n"));
|
|
|
|
|
|
|
|
if ( Options::indiMessages() )
|
|
|
|
parent->ksw->statusBar()->changeItem( TQString(valuXMLAtt(message)), 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::sendNewText (INDI_P *pp)
|
|
|
|
{
|
|
|
|
INDI_E *lp;
|
|
|
|
|
|
|
|
fprintf(serverFP, "<newTextVector\n");
|
|
|
|
fprintf(serverFP, " device='%s'\n", pp->pg->dp->name.ascii());
|
|
|
|
fprintf(serverFP, " name='%s'\n>", pp->name.ascii());
|
|
|
|
|
|
|
|
for (lp = pp->el.first(); lp != NULL; lp = pp->el.next())
|
|
|
|
{
|
|
|
|
fprintf(serverFP, " <oneText\n");
|
|
|
|
fprintf(serverFP, " name='%s'>\n", lp->name.ascii());
|
|
|
|
fprintf(serverFP, " %s\n", lp->text.ascii());
|
|
|
|
fprintf(serverFP, " </oneText>\n");
|
|
|
|
}
|
|
|
|
fprintf(serverFP, "</newTextVector>\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::sendNewNumber (INDI_P *pp)
|
|
|
|
{
|
|
|
|
INDI_E *lp;
|
|
|
|
|
|
|
|
fprintf(serverFP, "<newNumberVector\n");
|
|
|
|
fprintf(serverFP, " device='%s'\n", pp->pg->dp->name.ascii());
|
|
|
|
fprintf(serverFP, " name='%s'\n>", pp->name.ascii());
|
|
|
|
|
|
|
|
for (lp = pp->el.first(); lp != NULL; lp = pp->el.next())
|
|
|
|
{
|
|
|
|
fprintf(serverFP, " <oneNumber\n");
|
|
|
|
fprintf(serverFP, " name='%s'>\n", lp->name.ascii());
|
|
|
|
fprintf(serverFP, " %g\n", lp->targetValue);
|
|
|
|
fprintf(serverFP, " </oneNumber>\n");
|
|
|
|
}
|
|
|
|
fprintf(serverFP, "</newNumberVector>\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::sendNewSwitch (INDI_P *pp, int index)
|
|
|
|
{
|
|
|
|
INDI_E *lp;
|
|
|
|
int i=0;
|
|
|
|
|
|
|
|
fprintf (serverFP,"<newSwitchVector\n");
|
|
|
|
fprintf (serverFP," device='%s'\n", pp->pg->dp->name.ascii());
|
|
|
|
fprintf (serverFP," name='%s'>\n", pp->name.ascii());
|
|
|
|
|
|
|
|
for (lp = pp->el.first(); lp != NULL; lp = pp->el.next(), i++)
|
|
|
|
if (i == index)
|
|
|
|
{
|
|
|
|
fprintf (serverFP," <oneSwitch\n");
|
|
|
|
fprintf (serverFP," name='%s'>\n", lp->name.ascii());
|
|
|
|
fprintf (serverFP," %s\n", lp->state == PS_ON ? "On" : "Off");
|
|
|
|
fprintf (serverFP," </oneSwitch>\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fprintf (serverFP, "</newSwitchVector>\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::startBlob (TQString devName, TQString propName, TQString timestamp)
|
|
|
|
{
|
|
|
|
|
|
|
|
fprintf (serverFP, "<newBLOBVector\n");
|
|
|
|
fprintf (serverFP, " device='%s'\n", devName.ascii());
|
|
|
|
fprintf (serverFP, " name='%s'\n", propName.ascii());
|
|
|
|
fprintf (serverFP, " timestamp='%s'>\n", timestamp.ascii());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::sendOneBlob(TQString blobName, unsigned int blobSize, TQString blobFormat, unsigned char * blobBuffer)
|
|
|
|
{
|
|
|
|
|
|
|
|
fprintf (serverFP, " <oneBLOB\n");
|
|
|
|
fprintf (serverFP, " name='%s'\n", blobName.ascii());
|
|
|
|
fprintf (serverFP, " size='%d'\n", blobSize);
|
|
|
|
fprintf (serverFP, " format='%s'>\n", blobFormat.ascii());
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < blobSize; i += 72)
|
|
|
|
fprintf (serverFP, " %.72s\n", blobBuffer+i);
|
|
|
|
|
|
|
|
fprintf (serverFP, " </oneBLOB>\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::finishBlob()
|
|
|
|
{
|
|
|
|
fprintf (serverFP, "</newBLOBVector>\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "devicemanager.moc"
|