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.
1000 lines
23 KiB
1000 lines
23 KiB
/* INDI frontend for KStars
|
|
Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
|
|
Elwood C. Downey.
|
|
|
|
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:
|
|
2003-04-28 Used indimenu.c as a template. C --> C++, Xm --> KDE/TQt
|
|
2003-05-01 Added tab for devices and a group feature
|
|
2003-05-02 Added scrolling area. Most things are rewritten
|
|
2003-05-05 Device/Group seperation
|
|
2003-05-29 Replaced raw INDI time with KStars's timedialog
|
|
2003-08-02 Upgrading to INDI v 1.11
|
|
2003-08-09 Initial support for non-sidereal tracking
|
|
2004-01-15 redesigning the GUI to support INDI v1.2 and fix previous GUI bugs
|
|
and problems. The new GUI can easily incoperate extensions to the INDI
|
|
protocol as required.
|
|
|
|
*/
|
|
|
|
#include "indiproperty.h"
|
|
#include "indigroup.h"
|
|
#include "indidevice.h"
|
|
#include "devicemanager.h"
|
|
#include "indimenu.h"
|
|
#include "indidriver.h"
|
|
#include "indistd.h"
|
|
#include "indi/indicom.h"
|
|
#include "kstars.h"
|
|
#include "skyobject.h"
|
|
#include "timedialog.h"
|
|
#include "geolocation.h"
|
|
#include "indi/base64.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>
|
|
#include <zlib.h>
|
|
|
|
#include <tqlineedit.h>
|
|
#include <tqtextedit.h>
|
|
#include <tqframe.h>
|
|
#include <tqtabwidget.h>
|
|
#include <tqcheckbox.h>
|
|
#include <tqlabel.h>
|
|
#include <tqpushbutton.h>
|
|
#include <tqlayout.h>
|
|
#include <tqtooltip.h>
|
|
#include <tqwhatsthis.h>
|
|
#include <tqbuttongroup.h>
|
|
#include <tqscrollview.h>
|
|
#include <tqsocketnotifier.h>
|
|
#include <tqvbox.h>
|
|
#include <tqdatetime.h>
|
|
#include <tqtable.h>
|
|
#include <tqstring.h>
|
|
#include <tqptrlist.h>
|
|
|
|
#include <kled.h>
|
|
#include <klineedit.h>
|
|
#include <kpushbutton.h>
|
|
#include <kapplication.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <klistview.h>
|
|
#include <kdebug.h>
|
|
#include <kcombobox.h>
|
|
#include <knuminput.h>
|
|
#include <kdialogbase.h>
|
|
#include <kstatusbar.h>
|
|
#include <kpopupmenu.h>
|
|
|
|
#define NINDI_STD 26
|
|
/* INDI standard property used across all clients to enable interoperability. */
|
|
|
|
const char * indi_std[NINDI_STD] =
|
|
{"CONNECTION", "DEVICE_PORT", "TIME", "SDTIME", "GEOGRAPHIC_COORD", "EQUATORIAL_COORD", "EQUATORIAL_EOD_COORD", "HORIZONTAL_COORD", "ABORT_MOTION", "ON_COORD_SET", "SOLAR_SYSTEM", "MOVEMENT", "PARK", "CCD_EXPOSE_DURATION", "CCD_TEMPERATURE", "CCD_FRAME", "CCD_FRAME_TYPE", "CCD_BINNING", "CCD_INFO", "CCDPREVIEW_STREAM", "CCDPREVIEW_CTRL", "VIDEO_STREAM", "FOCUS_SPEED", "FOCUS_MOTION", "FOCUS_TIMER", "FILTER_SLOT" };
|
|
|
|
/*******************************************************************
|
|
** INDI Device: The work-horse. Responsible for handling its
|
|
** child properties and managing signal and changes.
|
|
*******************************************************************/
|
|
INDI_D::INDI_D(INDIMenu *menuParent, DeviceManager *parentManager, TQString inName, TQString inLabel)
|
|
{
|
|
name = inName;
|
|
label = inLabel;
|
|
parent = menuParent;
|
|
parentMgr = parentManager;
|
|
|
|
gl.setAutoDelete(true);
|
|
|
|
deviceVBox = menuParent->addVBoxPage(inLabel);
|
|
groupContainer = new TQTabWidget(deviceVBox);
|
|
|
|
msgST_w = new TQTextEdit(deviceVBox);
|
|
msgST_w->setReadOnly(true);
|
|
msgST_w->setMaximumHeight(100);
|
|
|
|
dataBuffer = (unsigned char *) malloc (1);
|
|
|
|
stdDev = new INDIStdDevice(this, parent->ksw);
|
|
|
|
curGroup = NULL;
|
|
|
|
INDIStdSupport = false;
|
|
|
|
}
|
|
|
|
INDI_D::~INDI_D()
|
|
{
|
|
gl.clear();
|
|
delete(deviceVBox);
|
|
delete (stdDev);
|
|
free (dataBuffer);
|
|
dataBuffer = NULL;
|
|
deviceVBox = NULL;
|
|
stdDev = NULL;
|
|
}
|
|
|
|
void INDI_D::registerProperty(INDI_P *pp)
|
|
{
|
|
|
|
if (isINDIStd(pp))
|
|
pp->pg->dp->INDIStdSupport = true;
|
|
|
|
stdDev->registerProperty(pp);
|
|
|
|
}
|
|
|
|
bool INDI_D::isINDIStd(INDI_P *pp)
|
|
{
|
|
for (uint i=0; i < NINDI_STD; i++)
|
|
if (!strcmp(pp->name.ascii(), indi_std[i]))
|
|
{
|
|
pp->stdID = i;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Remove a property from a group, if there are no more properties
|
|
* left in the group, then delete the group as well */
|
|
int INDI_D::removeProperty(INDI_P *pp)
|
|
{
|
|
for (unsigned int i=0; i < gl.count(); i++)
|
|
if (gl.at(i)->removeProperty(pp))
|
|
{
|
|
if (gl.at(i)->pl.count() == 0)
|
|
gl.remove(i);
|
|
return 0;
|
|
}
|
|
|
|
|
|
kdDebug() << "INDI: Device " << name << " has no property named " << pp->name << endl;
|
|
return (-1);
|
|
}
|
|
|
|
/* implement any <set???> received from the device.
|
|
* return 0 if ok, else -1 with reason in errmsg[]
|
|
*/
|
|
int INDI_D::setAnyCmd (XMLEle *root, char errmsg[])
|
|
{
|
|
XMLAtt *ap;
|
|
INDI_P *pp;
|
|
|
|
ap = findAtt (root, "name", errmsg);
|
|
if (!ap)
|
|
return (-1);
|
|
|
|
pp = findProp (valuXMLAtt(ap));
|
|
if (!pp)
|
|
{
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.32s> device %.32s has no property named %.64s",
|
|
tagXMLEle(root), name.ascii(), valuXMLAtt(ap));
|
|
return (-1);
|
|
}
|
|
|
|
parentMgr->checkMsg (root, this);
|
|
|
|
return (setValue (pp, root, errmsg));
|
|
}
|
|
|
|
/* set the given GUI property according to the XML command.
|
|
* return 0 if ok else -1 with reason in errmsg
|
|
*/
|
|
int INDI_D::setValue (INDI_P *pp, XMLEle *root, char errmsg[])
|
|
{
|
|
XMLAtt *ap;
|
|
|
|
/* set overall property state, if any */
|
|
ap = findXMLAtt (root, "state");
|
|
if (ap)
|
|
{
|
|
if (crackLightState (valuXMLAtt(ap), &pp->state) == 0)
|
|
pp->drawLt (pp->state);
|
|
else
|
|
{
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> bogus state %.64s for %.64s %.64s",
|
|
tagXMLEle(root), valuXMLAtt(ap), name.ascii(), pp->name.ascii());
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
/* allow changing the timeout */
|
|
ap = findXMLAtt (root, "timeout");
|
|
if (ap)
|
|
pp->timeout = atof(valuXMLAtt(ap));
|
|
|
|
/* process specific GUI features */
|
|
switch (pp->guitype)
|
|
{
|
|
case PG_NONE:
|
|
break;
|
|
|
|
case PG_NUMERIC: /* FALLTHRU */
|
|
case PG_TEXT:
|
|
return (setTextValue (pp, root, errmsg));
|
|
break;
|
|
|
|
case PG_BUTTONS:
|
|
case PG_LIGHTS:
|
|
case PG_RADIO:
|
|
case PG_MENU:
|
|
return (setLabelState (pp, root, errmsg));
|
|
break;
|
|
|
|
case PG_BLOB:
|
|
return (setBLOB(pp, root, errmsg));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/* set the given TEXT or NUMERIC property from the given element.
|
|
* root should have <text> or <number> child.
|
|
* return 0 if ok else -1 with reason in errmsg
|
|
*/
|
|
int INDI_D::setTextValue (INDI_P *pp, XMLEle *root, char errmsg[])
|
|
{
|
|
XMLEle *ep;
|
|
XMLAtt *ap;
|
|
INDI_E *lp;
|
|
TQString elementName;
|
|
char iNumber[32];
|
|
double min, max;
|
|
|
|
for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
|
|
{
|
|
if (strcmp (tagXMLEle(ep), "oneText") && strcmp(tagXMLEle(ep), "oneNumber"))
|
|
continue;
|
|
|
|
ap = findXMLAtt(ep, "name");
|
|
if (!ap)
|
|
{
|
|
kdDebug() << "Error: unable to find attribute 'name' for property " << pp->name << endl;
|
|
return (-1);
|
|
}
|
|
|
|
elementName = valuXMLAtt(ap);
|
|
|
|
lp = pp->findElement(elementName);
|
|
|
|
if (!lp)
|
|
{
|
|
snprintf(errmsg, ERRMSG_SIZE, "Error: unable to find element '%.64s' in property '%.64s'", elementName.ascii(), pp->name.ascii());
|
|
return (-1);
|
|
}
|
|
|
|
//fprintf(stderr, "tag okay, getting perm\n");
|
|
switch (pp->perm)
|
|
{
|
|
case PP_RW: // FALLTHRU
|
|
case PP_RO:
|
|
if (pp->guitype == PG_TEXT)
|
|
{
|
|
lp->text = TQString(pcdataXMLEle(ep));
|
|
lp->read_w->setText(lp->text);
|
|
}
|
|
else if (pp->guitype == PG_NUMERIC)
|
|
{
|
|
lp->value = atof(pcdataXMLEle(ep));
|
|
numberFormat(iNumber, lp->format.ascii(), lp->value);
|
|
lp->text = iNumber;
|
|
lp->read_w->setText(lp->text);
|
|
|
|
ap = findXMLAtt (ep, "min");
|
|
if (ap) { min = atof(valuXMLAtt(ap)); lp->setMin(min); }
|
|
ap = findXMLAtt (ep, "max");
|
|
if (ap) { max = atof(valuXMLAtt(ap)); lp->setMax(max); }
|
|
|
|
/*if (lp->spin_w)
|
|
{
|
|
lp->spin_w->setValue(lp->value);
|
|
lp->spinChanged(lp->value);
|
|
}*/
|
|
|
|
}
|
|
break;
|
|
|
|
case PP_WO:
|
|
if (pp->guitype == PG_TEXT)
|
|
lp->write_w->setText(TQString(pcdataXMLEle(ep)));
|
|
else if (pp->guitype == PG_NUMERIC)
|
|
{
|
|
lp->value = atof(pcdataXMLEle(ep));
|
|
numberFormat(iNumber, lp->format.ascii(), lp->value);
|
|
lp->text = iNumber;
|
|
|
|
if (lp->spin_w)
|
|
lp->spin_w->setValue(lp->value);
|
|
else
|
|
lp->write_w->setText(lp->text);
|
|
|
|
ap = findXMLAtt (ep, "min");
|
|
if (ap) { min = (int) atof(valuXMLAtt(ap)); lp->setMin(min); }
|
|
ap = findXMLAtt (ep, "max");
|
|
if (ap) { max = (int) atof(valuXMLAtt(ap)); lp->setMax(max); }
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
/* handle standard cases if needed */
|
|
stdDev->setTextValue(pp);
|
|
|
|
// suppress warning
|
|
errmsg = errmsg;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* set the given BUTTONS or LIGHTS property from the given element.
|
|
* root should have some <switch> or <light> tqchildren.
|
|
* return 0 if ok else -1 with reason in errmsg
|
|
*/
|
|
int INDI_D::setLabelState (INDI_P *pp, XMLEle *root, char errmsg[])
|
|
{
|
|
int menuChoice=0;
|
|
unsigned i=0;
|
|
XMLEle *ep;
|
|
XMLAtt *ap;
|
|
INDI_E *lp = NULL;
|
|
int islight;
|
|
PState state;
|
|
|
|
/* for each child element */
|
|
for (ep = nextXMLEle (root, 1), i=0; ep != NULL; ep = nextXMLEle (root, 0), i++)
|
|
{
|
|
|
|
/* only using light and switch */
|
|
islight = !strcmp (tagXMLEle(ep), "oneLight");
|
|
if (!islight && strcmp (tagXMLEle(ep), "oneSwitch"))
|
|
continue;
|
|
|
|
ap = findXMLAtt (ep, "name");
|
|
/* no name */
|
|
if (!ap)
|
|
{
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s %.64s requires name",
|
|
tagXMLEle(root), name.ascii(), pp->name.ascii(), tagXMLEle(ep));
|
|
return (-1);
|
|
}
|
|
|
|
if ((islight && crackLightState (pcdataXMLEle(ep), &state) < 0)
|
|
|| (!islight && crackSwitchState (pcdataXMLEle(ep), &state) < 0))
|
|
{
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s",
|
|
tagXMLEle(root), pcdataXMLEle(ep), name.ascii(), pp->name.ascii(), tagXMLEle(ep));
|
|
return (-1);
|
|
}
|
|
|
|
/* find matching label */
|
|
//fprintf(stderr, "Find matching label. Name from XML is %s\n", valuXMLAtt(ap));
|
|
lp = pp->findElement(TQString(valuXMLAtt(ap)));
|
|
|
|
if (!lp)
|
|
{
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s has no choice named %.64s",
|
|
tagXMLEle(root), name.ascii(), pp->name.ascii(), valuXMLAtt(ap));
|
|
return (-1);
|
|
}
|
|
|
|
TQFont buttonFont;
|
|
/* engage new state */
|
|
lp->state = state;
|
|
|
|
switch (pp->guitype)
|
|
{
|
|
case PG_BUTTONS:
|
|
if (islight)
|
|
break;
|
|
|
|
lp->push_w->setDown(state == PS_ON ? true : false);
|
|
buttonFont = lp->push_w->font();
|
|
buttonFont.setBold(state == PS_ON ? TRUE : FALSE);
|
|
lp->push_w->setFont(buttonFont);
|
|
|
|
break;
|
|
|
|
case PG_RADIO:
|
|
lp->check_w->setChecked(state == PS_ON ? true : false);
|
|
break;
|
|
case PG_MENU:
|
|
if (state == PS_ON)
|
|
{
|
|
if (menuChoice)
|
|
{
|
|
snprintf(errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s has multiple ON states", tagXMLEle(root), name.ascii(), pp->name.ascii());
|
|
return (-1);
|
|
}
|
|
menuChoice = 1;
|
|
pp->om_w->setCurrentItem(i);
|
|
}
|
|
break;
|
|
|
|
case PG_LIGHTS:
|
|
lp->drawLt();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
stdDev->setLabelState(pp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Set BLOB vector. Process incoming data stream
|
|
* Return 0 if okay, -1 if error
|
|
*/
|
|
int INDI_D::setBLOB(INDI_P *pp, XMLEle * root, char errmsg[])
|
|
{
|
|
|
|
XMLEle *ep;
|
|
INDI_E *blobEL;
|
|
|
|
for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0))
|
|
{
|
|
|
|
if (strcmp(tagXMLEle(ep), "oneBLOB") == 0)
|
|
{
|
|
|
|
blobEL = pp->findElement(TQString(findXMLAttValu (ep, "name")));
|
|
|
|
if (blobEL)
|
|
return processBlob(blobEL, ep, errmsg);
|
|
else
|
|
{
|
|
sprintf (errmsg, "INDI: set %64s.%64s.%64s not found", name.ascii(), pp->name.ascii(), findXMLAttValu(ep, "name"));
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
/* Process incoming data stream
|
|
* Return 0 if okay, -1 if error
|
|
*/
|
|
int INDI_D::processBlob(INDI_E *blobEL, XMLEle *ep, char errmsg[])
|
|
{
|
|
XMLAtt *ap;
|
|
int blobSize=0, r=0, dataType=0;
|
|
uLongf dataSize=0;
|
|
TQString dataFormat;
|
|
char *baseBuffer;
|
|
unsigned char *blobBuffer(NULL);
|
|
bool iscomp(false);
|
|
|
|
ap = findXMLAtt(ep, "size");
|
|
if (!ap)
|
|
{
|
|
sprintf (errmsg, "INDI: set %64s size not found", blobEL->name.ascii());
|
|
return (-1);
|
|
}
|
|
|
|
dataSize = atoi(valuXMLAtt(ap));
|
|
|
|
ap = findXMLAtt(ep, "format");
|
|
if (!ap)
|
|
{
|
|
sprintf (errmsg, "INDI: set %64s format not found", blobEL->name.ascii());
|
|
return (-1);
|
|
}
|
|
|
|
dataFormat = TQString(valuXMLAtt(ap));
|
|
|
|
baseBuffer = (char *) malloc ( (3*pcdatalenXMLEle(ep)/4) * sizeof (char));
|
|
blobSize = from64tobits (baseBuffer, pcdataXMLEle(ep));
|
|
blobBuffer = (unsigned char *) baseBuffer;
|
|
|
|
/* Blob size = 0 when only state changes */
|
|
if (dataSize == 0)
|
|
{
|
|
free (blobBuffer);
|
|
return (0);
|
|
}
|
|
else if (blobSize < 0)
|
|
{
|
|
free (blobBuffer);
|
|
sprintf (errmsg, "INDI: %64s.%64s.%64s bad base64", name.ascii(), blobEL->pp->name.ascii(), blobEL->name.ascii());
|
|
return (-1);
|
|
}
|
|
|
|
iscomp = (dataFormat.find(".z") != -1);
|
|
|
|
dataFormat.remove(".z");
|
|
|
|
if (dataFormat == ".fits") dataType = DATA_FITS;
|
|
else if (dataFormat == ".stream") dataType = DATA_STREAM;
|
|
else if (dataFormat == ".ccdpreview") dataType = DATA_CCDPREVIEW;
|
|
else dataType = DATA_OTHER;
|
|
|
|
//kdDebug() << "We're getting data with size " << dataSize << endl;
|
|
//kdDebug() << "data format " << dataFormat << endl;
|
|
|
|
if (iscomp)
|
|
{
|
|
|
|
dataBuffer = (unsigned char *) realloc (dataBuffer, (dataSize * sizeof(unsigned char)));
|
|
r = uncompress(dataBuffer, &dataSize, blobBuffer, (uLong) blobSize);
|
|
if (r != Z_OK)
|
|
{
|
|
sprintf(errmsg, "INDI: %64s.%64s.%64s compression error: %d", name.ascii(), blobEL->pp->name.ascii(), blobEL->name.ascii(), r);
|
|
free (blobBuffer);
|
|
return -1;
|
|
}
|
|
|
|
//kdDebug() << "compressed" << endl;
|
|
}
|
|
else
|
|
{
|
|
//kdDebug() << "uncompressed!!" << endl;
|
|
dataBuffer = (unsigned char *) realloc (dataBuffer, (dataSize * sizeof(unsigned char)));
|
|
memcpy(dataBuffer, blobBuffer, dataSize);
|
|
}
|
|
|
|
stdDev->handleBLOB(dataBuffer, dataSize, dataFormat);
|
|
|
|
free (blobBuffer);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
bool INDI_D::isOn()
|
|
{
|
|
|
|
INDI_P *prop;
|
|
|
|
prop = findProp(TQString("CONNECTION"));
|
|
if (!prop)
|
|
return false;
|
|
|
|
return (prop->isOn(TQString("CONNECT")));
|
|
}
|
|
|
|
INDI_P * INDI_D::addProperty (XMLEle *root, char errmsg[])
|
|
{
|
|
INDI_P *pp = NULL;
|
|
INDI_G *pg = NULL;
|
|
XMLAtt *ap = NULL;
|
|
|
|
// Search for group tag
|
|
ap = findAtt (root, "group", errmsg);
|
|
if (!ap)
|
|
{
|
|
kdDebug() << TQString(errmsg) << endl;
|
|
return NULL;
|
|
}
|
|
// Find an existing group, if none found, create one
|
|
pg = findGroup(TQString(valuXMLAtt(ap)), 1, errmsg);
|
|
|
|
if (!pg)
|
|
return NULL;
|
|
|
|
/* get property name and add new property to dp */
|
|
ap = findAtt (root, "name", errmsg);
|
|
if (ap == NULL)
|
|
return NULL;
|
|
|
|
if (findProp (valuXMLAtt(ap)))
|
|
{
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s %.64s %.64s> already exists.\n", tagXMLEle(root),
|
|
name.ascii(), valuXMLAtt(ap));
|
|
return NULL;
|
|
}
|
|
|
|
/* RemoveQt::Vertical spacer from group tqlayout, this is done everytime
|
|
* a new property arrives. The spacer is then appended to the end of the
|
|
* properties */
|
|
pg->propertyLayout->removeItem(pg->VerticalSpacer);
|
|
|
|
pp = new INDI_P(pg, TQString(valuXMLAtt(ap)));
|
|
|
|
/* init state */
|
|
ap = findAtt (root, "state", errmsg);
|
|
if (!ap)
|
|
{
|
|
delete(pp);
|
|
return (NULL);
|
|
}
|
|
|
|
if (crackLightState (valuXMLAtt(ap), &pp->state) < 0)
|
|
{
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> bogus state %.64s for %.64s %.64s",
|
|
tagXMLEle(root), valuXMLAtt(ap), pp->pg->dp->name.ascii(), pp->name.ascii());
|
|
delete(pp);
|
|
return (NULL);
|
|
}
|
|
|
|
/* init timeout */
|
|
ap = findAtt (root, "timeout", NULL);
|
|
/* default */
|
|
pp->timeout = ap ? atof(valuXMLAtt(ap)) : 0;
|
|
|
|
/* log any messages */
|
|
parentMgr->checkMsg (root, this);
|
|
|
|
pp->addGUI(root);
|
|
|
|
/* ok! */
|
|
return (pp);
|
|
}
|
|
|
|
INDI_P * INDI_D::findProp (TQString name)
|
|
{
|
|
for (unsigned int i = 0; i < gl.count(); i++)
|
|
for (unsigned int j = 0; j < gl.at(i)->pl.count(); j++)
|
|
if (name == gl.at(i)->pl.at(j)->name)
|
|
return (gl.at(i)->pl.at(j));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
INDI_G * INDI_D::findGroup (TQString grouptag, int create, char errmsg[])
|
|
{
|
|
INDI_G *ig;
|
|
|
|
for (ig = gl.first(); ig != NULL; ig = gl.next() )
|
|
if (ig->name == grouptag)
|
|
{
|
|
curGroup = ig;
|
|
return ig;
|
|
}
|
|
|
|
/* couldn't find an existing group, create a new one if create is 1*/
|
|
if (create)
|
|
{
|
|
if (grouptag.isEmpty())
|
|
grouptag = "Group_1";
|
|
|
|
curGroup = new INDI_G(this, grouptag);
|
|
gl.append(curGroup);
|
|
return curGroup;
|
|
}
|
|
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: group %.64s not found in %.64s", grouptag.ascii(), name.ascii());
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* find "perm" attribute in root, crack and set *pp.
|
|
* return 0 if ok else -1 with excuse in errmsg[]
|
|
*/
|
|
|
|
int INDI_D::findPerm (INDI_P *pp, XMLEle *root, PPerm *permp, char errmsg[])
|
|
{
|
|
XMLAtt *ap;
|
|
|
|
ap = findXMLAtt(root, "perm");
|
|
if (!ap) {
|
|
snprintf (errmsg, ERRMSG_SIZE,"INDI: <%.64s %.64s %.64s> missing attribute 'perm'",
|
|
tagXMLEle(root), pp->pg->dp->name.ascii(), pp->name.ascii());
|
|
return (-1);
|
|
}
|
|
if (!strcmp(valuXMLAtt(ap), "ro") || !strcmp(valuXMLAtt(ap), "r"))
|
|
*permp = PP_RO;
|
|
else if (!strcmp(valuXMLAtt(ap), "wo"))
|
|
*permp = PP_WO;
|
|
else if (!strcmp(valuXMLAtt(ap), "rw") || !strcmp(valuXMLAtt(ap), "w"))
|
|
*permp = PP_RW;
|
|
else {
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown perm %.64s for %.64s %.64s",
|
|
tagXMLEle(root), valuXMLAtt(ap), pp->pg->dp->name.ascii(), pp->name.ascii());
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* convert the given light/property state string to the PState at psp.
|
|
* return 0 if successful, else -1 and leave *psp unchanged.
|
|
*/
|
|
int INDI_D::crackLightState (char *name, PState *psp)
|
|
{
|
|
typedef struct
|
|
{
|
|
PState s;
|
|
const char *name;
|
|
} PSMap;
|
|
|
|
PSMap psmap[] =
|
|
{
|
|
{PS_IDLE, "Idle"},
|
|
{PS_OK, "Ok"},
|
|
{PS_BUSY, "Busy"},
|
|
{PS_ALERT, "Alert"},
|
|
};
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
if (!strcmp (psmap[i].name, name)) {
|
|
*psp = psmap[i].s;
|
|
return (0);
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
/* convert the given switch state string to the PState at psp.
|
|
* return 0 if successful, else -1 and leave *psp unchanged.
|
|
*/
|
|
int INDI_D::crackSwitchState (char *name, PState *psp)
|
|
{
|
|
typedef struct
|
|
{
|
|
PState s;
|
|
const char *name;
|
|
} PSMap;
|
|
|
|
PSMap psmap[] =
|
|
{
|
|
{PS_ON, "On"},
|
|
{PS_OFF, "Off"},
|
|
};
|
|
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
if (!strcmp (psmap[i].name, name))
|
|
{
|
|
*psp = psmap[i].s;
|
|
return (0);
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
int INDI_D::buildTextGUI(XMLEle *root, char errmsg[])
|
|
{
|
|
INDI_P *pp = NULL;
|
|
PPerm p;
|
|
|
|
/* build a new property */
|
|
pp = addProperty (root, errmsg);
|
|
|
|
if (pp == NULL)
|
|
return (-1);
|
|
|
|
/* get the permission, it will determine tqlayout issues */
|
|
if (findPerm (pp, root, &p, errmsg))
|
|
{
|
|
delete(pp);
|
|
return (-1);
|
|
}
|
|
|
|
/* we know it will be a general text GUI */
|
|
pp->guitype = PG_TEXT;
|
|
pp->perm = p;
|
|
|
|
if (pp->buildTextGUI(root, errmsg) < 0)
|
|
{
|
|
delete (pp);
|
|
return (-1);
|
|
}
|
|
|
|
pp->pg->addProperty(pp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* build GUI for a number property.
|
|
* return 0 if ok, else -1 with reason in errmsg[]
|
|
*/
|
|
int INDI_D::buildNumberGUI (XMLEle *root, char *errmsg)
|
|
{
|
|
INDI_P *pp = NULL;
|
|
PPerm p;
|
|
|
|
/* build a new property */
|
|
pp = addProperty (root, errmsg);
|
|
|
|
if (pp == NULL)
|
|
return (-1);
|
|
|
|
/* get the permission, it will determine tqlayout issues */
|
|
if (findPerm (pp, root, &p, errmsg))
|
|
{
|
|
delete(pp);
|
|
return (-1);
|
|
}
|
|
|
|
/* we know it will be a number GUI */
|
|
pp->guitype = PG_NUMERIC;
|
|
pp->perm = p;
|
|
|
|
if (pp->buildNumberGUI(root, errmsg) < 0)
|
|
{
|
|
delete (pp);
|
|
return (-1);
|
|
}
|
|
|
|
pp->pg->addProperty(pp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* build GUI for switches property.
|
|
* rule and number of will determine exactly how the GUI is built.
|
|
* return 0 if ok, else -1 with reason in errmsg[]
|
|
*/
|
|
int INDI_D::buildSwitchesGUI (XMLEle *root, char errmsg[])
|
|
{
|
|
INDI_P *pp;
|
|
XMLAtt *ap;
|
|
XMLEle *ep;
|
|
int n, err;
|
|
|
|
/* build a new property */
|
|
pp = addProperty (root, errmsg);
|
|
if (!pp)
|
|
return (-1);
|
|
|
|
ap = findAtt (root, "rule", errmsg);
|
|
if (!ap)
|
|
{
|
|
delete(pp);
|
|
return (-1);
|
|
}
|
|
|
|
/* decide GUI. might use MENU if OneOf but too many for button array */
|
|
if (!strcmp (valuXMLAtt(ap), "OneOfMany") || !strcmp (valuXMLAtt(ap), "AtMostOne"))
|
|
{
|
|
/* count number of switches -- make menu if too many */
|
|
for ( ep = nextXMLEle(root, 1) , n = 0 ; ep != NULL; ep = nextXMLEle(root, 0))
|
|
if (!strcmp (tagXMLEle(ep), "defSwitch"))
|
|
n++;
|
|
|
|
if (n > MAXRADIO)
|
|
{
|
|
pp->guitype = PG_MENU;
|
|
err = pp->buildMenuGUI (root, errmsg);
|
|
if (err < 0)
|
|
{
|
|
delete(pp);
|
|
pp=0;
|
|
return err;
|
|
}
|
|
|
|
pp->pg->addProperty(pp);
|
|
return (err);
|
|
}
|
|
|
|
/* otherwise, build 1-4 button tqlayout */
|
|
pp->guitype = PG_BUTTONS;
|
|
|
|
err = pp->buildSwitchesGUI(root, errmsg);
|
|
if (err < 0)
|
|
{
|
|
delete (pp);
|
|
pp=0;
|
|
return err;
|
|
}
|
|
|
|
pp->pg->addProperty(pp);
|
|
return (err);
|
|
|
|
}
|
|
else if (!strcmp (valuXMLAtt(ap), "AnyOfMany"))
|
|
{
|
|
/* 1-4 checkboxes tqlayout */
|
|
pp->guitype = PG_RADIO;
|
|
|
|
err = pp->buildSwitchesGUI(root, errmsg);
|
|
if (err < 0)
|
|
{
|
|
delete (pp);
|
|
pp=0;
|
|
return err;
|
|
}
|
|
|
|
pp->pg->addProperty(pp);
|
|
return (err);
|
|
}
|
|
|
|
snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown rule %.64s for %.64s %.64s",
|
|
tagXMLEle(root), valuXMLAtt(ap), name.ascii(), pp->name.ascii());
|
|
|
|
delete(pp);
|
|
return (-1);
|
|
}
|
|
|
|
|
|
|
|
/* build GUI for a lights GUI.
|
|
* return 0 if ok, else -1 with reason in errmsg[] */
|
|
int INDI_D::buildLightsGUI (XMLEle *root, char errmsg[])
|
|
{
|
|
INDI_P *pp;
|
|
|
|
// build a new property
|
|
pp = addProperty (root, errmsg);
|
|
if (!pp)
|
|
return (-1);
|
|
|
|
pp->guitype = PG_LIGHTS;
|
|
|
|
if (pp->buildLightsGUI(root, errmsg) < 0)
|
|
{
|
|
delete (pp);
|
|
return (-1);
|
|
}
|
|
|
|
pp->pg->addProperty(pp);
|
|
return (0);
|
|
}
|
|
|
|
/* build GUI for a BLOB GUI.
|
|
* return 0 if ok, else -1 with reason in errmsg[] */
|
|
int INDI_D::buildBLOBGUI (XMLEle *root, char errmsg[])
|
|
{
|
|
INDI_P *pp;
|
|
PPerm p;
|
|
|
|
// build a new property
|
|
pp = addProperty (root, errmsg);
|
|
if (!pp)
|
|
return (-1);
|
|
|
|
/* get the permission, it will determine tqlayout issues */
|
|
if (findPerm (pp, root, &p, errmsg))
|
|
{
|
|
delete(pp);
|
|
return (-1);
|
|
}
|
|
|
|
/* we know it will be a number GUI */
|
|
pp->perm = p;
|
|
pp->guitype = PG_BLOB;
|
|
|
|
if (pp->buildBLOBGUI(root, errmsg) < 0)
|
|
{
|
|
delete (pp);
|
|
return (-1);
|
|
}
|
|
|
|
pp->pg->addProperty(pp);
|
|
return (0);
|
|
}
|
|
|
|
INDI_E * INDI_D::findElem(TQString name)
|
|
{
|
|
INDI_G *grp;
|
|
INDI_P *prop;
|
|
INDI_E *el;
|
|
|
|
for (grp = gl.first(); grp != NULL; grp = gl.next())
|
|
{
|
|
for (prop = grp->pl.first(); prop != NULL; prop = grp->pl.next())
|
|
{
|
|
el = prop->findElement(name);
|
|
if (el != NULL) return el;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
#include "indidevice.moc"
|