You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kstars/kstars/devicemanager.cpp

503 lines
12 KiB

/* 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"