File indexing completed on 2024-03-24 03:46:48
0001 /* 0002 SPDX-FileCopyrightText: 2003 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "devicemanager.h" 0008 0009 #include "Options.h" 0010 #include "indimenu.h" 0011 #include "indiproperty.h" 0012 #include "indigroup.h" 0013 #include "indidevice.h" 0014 #include "indistd.h" 0015 #include "indidriver.h" 0016 #include "kstars.h" 0017 #include "kstarsdatetime.h" 0018 0019 #include <indicom.h> 0020 0021 #include <config-kstars.h> 0022 0023 //#include <stdlib.h> 0024 //#include <unistd.h> 0025 0026 #include <QTcpSocket> 0027 #include <QTextEdit> 0028 0029 #include <KProcess> 0030 #include <KLocale> 0031 #include <KDebug> 0032 #include <KMessageBox> 0033 #include <KStatusBar> 0034 0035 const int INDI_MAX_TRIES = 3; 0036 0037 /******************************************************************* 0038 ** The device manager contain devices running from one indiserver 0039 ** This allow KStars to control multiple devices distributed across 0040 ** multiple servers seemingly in a way that is completely transparent 0041 ** to devices and drivers alike. 0042 ** The device Manager can be thought of as the 'networking' parent 0043 ** of devices, while indimenu is 'GUI' parent of devices 0044 *******************************************************************/ 0045 DeviceManager::DeviceManager(INDIMenu *INDIparent, QString inHost, uint inPort, ManagerMode inMode) 0046 { 0047 parent = INDIparent; 0048 serverProcess = nullptr; 0049 XMLParser = nullptr; 0050 host = inHost; 0051 port = inPort; 0052 mode = inMode; 0053 } 0054 0055 DeviceManager::~DeviceManager() 0056 { 0057 serverSocket.close(); 0058 0059 if (serverProcess) 0060 serverProcess->close(); 0061 0062 delete (serverProcess); 0063 0064 if (XMLParser) 0065 delLilXML(XMLParser); 0066 0067 XMLParser = nullptr; 0068 0069 while (!indi_dev.isEmpty()) 0070 delete indi_dev.takeFirst(); 0071 } 0072 0073 void DeviceManager::startServer() 0074 { 0075 serverProcess = new KProcess; 0076 0077 if (managed_devices.isEmpty()) 0078 { 0079 kWarning() << "managed_devices was not set! Cannot start server!"; 0080 return; 0081 } 0082 #ifdef Q_OS_OSX 0083 if (Options::indiServerIsInternal()) 0084 *serverProcess << QCoreApplication::applicationDirPath() + "/indiserver"; 0085 else 0086 #endif 0087 *serverProcess << Options::indiServer(); 0088 *serverProcess << "-v" 0089 << "-p" << QString::number(port); 0090 0091 foreach (IDevice *device, managed_devices) 0092 { 0093 // JM: Temporary workaround for indiserver limit of client BLOBs for CCDs. 0094 if (device->type == KSTARS_CCD) 0095 { 0096 *serverProcess << "-m" 0097 << "100"; 0098 break; 0099 } 0100 } 0101 0102 foreach (IDevice *device, managed_devices) 0103 *serverProcess << device->driver; 0104 0105 if (mode == DeviceManager::M_LOCAL) 0106 { 0107 connect(serverProcess, SIGNAL(readyReadStandardError()), this, SLOT(processStandardError())); 0108 serverProcess->setOutputChannelMode(KProcess::SeparateChannels); 0109 serverProcess->setReadChannel(QProcess::StandardError); 0110 } 0111 0112 serverProcess->start(); 0113 0114 serverProcess->waitForStarted(); 0115 0116 if (mode == DeviceManager::M_LOCAL) 0117 connectToServer(); 0118 } 0119 0120 void DeviceManager::stopServer() 0121 { 0122 serverProcess->terminate(); 0123 } 0124 0125 void DeviceManager::connectToServer() 0126 { 0127 connect(&serverSocket, SIGNAL(readyRead()), this, SLOT(dataReceived())); 0128 0129 for (int i = 0; i < INDI_MAX_TRIES; i++) 0130 { 0131 serverSocket.connectToHost(host, port); 0132 if (serverSocket.waitForConnected(1000)) 0133 { 0134 connect(&serverSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connectionError())); 0135 connectionSuccess(); 0136 return; 0137 } 0138 0139 usleep(100000); 0140 } 0141 0142 connectionError(); 0143 } 0144 0145 void DeviceManager::enableBLOB(bool enable, QString device, QString property) 0146 { 0147 QTextStream serverFP(&serverSocket); 0148 QString openTag; 0149 0150 if (device.isEmpty()) 0151 return; 0152 0153 if (property.isEmpty() == false) 0154 openTag = QString("<enableBLOB device='%1' name='%2'>").arg(device).arg(property); 0155 else 0156 openTag = QString("<enableBLOB device='%1'>").arg(device); 0157 0158 if (enable) 0159 { 0160 serverFP << QString("%1Also</enableBLOB>\n").arg(openTag); 0161 kDebug() << QString("%1Also</enableBLOB>\n").arg(openTag); 0162 } 0163 else 0164 { 0165 serverFP << QString("%1Never</enableBLOB>\n").arg(openTag); 0166 kDebug() << QString("%1Never</enableBLOB>\n").arg(openTag); 0167 } 0168 } 0169 0170 void DeviceManager::connectionSuccess() 0171 { 0172 QTextStream serverFP(&serverSocket); 0173 0174 if (XMLParser) 0175 delLilXML(XMLParser); 0176 XMLParser = newLilXML(); 0177 0178 serverFP << QString("<getProperties version='%1'/>\n").arg(INDIVERSION); 0179 } 0180 0181 void DeviceManager::connectionError() 0182 { 0183 QString errMsg = QString("Connection to INDI host at %1 on port %2 encountered an error: %3.") 0184 .arg(host) 0185 .arg(port) 0186 .arg(serverSocket.errorString()); 0187 KMessageBox::error(nullptr, errMsg); 0188 0189 emit deviceManagerError(this); 0190 } 0191 0192 void DeviceManager::appendManagedDevices(QList<IDevice *> &processed_devices) 0193 { 0194 managed_devices = processed_devices; 0195 0196 foreach (IDevice *device, managed_devices) 0197 { 0198 device->unique_label = parent->getUniqueDeviceLabel(device->tree_label); 0199 //device->mode = mode; 0200 device->deviceManager = this; 0201 } 0202 } 0203 0204 void DeviceManager::processStandardError() 0205 { 0206 if (serverProcess == nullptr) 0207 return; 0208 0209 serverBuffer.append(serverProcess->readAllStandardError()); 0210 emit newServerInput(); 0211 } 0212 0213 void DeviceManager::dataReceived() 0214 { 0215 char errmsg[ERRMSG_SIZE]; 0216 int nr = 0, err_code = 0; 0217 QTextStream serverFP(&serverSocket); 0218 QString ibuf, err_cmd; 0219 0220 ibuf = serverFP.readAll(); 0221 nr = ibuf.length(); 0222 0223 /* process each char */ 0224 for (int i = 0; i < nr; i++) 0225 { 0226 if (!XMLParser) 0227 return; 0228 0229 XMLEle *root = readXMLEle(XMLParser, ibuf[i].toAscii(), errmsg); 0230 if (root) 0231 { 0232 if ((err_code = dispatchCommand(root, err_cmd)) < 0) 0233 { 0234 // Silently ignore property duplication errors 0235 if (err_code != INDI_PROPERTY_DUPLICATED) 0236 { 0237 //kDebug() << "Dispatch command error: " << err_cmd << endl; 0238 fprintf(stderr, "Dispatch command error: %d for command %s\n", err_code, qPrintable(err_cmd)); 0239 prXMLEle(stderr, root, 0); 0240 } 0241 } 0242 0243 delXMLEle(root); 0244 } 0245 else if (*errmsg) 0246 { 0247 kDebug() << "XML Root Error: " << errmsg; 0248 } 0249 } 0250 } 0251 0252 int DeviceManager::dispatchCommand(XMLEle *root, QString &errmsg) 0253 { 0254 if (!strcmp(tagXMLEle(root), "message")) 0255 return messageCmd(root, errmsg); 0256 else if (!strcmp(tagXMLEle(root), "delProperty")) 0257 return delPropertyCmd(root, errmsg); 0258 0259 /* Get the device, if not available, create it */ 0260 INDI_D *dp = findDev(root, 1, errmsg); 0261 0262 if (dp == nullptr) 0263 { 0264 errmsg = "No device available and none was created"; 0265 return INDI_DEVICE_NOT_FOUND; 0266 } 0267 0268 if (!strcmp(tagXMLEle(root), "defTextVector")) 0269 return dp->buildTextGUI(root, errmsg); 0270 else if (!strcmp(tagXMLEle(root), "defNumberVector")) 0271 return dp->buildNumberGUI(root, errmsg); 0272 else if (!strcmp(tagXMLEle(root), "defSwitchVector")) 0273 return dp->buildSwitchesGUI(root, errmsg); 0274 else if (!strcmp(tagXMLEle(root), "defLightVector")) 0275 return dp->buildLightsGUI(root, errmsg); 0276 else if (!strcmp(tagXMLEle(root), "defBLOBVector")) 0277 return dp->buildBLOBGUI(root, errmsg); 0278 else if (!strcmp(tagXMLEle(root), "setTextVector") || !strcmp(tagXMLEle(root), "setNumberVector") || 0279 !strcmp(tagXMLEle(root), "setSwitchVector") || !strcmp(tagXMLEle(root), "setLightVector") || 0280 !strcmp(tagXMLEle(root), "setBLOBVector")) 0281 return dp->setAnyCmd(root, errmsg); 0282 // Ignore if we get NewXXX commands 0283 else if (QString(tagXMLEle(root)).startsWith("new")) 0284 return 0; 0285 0286 return INDI_DISPATCH_ERROR; 0287 } 0288 0289 /* delete the property in the given device, including widgets and data structs. 0290 * when last property is deleted, delete the device too. 0291 * if no property name attribute at all, delete the whole device regardless. 0292 * return 0 if ok, else -1 with reason in errmsg[]. 0293 */ 0294 int DeviceManager::delPropertyCmd(XMLEle *root, QString &errmsg) 0295 { 0296 XMLAtt *ap; 0297 INDI_D *dp; 0298 INDI_P *pp; 0299 0300 /* dig out device and optional property name */ 0301 dp = findDev(root, 0, errmsg); 0302 if (!dp) 0303 return INDI_DEVICE_NOT_FOUND; 0304 0305 checkMsg(root, dp); 0306 0307 ap = findXMLAtt(root, "name"); 0308 0309 /* Delete property if it exists, otherwise, delete the whole device */ 0310 if (ap) 0311 { 0312 pp = dp->findProp(QString(valuXMLAtt(ap))); 0313 0314 if (pp) 0315 return dp->removeProperty(pp); 0316 else 0317 return INDI_PROPERTY_INVALID; 0318 } 0319 // delete the whole device 0320 else 0321 return removeDevice(dp->name, errmsg); 0322 } 0323 0324 int DeviceManager::removeDevice(const QString &devName, QString &errmsg) 0325 { 0326 // remove all devices if devName == nullptr 0327 if (devName == nullptr) 0328 { 0329 while (!indi_dev.isEmpty()) 0330 delete indi_dev.takeFirst(); 0331 return (0); 0332 } 0333 0334 for (int i = 0; i < indi_dev.size(); i++) 0335 { 0336 if (indi_dev[i]->name == devName) 0337 { 0338 delete indi_dev.takeAt(i); 0339 return (0); 0340 } 0341 } 0342 0343 errmsg = QString("Device %1 not found").arg(devName); 0344 return INDI_DEVICE_NOT_FOUND; 0345 } 0346 0347 INDI_D *DeviceManager::findDev(const QString &devName, QString &errmsg) 0348 { 0349 /* search for existing */ 0350 for (int i = 0; i < indi_dev.size(); i++) 0351 { 0352 if (indi_dev[i]->name == devName) 0353 return indi_dev[i]; 0354 } 0355 0356 errmsg = QString("INDI: no such device %1").arg(devName); 0357 0358 return nullptr; 0359 } 0360 0361 /* add new device to mainrc_w using info in dep. 0362 - * if trouble return nullptr with reason in errmsg[] 0363 - */ 0364 INDI_D *DeviceManager::addDevice(XMLEle *dep, QString &errmsg) 0365 { 0366 INDI_D *dp; 0367 XMLAtt *ap; 0368 QString device_name, unique_label; 0369 IDevice *targetDevice = nullptr; 0370 0371 /* allocate new INDI_D on indi_dev */ 0372 ap = findAtt(dep, "device", errmsg); 0373 if (!ap) 0374 { 0375 errmsg = QString("Unable to find device attribute in XML tree. Cannot add device."); 0376 kDebug() << errmsg << endl; 0377 return nullptr; 0378 } 0379 0380 device_name = QString(valuXMLAtt(ap)); 0381 0382 if (mode != M_CLIENT) 0383 foreach (IDevice *device, managed_devices) 0384 { 0385 // Each device manager has a list of managed_devices (IDevice). Each IDevice has the original constant name of the driver (driver_class) 0386 // Therefore, when a new device is discovered, we match the driver name (which never changes, it's always static from indiserver) against the driver_class 0387 // of IDevice because IDevice can have several names. It can have the tree_label which is the name it has in the local tree widget. Finally, the name that shows 0388 // up in the INDI control panel is the unique name of the driver, which is for most cases tree_label, but if that exists already then we get tree_label_1..etc 0389 0390 if (device->name == device_name && device->state == IDevice::DEV_TERMINATE) 0391 { 0392 device->state = IDevice::DEV_START; 0393 unique_label = device->unique_label = parent->getUniqueDeviceLabel(device->tree_label); 0394 targetDevice = device; 0395 break; 0396 } 0397 } 0398 0399 // For remote INDI drivers with no label attributes 0400 if (unique_label.isEmpty()) 0401 unique_label = parent->getUniqueDeviceLabel(device_name); 0402 0403 dp = new INDI_D(parent, this, device_name, unique_label, targetDevice); 0404 indi_dev.append(dp); 0405 emit newDevice(dp); 0406 0407 enableBLOB(true, device_name); 0408 0409 connect(dp->stdDev, SIGNAL(newTelescope()), parent->ksw->indiDriver(), SLOT(newTelescopeDiscovered()), 0410 Qt::QueuedConnection); 0411 0412 /* ok */ 0413 return dp; 0414 } 0415 0416 INDI_D *DeviceManager::findDev(XMLEle *root, int create, QString &errmsg) 0417 { 0418 XMLAtt *ap = findAtt(root, "device", errmsg); 0419 char *dn = nullptr; 0420 0421 /* get device name */ 0422 if (!ap) 0423 { 0424 errmsg = QString("No device attribute found in element %1").arg(tagXMLEle(root)); 0425 return nullptr; 0426 } 0427 dn = valuXMLAtt(ap); 0428 0429 /* search for existing */ 0430 for (int i = 0; i < indi_dev.size(); i++) 0431 { 0432 if (indi_dev[i]->name == QString(dn)) 0433 return indi_dev[i]; 0434 } 0435 0436 /* not found, create if ok */ 0437 if (create) 0438 return (addDevice(root, errmsg)); 0439 0440 errmsg = QString("INDI: <%1> no such device %2").arg(tagXMLEle(root)).arg(dn); 0441 return nullptr; 0442 } 0443 0444 /* a general message command received from the device. 0445 * return 0 if ok, else -1 with reason in errmsg[]. 0446 */ 0447 int DeviceManager::messageCmd(XMLEle *root, QString &errmsg) 0448 { 0449 checkMsg(root, findDev(root, 0, errmsg)); 0450 return (0); 0451 } 0452 0453 /* display message attribute. 0454 * N.B. don't put carriage control in msg, we take care of that. 0455 */ 0456 void DeviceManager::checkMsg(XMLEle *root, INDI_D *dp) 0457 { 0458 XMLAtt *ap; 0459 ap = findXMLAtt(root, "message"); 0460 0461 if (ap) 0462 doMsg(root, dp); 0463 } 0464 0465 /* display valu of message and timestamp in dp's scrolled area, if any, else general. 0466 * prefix our time stamp if not included. 0467 * N.B. don't put carriage control in msg, we take care of that. 0468 */ 0469 void DeviceManager::doMsg(XMLEle *msg, INDI_D *dp) 0470 { 0471 QTextEdit *txt_w; 0472 XMLAtt *message; 0473 XMLAtt *timestamp; 0474 0475 if (dp == nullptr) 0476 { 0477 kDebug() << "Warning: dp is null."; 0478 return; 0479 } 0480 0481 txt_w = dp->msgST_w; 0482 0483 /* prefix our timestamp if not with msg */ 0484 timestamp = findXMLAtt(msg, "timestamp"); 0485 0486 if (timestamp) 0487 txt_w->insertPlainText(QString(valuXMLAtt(timestamp)) + QString(" ")); 0488 else 0489 txt_w->insertPlainText(KStarsDateTime::currentDateTime().toString("yyyy/mm/dd - h:m:s ap ")); 0490 0491 /* finally! the msg */ 0492 message = findXMLAtt(msg, "message"); 0493 0494 if (!message) 0495 return; 0496 0497 // Prepend to the log viewer 0498 txt_w->insertPlainText(QString(valuXMLAtt(message)) + QString("\n")); 0499 QTextCursor c = txt_w->textCursor(); 0500 c.movePosition(QTextCursor::Start); 0501 txt_w->setTextCursor(c); 0502 0503 if (Options::showINDIMessages()) 0504 parent->ksw->statusBar()->changeItem(QString(valuXMLAtt(message)), 0); 0505 } 0506 0507 void DeviceManager::sendNewText(INDI_P *pp) 0508 { 0509 INDI_E *lp; 0510 0511 QTextStream serverFP(&serverSocket); 0512 0513 serverFP << QString("<newTextVector\n"); 0514 serverFP << QString(" device='%1'\n").arg(qPrintable(pp->pg->dp->name)); 0515 serverFP << QString(" name='%1'\n>").arg(qPrintable(pp->name)); 0516 0517 //for (lp = pp->el.first(); lp != nullptr; lp = pp->el.next()) 0518 foreach (lp, pp->el) 0519 { 0520 serverFP << QString(" <oneText\n"); 0521 serverFP << QString(" name='%1'>\n").arg(qPrintable(lp->name)); 0522 serverFP << QString(" %1\n").arg(qPrintable(lp->text)); 0523 serverFP << QString(" </oneText>\n"); 0524 } 0525 serverFP << QString("</newTextVector>\n"); 0526 } 0527 0528 void DeviceManager::sendNewNumber(INDI_P *pp) 0529 { 0530 INDI_E *lp; 0531 0532 QTextStream serverFP(&serverSocket); 0533 0534 serverFP << QString("<newNumberVector\n"); 0535 serverFP << QString(" device='%1'\n").arg(qPrintable(pp->pg->dp->name)); 0536 serverFP << QString(" name='%1'\n>").arg(qPrintable(pp->name)); 0537 0538 foreach (lp, pp->el) 0539 { 0540 serverFP << QString(" <oneNumber\n"); 0541 serverFP << QString(" name='%1'>\n").arg(qPrintable(lp->name)); 0542 if (lp->text.isEmpty() || lp->spin_w) 0543 serverFP << QString(" %1\n").arg(lp->targetValue); 0544 else 0545 serverFP << QString(" %1\n").arg(lp->text); 0546 serverFP << QString(" </oneNumber>\n"); 0547 } 0548 serverFP << QString("</newNumberVector>\n"); 0549 } 0550 0551 void DeviceManager::sendNewSwitch(INDI_P *pp, INDI_E *lp) 0552 { 0553 QTextStream serverFP(&serverSocket); 0554 0555 serverFP << QString("<newSwitchVector\n"); 0556 serverFP << QString(" device='%1'\n").arg(qPrintable(pp->pg->dp->name)); 0557 serverFP << QString(" name='%1'>\n").arg(qPrintable(pp->name)); 0558 serverFP << QString(" <oneSwitch\n"); 0559 serverFP << QString(" name='%1'>\n").arg(qPrintable(lp->name)); 0560 serverFP << QString(" %1\n").arg(lp->switch_state == ISS_ON ? "On" : "Off"); 0561 serverFP << QString(" </oneSwitch>\n"); 0562 0563 serverFP << QString("</newSwitchVector>\n"); 0564 } 0565 0566 void DeviceManager::startBlob(const QString &devName, const QString &propName, const QString ×tamp) 0567 { 0568 QTextStream serverFP(&serverSocket); 0569 0570 serverFP << QString("<newBLOBVector\n"); 0571 serverFP << QString(" device='%1'\n").arg(qPrintable(devName)); 0572 serverFP << QString(" name='%1'\n").arg(qPrintable(propName)); 0573 serverFP << QString(" timestamp='%1'>\n").arg(qPrintable(timestamp)); 0574 } 0575 0576 void DeviceManager::sendOneBlob(const QString &blobName, unsigned int blobSize, const QString &blobFormat, 0577 unsigned char *blobBuffer) 0578 { 0579 QTextStream serverFP(&serverSocket); 0580 0581 serverFP << QString(" <oneBLOB\n"); 0582 serverFP << QString(" name='%1'\n").arg(qPrintable(blobName)); 0583 serverFP << QString(" size='%1'\n").arg(blobSize); 0584 serverFP << QString(" format='%1'>\n").arg(qPrintable(blobFormat)); 0585 0586 for (unsigned i = 0; i < blobSize; i += 72) 0587 serverFP << QString().sprintf(" %.72s\n", blobBuffer + i); 0588 0589 serverFP << QString(" </oneBLOB>\n"); 0590 } 0591 0592 void DeviceManager::finishBlob() 0593 { 0594 QTextStream serverFP(&serverSocket); 0595 0596 serverFP << QString("</newBLOBVector>\n"); 0597 } 0598 0599 #include "devicemanager.moc"