File indexing completed on 2024-04-28 07:31:51
0001 /* 0002 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 Handle INDI Standard properties. 0007 */ 0008 0009 #include "indistd.h" 0010 0011 #include "clientmanager.h" 0012 #include "driverinfo.h" 0013 #include "deviceinfo.h" 0014 #include "imageviewer.h" 0015 #include "indi_debug.h" 0016 #include "kstars.h" 0017 #include "kstarsdata.h" 0018 #include "Options.h" 0019 #include "skymap.h" 0020 0021 #include "indimount.h" 0022 #include "indicamera.h" 0023 #include "indiguider.h" 0024 #include "indifocuser.h" 0025 #include "indifilterwheel.h" 0026 #include "indidome.h" 0027 #include "indigps.h" 0028 #include "indiweather.h" 0029 #include "indiadaptiveoptics.h" 0030 #include "indidustcap.h" 0031 #include "indilightbox.h" 0032 #include "indidetector.h" 0033 #include "indirotator.h" 0034 #include "indispectrograph.h" 0035 #include "indicorrelator.h" 0036 #include "indiauxiliary.h" 0037 0038 #include "genericdeviceadaptor.h" 0039 #include <indicom.h> 0040 #include <QImageReader> 0041 #include <QStatusBar> 0042 0043 namespace ISD 0044 { 0045 0046 GDSetCommand::GDSetCommand(INDI_PROPERTY_TYPE inPropertyType, const QString &inProperty, const QString &inElement, 0047 QVariant qValue, QObject *parent) 0048 : QObject(parent), propType(inPropertyType), indiProperty((inProperty)), indiElement(inElement), elementValue(qValue) 0049 { 0050 } 0051 0052 uint8_t GenericDevice::m_ID = 1; 0053 GenericDevice::GenericDevice(DeviceInfo &idv, ClientManager *cm, QObject *parent) : GDInterface(parent) 0054 { 0055 // Register DBus 0056 new GenericDeviceAdaptor(this); 0057 QDBusConnection::sessionBus().registerObject(QString("/KStars/INDI/GenericDevice/%1").arg(getID()), this); 0058 0059 m_DeviceInfo = &idv; 0060 m_DriverInfo = idv.getDriverInfo(); 0061 m_BaseDevice = idv.getBaseDevice(); 0062 m_ClientManager = cm; 0063 0064 Q_ASSERT_X(m_BaseDevice, __FUNCTION__, "Base device is invalid."); 0065 Q_ASSERT_X(m_ClientManager, __FUNCTION__, "Client manager is invalid."); 0066 0067 m_Name = m_BaseDevice.getDeviceName(); 0068 0069 setObjectName(m_Name); 0070 0071 m_DriverInterface = m_BaseDevice.getDriverInterface(); 0072 m_DriverVersion = m_BaseDevice.getDriverVersion(); 0073 0074 registerDBusType(); 0075 0076 // JM 2020-09-05: In case KStars time change, update driver time if applicable. 0077 connect(KStarsData::Instance()->clock(), &SimClock::timeChanged, this, [this]() 0078 { 0079 if (Options::useTimeUpdate() && Options::useKStarsSource()) 0080 { 0081 if (isConnected()) 0082 { 0083 auto tvp = m_BaseDevice.getText("TIME_UTC"); 0084 if (tvp && tvp.getPermission() != IP_RO) 0085 updateTime(); 0086 } 0087 } 0088 }); 0089 0090 m_ReadyTimer = new QTimer(this); 0091 m_ReadyTimer->setInterval(250); 0092 m_ReadyTimer->setSingleShot(true); 0093 connect(m_ReadyTimer, &QTimer::timeout, this, &GenericDevice::handleTimeout, Qt::UniqueConnection); 0094 0095 m_TimeUpdateTimer = new QTimer(this); 0096 m_TimeUpdateTimer->setInterval(5000); 0097 m_TimeUpdateTimer->setSingleShot(true); 0098 connect(m_TimeUpdateTimer, &QTimer::timeout, this, &GenericDevice::checkTimeUpdate, Qt::UniqueConnection); 0099 0100 m_LocationUpdateTimer = new QTimer(this); 0101 m_LocationUpdateTimer->setInterval(5000); 0102 m_LocationUpdateTimer->setSingleShot(true); 0103 connect(m_LocationUpdateTimer, &QTimer::timeout, this, &GenericDevice::checkLocationUpdate, Qt::UniqueConnection); 0104 0105 } 0106 0107 GenericDevice::~GenericDevice() 0108 { 0109 for (auto &metadata : streamFileMetadata) 0110 metadata.file->close(); 0111 } 0112 0113 void GenericDevice::handleTimeout() 0114 { 0115 generateDevices(); 0116 // N.B. JM 2022.10.15: Do not disconnect timer. 0117 // It is possible that other properties can come later. 0118 // Even they do not make it in the 250ms window. Increasing timeout value alone 0119 // to 1000ms or more would improve the situation but is not sufficient to account for 0120 // unexpected delays. Therefore, the best solution is to keep the timer active. 0121 //m_ReadyTimer->disconnect(this); 0122 m_Ready = true; 0123 emit ready(); 0124 } 0125 0126 void GenericDevice::checkTimeUpdate() 0127 { 0128 auto tvp = getProperty("TIME_UTC"); 0129 if (tvp) 0130 { 0131 auto timeTP = tvp.getText(); 0132 // If time still empty, then force update. 0133 if (timeTP && timeTP->getPermission() != IP_RO && timeTP->getState() == IPS_IDLE) 0134 updateTime(); 0135 } 0136 0137 } 0138 0139 void GenericDevice::checkLocationUpdate() 0140 { 0141 auto nvp = getProperty("GEOGRAPHIC_COORD"); 0142 if (nvp) 0143 { 0144 auto locationNP = nvp.getNumber(); 0145 // If time still empty, then force update. 0146 if (locationNP && locationNP->getPermission() != IP_RO && locationNP->getState() == IPS_IDLE) 0147 updateLocation(); 0148 } 0149 } 0150 0151 void GenericDevice::registerDBusType() 0152 { 0153 #ifndef KSTARS_LITE 0154 static bool isRegistered = false; 0155 0156 if (isRegistered == false) 0157 { 0158 qRegisterMetaType<ISD::ParkStatus>("ISD::ParkStatus"); 0159 qDBusRegisterMetaType<ISD::ParkStatus>(); 0160 isRegistered = true; 0161 } 0162 #endif 0163 } 0164 0165 const QString &GenericDevice::getDeviceName() const 0166 { 0167 return m_Name; 0168 } 0169 0170 void GenericDevice::registerProperty(INDI::Property prop) 0171 { 0172 if (!prop.getRegistered()) 0173 return; 0174 0175 m_ReadyTimer->start(); 0176 0177 const QString name = prop.getName(); 0178 0179 // In case driver already started 0180 if (name == "CONNECTION") 0181 { 0182 auto svp = prop.getSwitch(); 0183 0184 // Still connecting/disconnecting... 0185 if (!svp || svp->getState() == IPS_BUSY) 0186 return; 0187 0188 auto conSP = svp->findWidgetByName("CONNECT"); 0189 0190 if (!conSP) 0191 return; 0192 0193 if (m_Connected == false && svp->getState() == IPS_OK && conSP->getState() == ISS_ON) 0194 { 0195 m_Connected = true; 0196 emit Connected(); 0197 createDeviceInit(); 0198 } 0199 else if (m_Connected && conSP->getState() == ISS_OFF) 0200 { 0201 m_Connected = false; 0202 emit Disconnected(); 0203 } 0204 else 0205 m_Ready = (svp->s == IPS_OK && conSP->s == ISS_ON); 0206 } 0207 else if (name == "DRIVER_INFO") 0208 { 0209 auto tvp = prop.getText(); 0210 if (tvp) 0211 { 0212 auto tp = tvp->findWidgetByName("DRIVER_INTERFACE"); 0213 if (tp) 0214 { 0215 m_DriverInterface = static_cast<uint32_t>(atoi(tp->getText())); 0216 emit interfaceDefined(); 0217 } 0218 0219 tp = tvp->findWidgetByName("DRIVER_VERSION"); 0220 if (tp) 0221 { 0222 m_DriverVersion = QString(tp->getText()); 0223 } 0224 } 0225 } 0226 else if (name == "SYSTEM_PORTS") 0227 { 0228 // Check if our current port is set to one of the system ports. This indicates that the port 0229 // is not mapped yet to a permenant designation 0230 auto svp = prop.getSwitch(); 0231 auto port = m_BaseDevice.getText("DEVICE_PORT"); 0232 if (svp && port) 0233 { 0234 for (const auto &it : *svp) 0235 { 0236 if (it.isNameMatch(port.at(0)->getText())) 0237 { 0238 emit systemPortDetected(); 0239 break; 0240 } 0241 } 0242 } 0243 } 0244 else if (name == "TIME_UTC" && Options::useTimeUpdate()) 0245 { 0246 const auto &tvp = prop.getText(); 0247 0248 if (tvp) 0249 { 0250 if (Options::useKStarsSource() && tvp->getPermission() != IP_RO) 0251 updateTime(); 0252 else 0253 m_TimeUpdateTimer->start(); 0254 } 0255 } 0256 else if (name == "GEOGRAPHIC_COORD" && Options::useGeographicUpdate()) 0257 { 0258 if (Options::useKStarsSource() && prop.getPermission() != IP_RO) 0259 updateLocation(); 0260 else 0261 m_LocationUpdateTimer->start(); 0262 } 0263 else if (name == "WATCHDOG_HEARTBEAT") 0264 { 0265 if (watchDogTimer == nullptr) 0266 { 0267 watchDogTimer = new QTimer(this); 0268 connect(watchDogTimer, SIGNAL(timeout()), this, SLOT(resetWatchdog())); 0269 } 0270 0271 if (m_Connected && prop.getNumber()->at(0)->getValue() > 0) 0272 { 0273 // Send immediately a heart beat 0274 m_ClientManager->sendNewProperty(prop); 0275 } 0276 } 0277 0278 emit propertyDefined(prop); 0279 } 0280 0281 void GenericDevice::updateProperty(INDI::Property prop) 0282 { 0283 switch (prop.getType()) 0284 { 0285 case INDI_SWITCH: 0286 processSwitch(prop); 0287 break; 0288 case INDI_NUMBER: 0289 processNumber(prop); 0290 break; 0291 case INDI_TEXT: 0292 processText(prop); 0293 break; 0294 case INDI_LIGHT: 0295 processLight(prop); 0296 break; 0297 case INDI_BLOB: 0298 processBLOB(prop); 0299 break; 0300 default: 0301 break; 0302 } 0303 } 0304 0305 void GenericDevice::removeProperty(INDI::Property prop) 0306 { 0307 emit propertyDeleted(prop); 0308 } 0309 0310 void GenericDevice::processSwitch(INDI::Property prop) 0311 { 0312 if (prop.isNameMatch("CONNECTION")) 0313 { 0314 // Still connecting/disconnecting... 0315 if (prop.getState() == IPS_BUSY) 0316 return; 0317 0318 auto connectionOn = prop.getSwitch()->findWidgetByName("CONNECT"); 0319 if (m_Connected == false && prop.getState() == IPS_OK && connectionOn->getState() == ISS_ON) 0320 { 0321 m_Ready = false; 0322 connect(m_ReadyTimer, &QTimer::timeout, this, &GenericDevice::handleTimeout, Qt::UniqueConnection); 0323 0324 m_Connected = true; 0325 emit Connected(); 0326 createDeviceInit(); 0327 0328 if (watchDogTimer != nullptr) 0329 { 0330 auto nvp = m_BaseDevice.getNumber("WATCHDOG_HEARTBEAT"); 0331 if (nvp && nvp.at(0)->getValue() > 0) 0332 { 0333 // Send immediately 0334 m_ClientManager->sendNewProperty(nvp); 0335 } 0336 } 0337 0338 m_ReadyTimer->start(); 0339 } 0340 else if (m_Connected && connectionOn->getState() == ISS_OFF) 0341 { 0342 disconnect(m_ReadyTimer, &QTimer::timeout, this, &GenericDevice::handleTimeout); 0343 m_Connected = false; 0344 m_Ready = false; 0345 emit Disconnected(); 0346 } 0347 else 0348 m_Ready = (prop.getState() == IPS_OK && connectionOn->getState() == ISS_ON); 0349 } 0350 0351 emit propertyUpdated(prop); 0352 } 0353 0354 void GenericDevice::processNumber(INDI::Property prop) 0355 { 0356 QString deviceName = getDeviceName(); 0357 auto nvp = prop.getNumber(); 0358 0359 if (prop.isNameMatch("GEOGRAPHIC_COORD") && prop.getState() == IPS_OK && 0360 ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) || 0361 (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)))) 0362 { 0363 // Update KStars Location once we receive update from INDI, if the source is set to DEVICE 0364 dms lng, lat; 0365 double elev = 0; 0366 0367 auto np = nvp->findWidgetByName("LONG"); 0368 if (!np) 0369 return; 0370 0371 // INDI Longitude convention is 0 to 360. We need to turn it back into 0 to 180 EAST, 0 to -180 WEST 0372 if (np->value < 180) 0373 lng.setD(np->value); 0374 else 0375 lng.setD(np->value - 360.0); 0376 0377 np = nvp->findWidgetByName("LAT"); 0378 if (!np) 0379 return; 0380 0381 lat.setD(np->value); 0382 0383 // Double check we have valid values 0384 if (lng.Degrees() == 0 && lat.Degrees() == 0) 0385 { 0386 qCWarning(KSTARS_INDI) << "Ignoring invalid device coordinates."; 0387 return; 0388 } 0389 0390 np = nvp->findWidgetByName("ELEV"); 0391 if (np) 0392 elev = np->value; 0393 0394 auto geo = KStars::Instance()->data()->geo(); 0395 std::unique_ptr<GeoLocation> tempGeo; 0396 0397 QString newLocationName; 0398 if (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE) 0399 newLocationName = i18n("GPS Location"); 0400 else 0401 newLocationName = i18n("Mount Location"); 0402 0403 if (geo->name() != newLocationName) 0404 { 0405 double TZ0 = geo->TZ0(); 0406 TimeZoneRule *rule = geo->tzrule(); 0407 tempGeo.reset(new GeoLocation(lng, lat, newLocationName, "", "", TZ0, rule, elev)); 0408 geo = tempGeo.get(); 0409 } 0410 else 0411 { 0412 geo->setLong(lng); 0413 geo->setLat(lat); 0414 } 0415 0416 qCInfo(KSTARS_INDI) << "Setting location from device:" << deviceName << "Longitude:" << lng.toDMSString() << "Latitude:" << 0417 lat.toDMSString(); 0418 0419 KStars::Instance()->data()->setLocation(*geo); 0420 } 0421 else if (nvp->isNameMatch("WATCHDOG_HEARTBEAT")) 0422 { 0423 if (watchDogTimer == nullptr) 0424 { 0425 watchDogTimer = new QTimer(this); 0426 connect(watchDogTimer, &QTimer::timeout, this, &GenericDevice::resetWatchdog); 0427 } 0428 0429 auto value = nvp->at(0)->getValue(); 0430 if (m_Connected && value > 0) 0431 { 0432 // Reset timer 5 seconds before it is due 0433 // To account for any networking delays 0434 double nextMS = qMax(100.0, (value - 5) * 1000); 0435 watchDogTimer->start(nextMS); 0436 } 0437 else if (value == 0) 0438 watchDogTimer->stop(); 0439 } 0440 0441 emit propertyUpdated(prop); 0442 } 0443 0444 void GenericDevice::processText(INDI::Property prop) 0445 { 0446 auto tvp = prop.getText(); 0447 // If DRIVER_INFO is updated after being defined, make sure to re-generate concrete devices accordingly. 0448 if (tvp->isNameMatch("DRIVER_INFO")) 0449 { 0450 auto tp = tvp->findWidgetByName("DRIVER_INTERFACE"); 0451 if (tp) 0452 { 0453 m_DriverInterface = static_cast<uint32_t>(atoi(tp->getText())); 0454 emit interfaceDefined(); 0455 0456 // If devices were already created but we receieved an update to DRIVER_INTERFACE 0457 // then we need to re-generate the concrete devices to account for the change. 0458 if (m_ConcreteDevices.isEmpty() == false) 0459 { 0460 // If we generated ANY concrete device due to interface update, then we emit ready immediately. 0461 if (generateDevices()) 0462 emit ready(); 0463 } 0464 } 0465 0466 tp = tvp->findWidgetByName("DRIVER_VERSION"); 0467 if (tp) 0468 { 0469 m_DriverVersion = QString(tp->text); 0470 } 0471 0472 } 0473 // Update KStars time once we receive update from INDI, if the source is set to DEVICE 0474 else if (tvp->isNameMatch("TIME_UTC") && tvp->s == IPS_OK && 0475 ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) || 0476 (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)))) 0477 { 0478 int d, m, y, min, sec, hour; 0479 float utcOffset; 0480 QDate indiDate; 0481 QTime indiTime; 0482 0483 auto tp = tvp->findWidgetByName("UTC"); 0484 0485 if (!tp) 0486 { 0487 qCWarning(KSTARS_INDI) << "UTC property missing from TIME_UTC"; 0488 return; 0489 } 0490 0491 sscanf(tp->getText(), "%d%*[^0-9]%d%*[^0-9]%dT%d%*[^0-9]%d%*[^0-9]%d", &y, &m, &d, &hour, &min, &sec); 0492 indiDate.setDate(y, m, d); 0493 indiTime.setHMS(hour, min, sec); 0494 0495 KStarsDateTime indiDateTime(QDateTime(indiDate, indiTime, Qt::UTC)); 0496 0497 tp = tvp->findWidgetByName("OFFSET"); 0498 0499 if (!tp) 0500 { 0501 qCWarning(KSTARS_INDI) << "Offset property missing from TIME_UTC"; 0502 return; 0503 } 0504 0505 sscanf(tp->getText(), "%f", &utcOffset); 0506 0507 qCInfo(KSTARS_INDI) << "Setting UTC time from device:" << getDeviceName() << indiDateTime.toString(); 0508 0509 KStars::Instance()->data()->changeDateTime(indiDateTime); 0510 KStars::Instance()->data()->syncLST(); 0511 0512 auto geo = KStars::Instance()->data()->geo(); 0513 if (geo->tzrule()) 0514 utcOffset -= geo->tzrule()->deltaTZ(); 0515 0516 // TZ0 is the timezone WTIHOUT any DST offsets. Above, we take INDI UTC Offset (with DST already included) 0517 // and subtract from it the deltaTZ from the current TZ rule. 0518 geo->setTZ0(utcOffset); 0519 } 0520 0521 emit propertyUpdated(prop); 0522 } 0523 0524 void GenericDevice::processLight(INDI::Property prop) 0525 { 0526 emit propertyUpdated(prop); 0527 } 0528 0529 void GenericDevice::processMessage(int messageID) 0530 { 0531 emit messageUpdated(messageID); 0532 } 0533 0534 bool GenericDevice::processBLOB(INDI::Property prop) 0535 { 0536 // Ignore write-only BLOBs since we only receive it for state-change 0537 if (prop.getPermission() == IP_WO) 0538 return false; 0539 0540 auto bvp = prop.getBLOB(); 0541 auto bp = bvp->at(0); 0542 0543 // If any concrete device processed the blob then we return 0544 for (auto oneConcreteDevice : m_ConcreteDevices) 0545 { 0546 if (oneConcreteDevice->processBLOB(prop)) 0547 return true; 0548 } 0549 0550 INDIDataTypes dataType; 0551 0552 if (!strcmp(bp->getFormat(), ".ascii")) 0553 dataType = DATA_ASCII; 0554 else 0555 dataType = DATA_OTHER; 0556 0557 QString currentDir = Options::fitsDir(); 0558 int nr, n = 0; 0559 0560 if (currentDir.endsWith('/')) 0561 currentDir.truncate(sizeof(currentDir) - 1); 0562 0563 QString filename(currentDir + '/'); 0564 0565 QString ts = QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss"); 0566 0567 filename += QString("%1_").arg(bp->getLabel()) + ts + QString(bp->getFormat()).trimmed(); 0568 0569 // Text Streaming 0570 if (dataType == DATA_ASCII) 0571 { 0572 // First time, create a data file to hold the stream. 0573 0574 auto it = std::find_if(streamFileMetadata.begin(), streamFileMetadata.end(), [bvp, bp](const StreamFileMetadata & data) 0575 { 0576 return (bvp->getDeviceName() == data.device && bvp->getName() == data.property && bp->getName() == data.element); 0577 }); 0578 0579 QFile *streamDatafile = nullptr; 0580 0581 // New stream data file 0582 if (it == streamFileMetadata.end()) 0583 { 0584 StreamFileMetadata metadata; 0585 metadata.device = bvp->getDeviceName(); 0586 metadata.property = bvp->getName(); 0587 metadata.element = bp->getName(); 0588 0589 // Create new file instance 0590 // Since it's a child of this class, don't worry about deallocating it 0591 streamDatafile = new QFile(this); 0592 metadata.file = streamDatafile; 0593 0594 streamFileMetadata.append(metadata); 0595 } 0596 else 0597 streamDatafile = (*it).file; 0598 0599 // Try to get 0600 0601 QDataStream out(streamDatafile); 0602 for (nr = 0; nr < bp->getSize(); nr += n) 0603 n = out.writeRawData(static_cast<char *>(bp->getBlob()) + nr, bp->getSize() - nr); 0604 0605 out.writeRawData((const char *)"\n", 1); 0606 streamDatafile->flush(); 0607 0608 } 0609 else 0610 { 0611 QFile fits_temp_file(filename); 0612 if (!fits_temp_file.open(QIODevice::WriteOnly)) 0613 { 0614 qCCritical(KSTARS_INDI) << "GenericDevice Error: Unable to open " << fits_temp_file.fileName(); 0615 return false; 0616 } 0617 0618 QDataStream out(&fits_temp_file); 0619 0620 for (nr = 0; nr < bp->getSize(); nr += n) 0621 n = out.writeRawData(static_cast<char *>(bp->getBlob()) + nr, bp->getSize() - nr); 0622 0623 fits_temp_file.flush(); 0624 fits_temp_file.close(); 0625 0626 auto fmt = QString(bp->getFormat()).toLower().remove('.').toUtf8(); 0627 if (QImageReader::supportedImageFormats().contains(fmt)) 0628 { 0629 QUrl url(filename); 0630 url.setScheme("file"); 0631 auto iv = new ImageViewer(url, QString(), KStars::Instance()); 0632 if (iv) 0633 iv->show(); 0634 } 0635 } 0636 0637 if (dataType == DATA_OTHER) 0638 KStars::Instance()->statusBar()->showMessage(i18n("Data file saved to %1", filename), 0); 0639 0640 emit propertyUpdated(prop); 0641 return true; 0642 } 0643 0644 bool GenericDevice::setConfig(INDIConfig tConfig) 0645 { 0646 auto svp = m_BaseDevice.getSwitch("CONFIG_PROCESS"); 0647 0648 if (!svp) 0649 return false; 0650 0651 const char *strConfig = nullptr; 0652 0653 switch (tConfig) 0654 { 0655 case LOAD_LAST_CONFIG: 0656 strConfig = "CONFIG_LOAD"; 0657 break; 0658 0659 case SAVE_CONFIG: 0660 strConfig = "CONFIG_SAVE"; 0661 break; 0662 0663 case LOAD_DEFAULT_CONFIG: 0664 strConfig = "CONFIG_DEFAULT"; 0665 break; 0666 0667 case PURGE_CONFIG: 0668 strConfig = "CONFIG_PURGE"; 0669 break; 0670 } 0671 0672 svp.reset(); 0673 if (strConfig) 0674 { 0675 auto sp = svp.findWidgetByName(strConfig); 0676 if (!sp) 0677 return false; 0678 sp->setState(ISS_ON); 0679 } 0680 0681 m_ClientManager->sendNewProperty(svp); 0682 0683 return true; 0684 } 0685 0686 void GenericDevice::createDeviceInit() 0687 { 0688 if (Options::showINDIMessages()) 0689 KStars::Instance()->statusBar()->showMessage(i18n("%1 is online.", m_Name), 0); 0690 0691 KStars::Instance()->map()->forceUpdateNow(); 0692 } 0693 0694 /*********************************************************************************/ 0695 /* Update the Driver's Time */ 0696 /*********************************************************************************/ 0697 void GenericDevice::updateTime() 0698 { 0699 QString offset, isoTS; 0700 0701 offset = QString().setNum(KStars::Instance()->data()->geo()->TZ(), 'g', 2); 0702 0703 //QTime newTime( KStars::Instance()->data()->ut().time()); 0704 //QDate newDate( KStars::Instance()->data()->ut().date()); 0705 0706 //isoTS = QString("%1-%2-%3T%4:%5:%6").arg(newDate.year()).arg(newDate.month()).arg(newDate.day()).arg(newTime.hour()).arg(newTime.minute()).arg(newTime.second()); 0707 0708 isoTS = KStars::Instance()->data()->ut().toString(Qt::ISODate).remove('Z'); 0709 0710 /* Update Date/Time */ 0711 auto timeUTC = m_BaseDevice.getText("TIME_UTC"); 0712 0713 if (timeUTC) 0714 { 0715 auto timeEle = timeUTC.findWidgetByName("UTC"); 0716 if (timeEle) 0717 timeEle->setText(isoTS.toLatin1().constData()); 0718 0719 auto offsetEle = timeUTC.findWidgetByName("OFFSET"); 0720 if (offsetEle) 0721 offsetEle->setText(offset.toLatin1().constData()); 0722 0723 if (timeEle && offsetEle) 0724 { 0725 qCInfo(KSTARS_INDI) << "Updating" << getDeviceName() << "Time UTC:" << isoTS << "Offset:" << offset; 0726 m_ClientManager->sendNewProperty(timeUTC); 0727 } 0728 } 0729 } 0730 0731 /*********************************************************************************/ 0732 /* Update the Driver's Geographical Location */ 0733 /*********************************************************************************/ 0734 void GenericDevice::updateLocation() 0735 { 0736 GeoLocation *geo = KStars::Instance()->data()->geo(); 0737 double longNP; 0738 0739 if (geo->lng()->Degrees() >= 0) 0740 longNP = geo->lng()->Degrees(); 0741 else 0742 longNP = dms(geo->lng()->Degrees() + 360.0).Degrees(); 0743 0744 auto nvp = m_BaseDevice.getNumber("GEOGRAPHIC_COORD"); 0745 0746 if (!nvp) 0747 return; 0748 0749 auto np = nvp.findWidgetByName("LONG"); 0750 0751 if (!np) 0752 return; 0753 0754 np->setValue(longNP); 0755 0756 np = nvp.findWidgetByName("LAT"); 0757 if (!np) 0758 return; 0759 0760 np->setValue(geo->lat()->Degrees()); 0761 0762 np = nvp.findWidgetByName("ELEV"); 0763 if (!np) 0764 return; 0765 0766 np->setValue(geo->elevation()); 0767 0768 qCInfo(KSTARS_INDI) << "Updating" << getDeviceName() << "Location Longitude:" << longNP << "Latitude:" << geo->lat()->Degrees() << "Elevation:" << geo->elevation(); 0769 0770 m_ClientManager->sendNewProperty(nvp); 0771 } 0772 0773 void GenericDevice::Connect() 0774 { 0775 m_ClientManager->connectDevice(m_Name.toLatin1().constData()); 0776 } 0777 0778 void GenericDevice::Disconnect() 0779 { 0780 m_ClientManager->disconnectDevice(m_Name.toLatin1().constData()); 0781 } 0782 0783 bool GenericDevice::setProperty(QObject *setPropCommand) 0784 { 0785 GDSetCommand *indiCommand = static_cast<GDSetCommand *>(setPropCommand); 0786 0787 //qDebug() << Q_FUNC_INFO << "We are trying to set value for property " << indiCommand->indiProperty << " and element" << indiCommand->indiElement << " and value " << indiCommand->elementValue; 0788 0789 auto prop = m_BaseDevice.getProperty(indiCommand->indiProperty.toLatin1().constData()); 0790 0791 if (!prop) 0792 return false; 0793 0794 switch (indiCommand->propType) 0795 { 0796 case INDI_SWITCH: 0797 { 0798 auto svp = prop.getSwitch(); 0799 0800 if (!svp) 0801 return false; 0802 0803 auto sp = svp->findWidgetByName(indiCommand->indiElement.toLatin1().constData()); 0804 0805 if (!sp) 0806 return false; 0807 0808 if (svp->getRule() == ISR_1OFMANY || svp->getRule() == ISR_ATMOST1) 0809 svp->reset(); 0810 0811 sp->setState(indiCommand->elementValue.toInt() == 0 ? ISS_OFF : ISS_ON); 0812 0813 //qDebug() << Q_FUNC_INFO << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off"); 0814 m_ClientManager->sendNewProperty(svp); 0815 0816 return true; 0817 } 0818 0819 case INDI_NUMBER: 0820 { 0821 auto nvp = prop.getNumber(); 0822 0823 if (!nvp) 0824 return false; 0825 0826 auto np = nvp->findWidgetByName(indiCommand->indiElement.toLatin1().constData()); 0827 0828 if (!np) 0829 return false; 0830 0831 double value = indiCommand->elementValue.toDouble(); 0832 0833 if (value == np->getValue()) 0834 return true; 0835 0836 np->setValue(value); 0837 0838 //qDebug() << Q_FUNC_INFO << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off"); 0839 m_ClientManager->sendNewProperty(nvp); 0840 } 0841 break; 0842 // TODO: Add set property for other types of properties 0843 default: 0844 break; 0845 } 0846 0847 return true; 0848 } 0849 0850 bool GenericDevice::getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, 0851 double *step) 0852 { 0853 auto nvp = m_BaseDevice.getNumber(propName.toLatin1()); 0854 0855 if (!nvp) 0856 return false; 0857 0858 auto np = nvp.findWidgetByName(elementName.toLatin1()); 0859 0860 if (!np) 0861 return false; 0862 0863 *min = np->getMin(); 0864 *max = np->getMax(); 0865 *step = np->getStep(); 0866 0867 return true; 0868 } 0869 0870 IPState GenericDevice::getState(const QString &propName) 0871 { 0872 return m_BaseDevice.getPropertyState(propName.toLatin1().constData()); 0873 } 0874 0875 IPerm GenericDevice::getPermission(const QString &propName) 0876 { 0877 return m_BaseDevice.getPropertyPermission(propName.toLatin1().constData()); 0878 } 0879 0880 INDI::Property GenericDevice::getProperty(const QString &propName) 0881 { 0882 return m_BaseDevice.getProperty(propName.toLatin1().constData()); 0883 } 0884 0885 bool GenericDevice::setJSONProperty(const QString &propName, const QJsonArray &propElements) 0886 { 0887 for (auto &oneProp : * (m_BaseDevice.getProperties())) 0888 { 0889 if (propName == QString(oneProp.getName())) 0890 { 0891 switch (oneProp.getType()) 0892 { 0893 case INDI_SWITCH: 0894 { 0895 auto svp = oneProp.getSwitch(); 0896 if (svp->getRule() == ISR_1OFMANY || svp->getRule() == ISR_ATMOST1) 0897 svp->reset(); 0898 0899 for (auto oneElement : propElements) 0900 { 0901 QJsonObject oneElementObject = oneElement.toObject(); 0902 auto sp = svp->findWidgetByName(oneElementObject["name"].toString().toLatin1().constData()); 0903 if (sp) 0904 { 0905 sp->setState(static_cast<ISState>(oneElementObject["state"].toInt())); 0906 } 0907 } 0908 0909 m_ClientManager->sendNewProperty(svp); 0910 return true; 0911 } 0912 0913 case INDI_NUMBER: 0914 { 0915 auto nvp = oneProp.getNumber(); 0916 for (const auto &oneElement : propElements) 0917 { 0918 QJsonObject oneElementObject = oneElement.toObject(); 0919 auto np = nvp->findWidgetByName(oneElementObject["name"].toString().toLatin1().constData()); 0920 if (np) 0921 { 0922 double newValue = oneElementObject["value"].toDouble(std::numeric_limits<double>::quiet_NaN()); 0923 if (std::isnan(newValue)) 0924 { 0925 f_scansexa(oneElementObject["value"].toString().toLatin1().constData(), &newValue); 0926 } 0927 np->setValue(newValue); 0928 } 0929 } 0930 0931 m_ClientManager->sendNewProperty(nvp); 0932 return true; 0933 } 0934 0935 case INDI_TEXT: 0936 { 0937 auto tvp = oneProp.getText(); 0938 for (const auto &oneElement : propElements) 0939 { 0940 QJsonObject oneElementObject = oneElement.toObject(); 0941 auto tp = tvp->findWidgetByName(oneElementObject["name"].toString().toLatin1().constData()); 0942 if (tp) 0943 tp->setText(oneElementObject["text"].toString().toLatin1().constData()); 0944 } 0945 0946 m_ClientManager->sendNewProperty(tvp); 0947 return true; 0948 } 0949 0950 case INDI_BLOB: 0951 // TODO 0952 break; 0953 0954 default: 0955 break; 0956 } 0957 } 0958 } 0959 0960 return false; 0961 } 0962 0963 bool GenericDevice::getJSONProperty(const QString &propName, QJsonObject &propObject, bool compact) 0964 { 0965 for (auto oneProp : m_BaseDevice.getProperties()) 0966 { 0967 if (propName == oneProp.getName()) 0968 { 0969 switch (oneProp.getType()) 0970 { 0971 case INDI_SWITCH: 0972 switchToJson(oneProp, propObject, compact); 0973 return true; 0974 0975 case INDI_NUMBER: 0976 numberToJson(oneProp, propObject, compact); 0977 return true; 0978 0979 case INDI_TEXT: 0980 textToJson(oneProp, propObject, compact); 0981 return true; 0982 0983 case INDI_LIGHT: 0984 lightToJson(oneProp, propObject, compact); 0985 return true; 0986 0987 case INDI_BLOB: 0988 // TODO 0989 break; 0990 0991 default: 0992 break; 0993 } 0994 } 0995 } 0996 0997 return false; 0998 } 0999 1000 bool GenericDevice::getJSONBLOB(const QString &propName, const QString &elementName, QJsonObject &blobObject) 1001 { 1002 auto blobProperty = m_BaseDevice.getProperty(propName.toLatin1().constData()); 1003 if (!blobProperty.isValid()) 1004 return false; 1005 1006 auto oneBLOB = blobProperty.getBLOB()->findWidgetByName(elementName.toLatin1().constData()); 1007 if (!oneBLOB) 1008 return false; 1009 1010 // Now convert to base64 and send back. 1011 QByteArray data = QByteArray::fromRawData(static_cast<const char *>(oneBLOB->getBlob()), oneBLOB->getBlobLen()); 1012 1013 QString encoded = data.toBase64(QByteArray::Base64UrlEncoding); 1014 blobObject.insert("property", propName); 1015 blobObject.insert("element", elementName); 1016 blobObject.insert("size", encoded.size()); 1017 blobObject.insert("data", encoded); 1018 1019 return true; 1020 } 1021 1022 void GenericDevice::resetWatchdog() 1023 { 1024 auto nvp = m_BaseDevice.getNumber("WATCHDOG_HEARTBEAT"); 1025 1026 if (nvp) 1027 // Send heartbeat to driver 1028 m_ClientManager->sendNewProperty(nvp); 1029 } 1030 1031 bool GenericDevice::findConcreteDevice(uint32_t interface, QSharedPointer<ConcreteDevice> &device) 1032 { 1033 if (m_ConcreteDevices.contains(interface)) 1034 { 1035 device = m_ConcreteDevices[interface]; 1036 return true; 1037 } 1038 return false; 1039 } 1040 1041 ISD::Mount *GenericDevice::getMount() 1042 { 1043 if (m_ConcreteDevices.contains(INDI::BaseDevice::TELESCOPE_INTERFACE)) 1044 return dynamic_cast<ISD::Mount*>(m_ConcreteDevices[INDI::BaseDevice::TELESCOPE_INTERFACE].get()); 1045 return nullptr; 1046 } 1047 1048 ISD::Camera *GenericDevice::getCamera() 1049 { 1050 if (m_ConcreteDevices.contains(INDI::BaseDevice::CCD_INTERFACE)) 1051 return dynamic_cast<ISD::Camera*>(m_ConcreteDevices[INDI::BaseDevice::CCD_INTERFACE].get()); 1052 return nullptr; 1053 } 1054 1055 ISD::Guider *GenericDevice::getGuider() 1056 { 1057 if (m_ConcreteDevices.contains(INDI::BaseDevice::GUIDER_INTERFACE)) 1058 return dynamic_cast<ISD::Guider*>(m_ConcreteDevices[INDI::BaseDevice::GUIDER_INTERFACE].get()); 1059 return nullptr; 1060 } 1061 1062 ISD::Focuser *GenericDevice::getFocuser() 1063 { 1064 if (m_ConcreteDevices.contains(INDI::BaseDevice::FOCUSER_INTERFACE)) 1065 return dynamic_cast<ISD::Focuser*>(m_ConcreteDevices[INDI::BaseDevice::FOCUSER_INTERFACE].get()); 1066 return nullptr; 1067 } 1068 1069 ISD::FilterWheel *GenericDevice::getFilterWheel() 1070 { 1071 if (m_ConcreteDevices.contains(INDI::BaseDevice::FILTER_INTERFACE)) 1072 return dynamic_cast<ISD::FilterWheel*>(m_ConcreteDevices[INDI::BaseDevice::FILTER_INTERFACE].get()); 1073 return nullptr; 1074 } 1075 1076 ISD::Dome *GenericDevice::getDome() 1077 { 1078 if (m_ConcreteDevices.contains(INDI::BaseDevice::DOME_INTERFACE)) 1079 return dynamic_cast<ISD::Dome*>(m_ConcreteDevices[INDI::BaseDevice::DOME_INTERFACE].get()); 1080 return nullptr; 1081 } 1082 1083 ISD::GPS *GenericDevice::getGPS() 1084 { 1085 if (m_ConcreteDevices.contains(INDI::BaseDevice::GPS_INTERFACE)) 1086 return dynamic_cast<ISD::GPS*>(m_ConcreteDevices[INDI::BaseDevice::GPS_INTERFACE].get()); 1087 return nullptr; 1088 } 1089 1090 ISD::Weather *GenericDevice::getWeather() 1091 { 1092 if (m_ConcreteDevices.contains(INDI::BaseDevice::WEATHER_INTERFACE)) 1093 return dynamic_cast<ISD::Weather*>(m_ConcreteDevices[INDI::BaseDevice::WEATHER_INTERFACE].get()); 1094 return nullptr; 1095 } 1096 1097 ISD::AdaptiveOptics *GenericDevice::getAdaptiveOptics() 1098 { 1099 if (m_ConcreteDevices.contains(INDI::BaseDevice::AO_INTERFACE)) 1100 return dynamic_cast<ISD::AdaptiveOptics*>(m_ConcreteDevices[INDI::BaseDevice::AO_INTERFACE].get()); 1101 return nullptr; 1102 } 1103 1104 ISD::DustCap *GenericDevice::getDustCap() 1105 { 1106 if (m_ConcreteDevices.contains(INDI::BaseDevice::DUSTCAP_INTERFACE)) 1107 return dynamic_cast<ISD::DustCap*>(m_ConcreteDevices[INDI::BaseDevice::DUSTCAP_INTERFACE].get()); 1108 return nullptr; 1109 } 1110 1111 ISD::LightBox *GenericDevice::getLightBox() 1112 { 1113 if (m_ConcreteDevices.contains(INDI::BaseDevice::LIGHTBOX_INTERFACE)) 1114 return dynamic_cast<ISD::LightBox*>(m_ConcreteDevices[INDI::BaseDevice::LIGHTBOX_INTERFACE].get()); 1115 return nullptr; 1116 } 1117 1118 ISD::Detector *GenericDevice::getDetector() 1119 { 1120 if (m_ConcreteDevices.contains(INDI::BaseDevice::DETECTOR_INTERFACE)) 1121 return dynamic_cast<ISD::Detector*>(m_ConcreteDevices[INDI::BaseDevice::DETECTOR_INTERFACE].get()); 1122 return nullptr; 1123 } 1124 1125 ISD::Rotator *GenericDevice::getRotator() 1126 { 1127 if (m_ConcreteDevices.contains(INDI::BaseDevice::ROTATOR_INTERFACE)) 1128 return dynamic_cast<ISD::Rotator*>(m_ConcreteDevices[INDI::BaseDevice::ROTATOR_INTERFACE].get()); 1129 return nullptr; 1130 } 1131 1132 ISD::Spectrograph *GenericDevice::getSpectrograph() 1133 { 1134 if (m_ConcreteDevices.contains(INDI::BaseDevice::SPECTROGRAPH_INTERFACE)) 1135 return dynamic_cast<ISD::Spectrograph*>(m_ConcreteDevices[INDI::BaseDevice::SPECTROGRAPH_INTERFACE].get()); 1136 return nullptr; 1137 } 1138 1139 ISD::Correlator *GenericDevice::getCorrelator() 1140 { 1141 if (m_ConcreteDevices.contains(INDI::BaseDevice::CORRELATOR_INTERFACE)) 1142 return dynamic_cast<ISD::Correlator*>(m_ConcreteDevices[INDI::BaseDevice::CORRELATOR_INTERFACE].get()); 1143 return nullptr; 1144 } 1145 1146 ISD::Auxiliary *GenericDevice::getAuxiliary() 1147 { 1148 if (m_ConcreteDevices.contains(INDI::BaseDevice::AUX_INTERFACE)) 1149 return dynamic_cast<ISD::Auxiliary*>(m_ConcreteDevices[INDI::BaseDevice::AUX_INTERFACE].get()); 1150 return nullptr; 1151 } 1152 1153 bool GenericDevice::generateDevices() 1154 { 1155 auto generated = false; 1156 // Mount 1157 if (m_DriverInterface & INDI::BaseDevice::TELESCOPE_INTERFACE && 1158 m_ConcreteDevices[INDI::BaseDevice::TELESCOPE_INTERFACE].isNull()) 1159 { 1160 auto mount = new ISD::Mount(this); 1161 mount->setObjectName("Mount:" + objectName()); 1162 generated = true; 1163 m_ConcreteDevices[INDI::BaseDevice::TELESCOPE_INTERFACE].reset(mount); 1164 mount->registeProperties(); 1165 if (m_Connected) 1166 { 1167 mount->processProperties(); 1168 emit newMount(mount); 1169 } 1170 else 1171 { 1172 connect(mount, &ISD::ConcreteDevice::ready, this, [this, mount]() 1173 { 1174 emit newMount(mount); 1175 }); 1176 } 1177 } 1178 1179 // Camera 1180 if (m_DriverInterface & INDI::BaseDevice::CCD_INTERFACE && 1181 m_ConcreteDevices[INDI::BaseDevice::CCD_INTERFACE].isNull()) 1182 { 1183 auto camera = new ISD::Camera(this); 1184 camera->setObjectName("Camera:" + objectName()); 1185 generated = true; 1186 m_ConcreteDevices[INDI::BaseDevice::CCD_INTERFACE].reset(camera); 1187 camera->registeProperties(); 1188 if (m_Connected) 1189 { 1190 camera->processProperties(); 1191 emit newCamera(camera); 1192 emit ready(); 1193 } 1194 else 1195 { 1196 connect(camera, &ISD::ConcreteDevice::ready, this, [this, camera]() 1197 { 1198 emit newCamera(camera); 1199 }); 1200 } 1201 } 1202 1203 // Guider 1204 if (m_DriverInterface & INDI::BaseDevice::GUIDER_INTERFACE && 1205 m_ConcreteDevices[INDI::BaseDevice::GUIDER_INTERFACE].isNull()) 1206 { 1207 auto guider = new ISD::Guider(this); 1208 guider->setObjectName("Guider:" + objectName()); 1209 generated = true; 1210 m_ConcreteDevices[INDI::BaseDevice::GUIDER_INTERFACE].reset(guider); 1211 guider->registeProperties(); 1212 if (m_Connected) 1213 { 1214 guider->processProperties(); 1215 emit newGuider(guider); 1216 } 1217 else 1218 { 1219 connect(guider, &ISD::ConcreteDevice::ready, this, [this, guider]() 1220 { 1221 emit newGuider(guider); 1222 }); 1223 } 1224 } 1225 1226 // Focuser 1227 if (m_DriverInterface & INDI::BaseDevice::FOCUSER_INTERFACE && 1228 m_ConcreteDevices[INDI::BaseDevice::FOCUSER_INTERFACE].isNull()) 1229 { 1230 auto focuser = new ISD::Focuser(this); 1231 focuser->setObjectName("Focuser:" + objectName()); 1232 generated = true; 1233 m_ConcreteDevices[INDI::BaseDevice::FOCUSER_INTERFACE].reset(focuser); 1234 focuser->registeProperties(); 1235 if (m_Connected) 1236 { 1237 focuser->processProperties(); 1238 emit newFocuser(focuser); 1239 } 1240 else 1241 { 1242 connect(focuser, &ISD::ConcreteDevice::ready, this, [this, focuser]() 1243 { 1244 emit newFocuser(focuser); 1245 }); 1246 } 1247 } 1248 1249 // Filter Wheel 1250 if (m_DriverInterface & INDI::BaseDevice::FILTER_INTERFACE && 1251 m_ConcreteDevices[INDI::BaseDevice::FILTER_INTERFACE].isNull()) 1252 { 1253 auto filterWheel = new ISD::FilterWheel(this); 1254 filterWheel->setObjectName("FilterWheel:" + objectName()); 1255 generated = true; 1256 m_ConcreteDevices[INDI::BaseDevice::FILTER_INTERFACE].reset(filterWheel); 1257 filterWheel->registeProperties(); 1258 if (m_Connected) 1259 { 1260 filterWheel->processProperties(); 1261 emit newFilterWheel(filterWheel); 1262 } 1263 else 1264 { 1265 connect(filterWheel, &ISD::ConcreteDevice::ready, this, [this, filterWheel]() 1266 { 1267 emit newFilterWheel(filterWheel); 1268 }); 1269 } 1270 } 1271 1272 // Dome 1273 if (m_DriverInterface & INDI::BaseDevice::DOME_INTERFACE && 1274 m_ConcreteDevices[INDI::BaseDevice::DOME_INTERFACE].isNull()) 1275 { 1276 auto dome = new ISD::Dome(this); 1277 dome->setObjectName("Dome:" + objectName()); 1278 generated = true; 1279 m_ConcreteDevices[INDI::BaseDevice::DOME_INTERFACE].reset(dome); 1280 dome->registeProperties(); 1281 if (m_Connected) 1282 { 1283 dome->processProperties(); 1284 emit newDome(dome); 1285 } 1286 else 1287 { 1288 connect(dome, &ISD::ConcreteDevice::ready, this, [this, dome]() 1289 { 1290 emit newDome(dome); 1291 }); 1292 } 1293 } 1294 1295 // GPS 1296 if (m_DriverInterface & INDI::BaseDevice::GPS_INTERFACE && 1297 m_ConcreteDevices[INDI::BaseDevice::GPS_INTERFACE].isNull()) 1298 { 1299 auto gps = new ISD::GPS(this); 1300 gps->setObjectName("GPS:" + objectName()); 1301 generated = true; 1302 m_ConcreteDevices[INDI::BaseDevice::GPS_INTERFACE].reset(gps); 1303 gps->registeProperties(); 1304 if (m_Connected) 1305 { 1306 gps->processProperties(); 1307 emit newGPS(gps); 1308 } 1309 else 1310 { 1311 connect(gps, &ISD::ConcreteDevice::ready, this, [this, gps]() 1312 { 1313 emit newGPS(gps); 1314 }); 1315 } 1316 } 1317 1318 // Weather 1319 if (m_DriverInterface & INDI::BaseDevice::WEATHER_INTERFACE && 1320 m_ConcreteDevices[INDI::BaseDevice::WEATHER_INTERFACE].isNull()) 1321 { 1322 auto weather = new ISD::Weather(this); 1323 weather->setObjectName("Weather:" + objectName()); 1324 generated = true; 1325 m_ConcreteDevices[INDI::BaseDevice::WEATHER_INTERFACE].reset(weather); 1326 weather->registeProperties(); 1327 if (m_Connected) 1328 { 1329 weather->processProperties(); 1330 emit newWeather(weather); 1331 } 1332 else 1333 { 1334 connect(weather, &ISD::ConcreteDevice::ready, this, [this, weather]() 1335 { 1336 emit newWeather(weather); 1337 }); 1338 } 1339 } 1340 1341 // Adaptive Optics 1342 if (m_DriverInterface & INDI::BaseDevice::AO_INTERFACE && 1343 m_ConcreteDevices[INDI::BaseDevice::AO_INTERFACE].isNull()) 1344 { 1345 auto ao = new ISD::AdaptiveOptics(this); 1346 ao->setObjectName("AdaptiveOptics:" + objectName()); 1347 generated = true; 1348 m_ConcreteDevices[INDI::BaseDevice::AO_INTERFACE].reset(ao); 1349 ao->registeProperties(); 1350 if (m_Connected) 1351 { 1352 ao->processProperties(); 1353 emit newAdaptiveOptics(ao); 1354 } 1355 else 1356 { 1357 connect(ao, &ISD::ConcreteDevice::ready, this, [this, ao]() 1358 { 1359 emit newAdaptiveOptics(ao); 1360 }); 1361 } 1362 } 1363 1364 // Dust Cap 1365 if (m_DriverInterface & INDI::BaseDevice::DUSTCAP_INTERFACE && 1366 m_ConcreteDevices[INDI::BaseDevice::DUSTCAP_INTERFACE].isNull()) 1367 { 1368 auto dustCap = new ISD::DustCap(this); 1369 dustCap->setObjectName("DustCap:" + objectName()); 1370 generated = true; 1371 m_ConcreteDevices[INDI::BaseDevice::DUSTCAP_INTERFACE].reset(dustCap); 1372 dustCap->registeProperties(); 1373 if (m_Connected) 1374 { 1375 dustCap->processProperties(); 1376 emit newDustCap(dustCap); 1377 } 1378 else 1379 { 1380 connect(dustCap, &ISD::ConcreteDevice::ready, this, [this, dustCap]() 1381 { 1382 emit newDustCap(dustCap); 1383 }); 1384 } 1385 } 1386 1387 // Light box 1388 if (m_DriverInterface & INDI::BaseDevice::LIGHTBOX_INTERFACE && 1389 m_ConcreteDevices[INDI::BaseDevice::LIGHTBOX_INTERFACE].isNull()) 1390 { 1391 auto lightBox = new ISD::LightBox(this); 1392 lightBox->setObjectName("LightBox:" + objectName()); 1393 generated = true; 1394 m_ConcreteDevices[INDI::BaseDevice::LIGHTBOX_INTERFACE].reset(lightBox); 1395 lightBox->registeProperties(); 1396 if (m_Connected) 1397 { 1398 lightBox->processProperties(); 1399 emit newLightBox(lightBox); 1400 } 1401 else 1402 { 1403 connect(lightBox, &ISD::ConcreteDevice::ready, this, [this, lightBox]() 1404 { 1405 emit newLightBox(lightBox); 1406 }); 1407 } 1408 } 1409 1410 // Rotator 1411 if (m_DriverInterface & INDI::BaseDevice::ROTATOR_INTERFACE && 1412 m_ConcreteDevices[INDI::BaseDevice::ROTATOR_INTERFACE].isNull()) 1413 { 1414 auto rotator = new ISD::Rotator(this); 1415 rotator->setObjectName("Rotator:" + objectName()); 1416 generated = true; 1417 m_ConcreteDevices[INDI::BaseDevice::ROTATOR_INTERFACE].reset(rotator); 1418 rotator->registeProperties(); 1419 if (m_Connected) 1420 { 1421 rotator->processProperties(); 1422 emit newRotator(rotator); 1423 } 1424 else 1425 { 1426 connect(rotator, &ISD::ConcreteDevice::ready, this, [this, rotator]() 1427 { 1428 emit newRotator(rotator); 1429 }); 1430 } 1431 } 1432 1433 // Detector 1434 if (m_DriverInterface & INDI::BaseDevice::DETECTOR_INTERFACE && 1435 m_ConcreteDevices[INDI::BaseDevice::DETECTOR_INTERFACE].isNull()) 1436 { 1437 auto detector = new ISD::Detector(this); 1438 detector->setObjectName("Detector:" + objectName()); 1439 generated = true; 1440 m_ConcreteDevices[INDI::BaseDevice::DETECTOR_INTERFACE].reset(detector); 1441 detector->registeProperties(); 1442 if (m_Connected) 1443 { 1444 detector->processProperties(); 1445 emit newDetector(detector); 1446 } 1447 else 1448 { 1449 connect(detector, &ISD::ConcreteDevice::ready, this, [this, detector]() 1450 { 1451 emit newDetector(detector); 1452 }); 1453 } 1454 } 1455 1456 // Spectrograph 1457 if (m_DriverInterface & INDI::BaseDevice::SPECTROGRAPH_INTERFACE && 1458 m_ConcreteDevices[INDI::BaseDevice::SPECTROGRAPH_INTERFACE].isNull()) 1459 { 1460 auto spectrograph = new ISD::Spectrograph(this); 1461 spectrograph->setObjectName("Spectrograph:" + objectName()); 1462 generated = true; 1463 m_ConcreteDevices[INDI::BaseDevice::SPECTROGRAPH_INTERFACE].reset(spectrograph); 1464 spectrograph->registeProperties(); 1465 if (m_Connected) 1466 { 1467 spectrograph->processProperties(); 1468 emit newSpectrograph(spectrograph); 1469 } 1470 else 1471 { 1472 connect(spectrograph, &ISD::ConcreteDevice::ready, this, [this, spectrograph]() 1473 { 1474 emit newSpectrograph(spectrograph); 1475 }); 1476 } 1477 } 1478 1479 // Correlator 1480 if (m_DriverInterface & INDI::BaseDevice::CORRELATOR_INTERFACE && 1481 m_ConcreteDevices[INDI::BaseDevice::CORRELATOR_INTERFACE].isNull()) 1482 { 1483 auto correlator = new ISD::Correlator(this); 1484 correlator->setObjectName("Correlator:" + objectName()); 1485 generated = true; 1486 m_ConcreteDevices[INDI::BaseDevice::CORRELATOR_INTERFACE].reset(correlator); 1487 correlator->registeProperties(); 1488 if (m_Connected) 1489 { 1490 correlator->processProperties(); 1491 emit newCorrelator(correlator); 1492 } 1493 else 1494 { 1495 connect(correlator, &ISD::ConcreteDevice::ready, this, [this, correlator]() 1496 { 1497 emit newCorrelator(correlator); 1498 }); 1499 } 1500 } 1501 1502 // Auxiliary 1503 if (m_DriverInterface & INDI::BaseDevice::AUX_INTERFACE && 1504 m_ConcreteDevices[INDI::BaseDevice::AUX_INTERFACE].isNull()) 1505 { 1506 auto aux = new ISD::Auxiliary(this); 1507 aux->setObjectName("Auxiliary:" + objectName()); 1508 generated = true; 1509 m_ConcreteDevices[INDI::BaseDevice::AUX_INTERFACE].reset(aux); 1510 aux->registeProperties(); 1511 if (m_Connected) 1512 { 1513 aux->processProperties(); 1514 emit newAuxiliary(aux); 1515 } 1516 else 1517 { 1518 connect(aux, &ISD::ConcreteDevice::ready, this, [this, aux]() 1519 { 1520 emit newAuxiliary(aux); 1521 }); 1522 } 1523 } 1524 1525 return generated; 1526 } 1527 1528 void GenericDevice::sendNewProperty(INDI::Property prop) 1529 { 1530 m_ClientManager->sendNewProperty(prop); 1531 } 1532 1533 void switchToJson(INDI::Property prop, QJsonObject &propObject, bool compact) 1534 { 1535 auto svp = prop.getSwitch(); 1536 QJsonArray switches; 1537 for (int i = 0; i < svp->count(); i++) 1538 { 1539 QJsonObject oneSwitch = {{"name", svp->at(i)->getName()}, {"state", svp->at(i)->getState()}}; 1540 if (!compact) 1541 oneSwitch.insert("label", svp->at(i)->getLabel()); 1542 switches.append(oneSwitch); 1543 } 1544 1545 propObject = {{"device", svp->getDeviceName()}, {"name", svp->getName()}, {"state", svp->getState()}, {"switches", switches}}; 1546 if (!compact) 1547 { 1548 propObject.insert("label", svp->getLabel()); 1549 propObject.insert("group", svp->getGroupName()); 1550 propObject.insert("perm", svp->getPermission()); 1551 propObject.insert("rule", svp->getRule()); 1552 } 1553 } 1554 1555 void numberToJson(INDI::Property prop, QJsonObject &propObject, bool compact) 1556 { 1557 auto nvp = prop.getNumber(); 1558 QJsonArray numbers; 1559 for (int i = 0; i < nvp->count(); i++) 1560 { 1561 QJsonObject oneNumber = {{"name", nvp->at(i)->getName()}, {"value", nvp->at(i)->getValue()}}; 1562 if (!compact) 1563 { 1564 oneNumber.insert("label", nvp->at(i)->getLabel()); 1565 oneNumber.insert("min", nvp->at(i)->getMin()); 1566 oneNumber.insert("max", nvp->at(i)->getMax()); 1567 oneNumber.insert("step", nvp->at(i)->getStep()); 1568 oneNumber.insert("format", nvp->at(i)->getFormat()); 1569 } 1570 numbers.append(oneNumber); 1571 } 1572 1573 propObject = {{"device", nvp->getDeviceName()}, {"name", nvp->getName()}, {"state", nvp->getState()}, {"numbers", numbers}}; 1574 if (!compact) 1575 { 1576 propObject.insert("label", nvp->getLabel()); 1577 propObject.insert("group", nvp->getGroupName()); 1578 propObject.insert("perm", nvp->getPermission()); 1579 } 1580 } 1581 1582 void textToJson(INDI::Property prop, QJsonObject &propObject, bool compact) 1583 { 1584 auto tvp = prop.getText(); 1585 QJsonArray Texts; 1586 for (int i = 0; i < tvp->count(); i++) 1587 { 1588 QJsonObject oneText = {{"name", tvp->at(i)->getName()}, {"text", tvp->at(i)->getText()}}; 1589 if (!compact) 1590 { 1591 oneText.insert("label", tvp->at(i)->getLabel()); 1592 } 1593 Texts.append(oneText); 1594 } 1595 1596 propObject = {{"device", tvp->getDeviceName()}, {"name", tvp->getName()}, {"state", tvp->getState()}, {"texts", Texts}}; 1597 if (!compact) 1598 { 1599 propObject.insert("label", tvp->getLabel()); 1600 propObject.insert("group", tvp->getGroupName()); 1601 propObject.insert("perm", tvp->getPermission()); 1602 } 1603 } 1604 1605 void lightToJson(INDI::Property prop, QJsonObject &propObject, bool compact) 1606 { 1607 auto lvp = prop.getLight(); 1608 QJsonArray Lights; 1609 for (int i = 0; i < lvp->count(); i++) 1610 { 1611 QJsonObject oneLight = {{"name", lvp->at(i)->getName()}, {"state", lvp->at(i)->getState()}}; 1612 if (!compact) 1613 { 1614 oneLight.insert("label", lvp->at(i)->getLabel()); 1615 } 1616 Lights.append(oneLight); 1617 } 1618 1619 propObject = {{"device", lvp->getDeviceName()}, {"name", lvp->getName()}, {"state", lvp->getState()}, {"lights", Lights}}; 1620 if (!compact) 1621 { 1622 propObject.insert("label", lvp->getLabel()); 1623 propObject.insert("group", lvp->getGroupName()); 1624 } 1625 } 1626 1627 void propertyToJson(INDI::Property prop, QJsonObject &propObject, bool compact) 1628 { 1629 switch (prop.getType()) 1630 { 1631 case INDI_SWITCH: 1632 switchToJson(prop, propObject, compact); 1633 break; 1634 case INDI_TEXT: 1635 textToJson(prop, propObject, compact); 1636 break; 1637 case INDI_NUMBER: 1638 numberToJson(prop, propObject, compact); 1639 break; 1640 case INDI_LIGHT: 1641 lightToJson(prop, propObject, compact); 1642 break; 1643 default: 1644 break; 1645 } 1646 } 1647 } 1648 1649 #ifndef KSTARS_LITE 1650 QDBusArgument &operator<<(QDBusArgument &argument, const ISD::ParkStatus &source) 1651 { 1652 argument.beginStructure(); 1653 argument << static_cast<int>(source); 1654 argument.endStructure(); 1655 return argument; 1656 } 1657 1658 const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::ParkStatus &dest) 1659 { 1660 int a; 1661 argument.beginStructure(); 1662 argument >> a; 1663 argument.endStructure(); 1664 dest = static_cast<ISD::ParkStatus>(a); 1665 return argument; 1666 } 1667 #endif