File indexing completed on 2024-03-24 03:46:47
0001 /* 0002 SPDX-FileCopyrightText: 2016 Artem Fedoskin <afedoskin3@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "clientmanagerlite.h" 0007 0008 #include "basedevice.h" 0009 #include "indicom.h" 0010 #include "inditelescopelite.h" 0011 #include "kspaths.h" 0012 #include "kstarslite.h" 0013 #include "Options.h" 0014 #include "skymaplite.h" 0015 #include "fitsviewer/fitsdata.h" 0016 #include "kstarslite/imageprovider.h" 0017 #include "kstarslite/skyitems/telescopesymbolsitem.h" 0018 0019 #include <KLocalizedString> 0020 0021 #include <QApplication> 0022 #include <QDebug> 0023 #include <QFileDialog> 0024 #include <QImageReader> 0025 #include <QJsonArray> 0026 #include <QJsonDocument> 0027 #include <QProcess> 0028 #include <QQmlApplicationEngine> 0029 #include <QQmlContext> 0030 #include <QTemporaryFile> 0031 0032 const char *libindi_strings_context = "string from libindi, used in the config dialog"; 0033 0034 #ifdef Q_OS_ANDROID 0035 #include "libraw/libraw.h" 0036 #endif 0037 0038 DeviceInfoLite::DeviceInfoLite(INDI::BaseDevice *dev) : device(dev) 0039 { 0040 } 0041 0042 DeviceInfoLite::~DeviceInfoLite() 0043 { 0044 } 0045 0046 ClientManagerLite::ClientManagerLite(QQmlContext& main_context) : context(main_context) 0047 { 0048 #ifdef ANDROID 0049 defaultImageType = ".jpeg"; 0050 defaultImagesLocation = QDir(KSPaths::writableLocation(QStandardPaths::PicturesLocation) + "/" + qAppName()).path(); 0051 #endif 0052 qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeNS"); 0053 qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeWE"); 0054 qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeCommand"); 0055 context.setContextProperty("webMProfileModel", QVariant::fromValue(webMProfiles)); 0056 } 0057 0058 ClientManagerLite::~ClientManagerLite() 0059 { 0060 } 0061 0062 bool ClientManagerLite::setHost(const QString &ip, unsigned int port) 0063 { 0064 if (!isConnected()) 0065 { 0066 setServer(ip.toStdString().c_str(), port); 0067 qDebug() << ip << port; 0068 if (connectServer()) 0069 { 0070 setConnectedHost(ip + ':' + QString::number(port)); 0071 //Update last used server and port 0072 setLastUsedServer(ip); 0073 setLastUsedPort(port); 0074 0075 return true; 0076 } 0077 } 0078 return false; 0079 } 0080 0081 void ClientManagerLite::disconnectHost() 0082 { 0083 disconnectServer(); 0084 clearDevices(); 0085 setConnectedHost(""); 0086 } 0087 0088 void ClientManagerLite::getWebManagerProfiles(const QString &ip, unsigned int port) 0089 { 0090 if (webMProfilesReply.get() != nullptr) 0091 return; 0092 0093 QString urlStr(QString("http://%1:%2/api/profiles").arg(ip).arg(port)); 0094 QNetworkRequest request { QUrl(urlStr) }; 0095 0096 webMProfilesReply.reset(manager.get(request)); 0097 connect(webMProfilesReply.get(), SIGNAL(error(QNetworkReply::NetworkError)), 0098 this, SLOT(webManagerReplyError(QNetworkReply::NetworkError))); 0099 connect(webMProfilesReply.get(), &QNetworkReply::finished, this, &ClientManagerLite::webManagerReplyFinished); 0100 setLastUsedServer(ip); 0101 setLastUsedWebManagerPort(port); 0102 } 0103 0104 void ClientManagerLite::startWebManagerProfile(const QString &profile) 0105 { 0106 if (webMStartProfileReply.get() != nullptr) 0107 return; 0108 0109 QString urlStr("http://%1:%2/api/server/start/%3"); 0110 0111 urlStr = urlStr.arg(getLastUsedServer()).arg(getLastUsedWebManagerPort()).arg(profile); 0112 QNetworkRequest request { QUrl(urlStr) }; 0113 0114 webMStartProfileReply.reset(manager.post(request, QByteArray())); 0115 connect(webMStartProfileReply.get(), SIGNAL(error(QNetworkReply::NetworkError)), 0116 this, SLOT(webManagerReplyError(QNetworkReply::NetworkError))); 0117 connect(webMStartProfileReply.get(), &QNetworkReply::finished, 0118 this, &ClientManagerLite::webManagerReplyFinished); 0119 } 0120 0121 void ClientManagerLite::stopWebManagerProfile() 0122 { 0123 if (webMStopProfileReply.get() != nullptr) 0124 return; 0125 0126 QString urlStr(QString("http://%1:%2/api/server/stop").arg(getLastUsedServer()).arg(getLastUsedWebManagerPort())); 0127 QNetworkRequest request { QUrl(urlStr) }; 0128 0129 webMStopProfileReply.reset(manager.post(request, QByteArray())); 0130 connect(webMStopProfileReply.get(), SIGNAL(error(QNetworkReply::NetworkError)), 0131 this, SLOT(webManagerReplyError(QNetworkReply::NetworkError))); 0132 connect(webMStopProfileReply.get(), &QNetworkReply::finished, 0133 this, &ClientManagerLite::webManagerReplyFinished); 0134 } 0135 0136 void ClientManagerLite::webManagerReplyError(QNetworkReply::NetworkError code) 0137 { 0138 if (webMProfilesReply.get() != nullptr) 0139 { 0140 qWarning("Web Manager profile query error: %d", (int)code); 0141 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager")); 0142 webMProfilesReply.release()->deleteLater(); 0143 return; 0144 } 0145 if (webMStatusReply.get() != nullptr) 0146 { 0147 qWarning("Web Manager status query error: %d", (int)code); 0148 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager")); 0149 webMStatusReply.release()->deleteLater(); 0150 return; 0151 } 0152 if (webMStopProfileReply.get() != nullptr) 0153 { 0154 qWarning("Web Manager stop active profile error: %d", (int)code); 0155 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager")); 0156 webMStopProfileReply.release()->deleteLater(); 0157 return; 0158 } 0159 if (webMStartProfileReply.get() != nullptr) 0160 { 0161 qWarning("Web Manager start active profile error: %d", (int)code); 0162 KStarsLite::Instance()->notificationMessage(i18n("Could not connect to the Web Manager")); 0163 webMStartProfileReply.release()->deleteLater(); 0164 return; 0165 } 0166 } 0167 0168 void ClientManagerLite::webManagerReplyFinished() 0169 { 0170 // Web Manager profile query 0171 if (webMProfilesReply.get() != nullptr) 0172 { 0173 QByteArray responseData = webMProfilesReply->readAll(); 0174 QJsonDocument json = QJsonDocument::fromJson(responseData); 0175 0176 webMProfilesReply.release()->deleteLater(); 0177 if (!json.isArray()) 0178 { 0179 KStarsLite::Instance()->notificationMessage(i18n("Invalid response from Web Manager")); 0180 return; 0181 } 0182 QJsonArray array = json.array(); 0183 0184 webMProfiles.clear(); 0185 for (int i = 0; i < array.size(); ++i) 0186 { 0187 if (array.at(i).isObject() && array.at(i).toObject().contains("name")) 0188 { 0189 webMProfiles += array.at(i).toObject()["name"].toString(); 0190 } 0191 } 0192 // Send a query for the network status 0193 QString urlStr(QString("http://%1:%2/api/server/status").arg(getLastUsedServer()).arg(getLastUsedWebManagerPort())); 0194 QNetworkRequest request { QUrl(urlStr) }; 0195 0196 webMStatusReply.reset(manager.get(request)); 0197 connect(webMStatusReply.get(), SIGNAL(error(QNetworkReply::NetworkError)), 0198 this, SLOT(webManagerReplyError(QNetworkReply::NetworkError))); 0199 connect(webMStatusReply.get(), &QNetworkReply::finished, this, &ClientManagerLite::webManagerReplyFinished); 0200 return; 0201 } 0202 // Web Manager status query 0203 if (webMStatusReply.get() != nullptr) 0204 { 0205 QByteArray responseData = webMStatusReply->readAll(); 0206 QJsonDocument json = QJsonDocument::fromJson(responseData); 0207 0208 webMStatusReply.release()->deleteLater(); 0209 if (!json.isArray() || json.array().size() != 1 || !json.array().at(0).isObject()) 0210 { 0211 KStarsLite::Instance()->notificationMessage(i18n("Invalid response from Web Manager")); 0212 return; 0213 } 0214 QJsonObject object = json.array().at(0).toObject(); 0215 0216 // Check the response 0217 if (!object.contains("status") || !object.contains("active_profile")) 0218 { 0219 KStarsLite::Instance()->notificationMessage(i18n("Invalid response from Web Manager")); 0220 return; 0221 } 0222 QString statusStr = object["status"].toString(); 0223 QString activeProfileStr = object["active_profile"].toString(); 0224 0225 indiControlPage->setProperty("webMBrowserButtonVisible", true); 0226 indiControlPage->setProperty("webMStatusTextVisible", true); 0227 if (statusStr == "True") 0228 { 0229 // INDI Server is running (online) 0230 indiControlPage->setProperty("webMStatusText", i18n("Web Manager Status: Online")); 0231 indiControlPage->setProperty("webMActiveProfileText", 0232 i18n("Active Profile: %1", activeProfileStr)); 0233 indiControlPage->setProperty("webMActiveProfileLayoutVisible", true); 0234 indiControlPage->setProperty("webMProfileListVisible", false); 0235 } else { 0236 // INDI Server is not running (offline) 0237 indiControlPage->setProperty("webMStatusText", i18n("Web Manager Status: Offline")); 0238 indiControlPage->setProperty("webMActiveProfileLayoutVisible", false); 0239 context.setContextProperty("webMProfileModel", QVariant::fromValue(webMProfiles)); 0240 indiControlPage->setProperty("webMProfileListVisible", true); 0241 } 0242 return; 0243 } 0244 // Web Manager stop active profile 0245 if (webMStopProfileReply.get() != nullptr) 0246 { 0247 webMStopProfileReply.release()->deleteLater(); 0248 indiControlPage->setProperty("webMStatusText", QString(i18n("Web Manager Status:")+' '+i18n("Offline"))); 0249 indiControlPage->setProperty("webMStatusTextVisible", true); 0250 indiControlPage->setProperty("webMActiveProfileLayoutVisible", false); 0251 context.setContextProperty("webMProfileModel", QVariant::fromValue(webMProfiles)); 0252 indiControlPage->setProperty("webMProfileListVisible", true); 0253 return; 0254 } 0255 // Web Manager start active profile 0256 if (webMStartProfileReply.get() != nullptr) 0257 { 0258 webMStartProfileReply.release()->deleteLater(); 0259 // Send a query for the network status 0260 QString urlStr("http://%1:%2/api/server/status"); 0261 0262 urlStr = urlStr.arg(getLastUsedServer()).arg(getLastUsedWebManagerPort()); 0263 QNetworkRequest request { QUrl(urlStr) }; 0264 0265 webMStatusReply.reset(manager.get(request)); 0266 connect(webMStatusReply.get(), SIGNAL(error(QNetworkReply::NetworkError)), 0267 this, SLOT(webManagerReplyError(QNetworkReply::NetworkError))); 0268 connect(webMStatusReply.get(), &QNetworkReply::finished, 0269 this, &ClientManagerLite::webManagerReplyFinished); 0270 // Connect to the server automatically 0271 QMetaObject::invokeMethod(indiControlPage, "connectIndiServer"); 0272 return; 0273 } 0274 } 0275 0276 TelescopeLite *ClientManagerLite::getTelescope() 0277 { 0278 for (auto& devInfo : m_devices) 0279 { 0280 if (devInfo->telescope.get()) 0281 { 0282 return devInfo->telescope.get(); 0283 } 0284 } 0285 return nullptr; 0286 } 0287 0288 void ClientManagerLite::setConnectedHost(const QString &connectedHost) 0289 { 0290 m_connectedHost = connectedHost; 0291 setConnected(m_connectedHost.size() > 0); 0292 0293 emit connectedHostChanged(connectedHost); 0294 } 0295 0296 void ClientManagerLite::setConnected(bool connected) 0297 { 0298 m_connected = connected; 0299 emit connectedChanged(connected); 0300 } 0301 0302 QString ClientManagerLite::syncLED(const QString &device, const QString &property, const QString &name) 0303 { 0304 foreach (DeviceInfoLite *devInfo, m_devices) 0305 { 0306 if (devInfo->device->getDeviceName() == device) 0307 { 0308 INDI::Property prop = devInfo->device->getProperty(property.toLatin1()); 0309 if (prop) 0310 { 0311 IPState state = prop->getState(); 0312 if (!name.isEmpty()) 0313 { 0314 ILight *lights = prop->getLight()->lp; 0315 for (int i = 0; i < prop->getLight()->nlp; i++) 0316 { 0317 if (lights[i].name == name) 0318 { 0319 state = lights[i].s; 0320 break; 0321 } 0322 if (i == prop->getLight()->nlp - 1) 0323 return ""; // no Light with name "name" found so return empty string 0324 } 0325 } 0326 switch (state) 0327 { 0328 case IPS_IDLE: 0329 return "grey"; 0330 break; 0331 0332 case IPS_OK: 0333 return "green"; 0334 break; 0335 0336 case IPS_BUSY: 0337 return "yellow"; 0338 break; 0339 0340 case IPS_ALERT: 0341 return "red"; 0342 break; 0343 0344 default: 0345 return "grey"; 0346 break; 0347 } 0348 } 0349 } 0350 } 0351 return "grey"; 0352 } 0353 0354 void ClientManagerLite::buildTextGUI(Property *property) 0355 { 0356 auto tvp = property->getText(); 0357 if (!tvp) 0358 return; 0359 0360 for (const auto &it: *tvp) 0361 { 0362 QString name = it.getName(); 0363 QString label = it.getLabel(); 0364 QString text = it.getText(); 0365 bool read = false; 0366 bool write = false; 0367 /*if (tp->label[0]) 0368 label = i18nc(libindi_strings_context, itp->label); 0369 0370 if (label == "(I18N_EMPTY_MESSAGE)") 0371 label = itp->label;*/ 0372 0373 if (label.isEmpty()) 0374 label = tvp->getName(); // #PS: it should be it.getName() ? 0375 /*label = i18nc(libindi_strings_context, itp->name); 0376 0377 if (label == "(I18N_EMPTY_MESSAGE)")*/ 0378 0379 //setupElementLabel(); 0380 0381 /*if (tp->text[0]) 0382 text = i18nc(libindi_strings_context, tp->text);*/ 0383 0384 switch (property->getPermission()) 0385 { 0386 case IP_RW: 0387 read = true; 0388 write = true; 0389 break; 0390 0391 case IP_RO: 0392 read = true; 0393 write = false; 0394 break; 0395 0396 case IP_WO: 0397 read = false; 0398 write = true; 0399 break; 0400 } 0401 emit createINDIText(property->getDeviceName(), property->getName(), label, name, text, read, write); 0402 } 0403 } 0404 0405 void ClientManagerLite::buildNumberGUI(Property *property) 0406 { 0407 auto nvp = property->getNumber(); 0408 if (!nvp) 0409 return; 0410 0411 //for (int i = 0; i < nvp->nnp; i++) 0412 for (const auto &it: nvp) 0413 { 0414 bool scale = false; 0415 char iNumber[MAXINDIFORMAT]; 0416 0417 QString name = it.getName(); 0418 QString label = it.getLabel(); 0419 QString text; 0420 bool read = false; 0421 bool write = false; 0422 /*if (tp->label[0]) 0423 label = i18nc(libindi_strings_context, itp->label); 0424 0425 if (label == "(I18N_EMPTY_MESSAGE)") 0426 label = itp->label;*/ 0427 0428 if (label.isEmpty()) 0429 label = np->getName(); 0430 0431 numberFormat(iNumber, np.getFormat(), np.getValue()); 0432 text = iNumber; 0433 0434 /*label = i18nc(libindi_strings_context, itp->name); 0435 0436 if (label == "(I18N_EMPTY_MESSAGE)")*/ 0437 0438 //setupElementLabel(); 0439 0440 /*if (tp->text[0]) 0441 text = i18nc(libindi_strings_context, tp->text);*/ 0442 0443 if (it.getStep() != 0 && (it.getMax() - it.getMin()) / it.getStep() <= 100) 0444 scale = true; 0445 0446 switch (property->getPermission()) 0447 { 0448 case IP_RW: 0449 read = true; 0450 write = true; 0451 break; 0452 0453 case IP_RO: 0454 read = true; 0455 write = false; 0456 break; 0457 0458 case IP_WO: 0459 read = false; 0460 write = true; 0461 break; 0462 } 0463 emit createINDINumber(property->getDeviceName(), property->getName(), label, name, text, read, write, 0464 scale); 0465 } 0466 } 0467 0468 void ClientManagerLite::buildMenuGUI(INDI::Property property) 0469 { 0470 /*QStringList menuOptions; 0471 QString oneOption; 0472 int onItem=-1;*/ 0473 auto svp = property->getSwitch(); 0474 0475 if (!svp) 0476 return; 0477 0478 for (auto &it: *svp) 0479 { 0480 buildSwitch(false, &it, property); 0481 0482 /*if (tp->s == ISS_ON) 0483 onItem = i; 0484 0485 lp = new INDI_E(this, dataProp); 0486 0487 lp->buildMenuItem(tp); 0488 0489 oneOption = i18nc(libindi_strings_context, lp->getLabel().toUtf8()); 0490 0491 if (oneOption == "(I18N_EMPTY_MESSAGE)") 0492 oneOption = lp->getLabel().toUtf8(); 0493 0494 menuOptions.append(oneOption); 0495 0496 elementList.append(lp);*/ 0497 } 0498 } 0499 0500 void ClientManagerLite::buildSwitchGUI(INDI::Property property, PGui guiType) 0501 { 0502 auto svp = property->getSwitch(); 0503 bool exclusive = false; 0504 0505 if (!svp) 0506 return; 0507 0508 if (guiType == PG_BUTTONS) 0509 { 0510 if (svp->getRule() == ISR_1OFMANY) 0511 exclusive = true; 0512 else 0513 exclusive = false; 0514 } 0515 else if (guiType == PG_RADIO) 0516 exclusive = false; 0517 0518 /*if (svp->p != IP_RO) 0519 QObject::connect(groupB, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(newSwitch(QAbstractButton*)));*/ 0520 0521 for (auto &it: *svp) 0522 { 0523 buildSwitch(true, &it, property, exclusive, guiType); 0524 } 0525 } 0526 0527 void ClientManagerLite::buildSwitch(bool buttonGroup, ISwitch *sw, INDI::Property property, bool exclusive, 0528 PGui guiType) 0529 { 0530 QString name = sw->name; 0531 QString label = sw->label; //i18nc(libindi_strings_context, sw->label); 0532 0533 if (label == "(I18N_EMPTY_MESSAGE)") 0534 label = sw->label; 0535 0536 if (label.isEmpty()) 0537 label = sw->name; 0538 //label = i18nc(libindi_strings_context, sw->name); 0539 0540 if (label == "(I18N_EMPTY_MESSAGE)") 0541 label = sw->name; 0542 0543 if (!buttonGroup) 0544 { 0545 bool isSelected = false; 0546 if (sw->s == ISS_ON) 0547 isSelected = true; 0548 emit createINDIMenu(property->getDeviceName(), property->getName(), label, sw->name, isSelected); 0549 return; 0550 } 0551 0552 bool enabled = true; 0553 0554 if (sw->svp->p == IP_RO) 0555 enabled = (sw->s == ISS_ON); 0556 0557 switch (guiType) 0558 { 0559 case PG_BUTTONS: 0560 emit createINDIButton(property->getDeviceName(), property->getName(), label, name, true, true, exclusive, 0561 sw->s == ISS_ON, enabled); 0562 break; 0563 0564 case PG_RADIO: 0565 emit createINDIRadio(property->getDeviceName(), property->getName(), label, name, true, true, exclusive, 0566 sw->s == ISS_ON, enabled); 0567 /*check_w = new QCheckBox(label, guiProp->getGroup()->getContainer()); 0568 groupB->addButton(check_w); 0569 0570 syncSwitch(); 0571 0572 guiProp->addWidget(check_w); 0573 0574 check_w->show(); 0575 0576 if (sw->svp->p == IP_RO) 0577 check_w->setEnabled(sw->s == ISS_ON); 0578 0579 break;*/ 0580 0581 default: 0582 break; 0583 } 0584 } 0585 0586 void ClientManagerLite::buildLightGUI(INDI::Property property) 0587 { 0588 auto lvp = property->getLight(); 0589 0590 if (!lvp) 0591 return; 0592 0593 for (auto &it: *lvp) 0594 { 0595 QString name = it.getName(); 0596 QString label = i18nc(libindi_strings_context, it.getLabel()); 0597 0598 if (label == "(I18N_EMPTY_MESSAGE)") 0599 label = it.getLabel(); 0600 0601 if (label.isEmpty()) 0602 label = i18nc(libindi_strings_context, it.getName()); 0603 0604 if (label == "(I18N_EMPTY_MESSAGE)") 0605 label = it.getName();; 0606 0607 emit createINDILight(property->getDeviceName(), property->getName(), label, name); 0608 } 0609 } 0610 0611 /*void ClientManagerLite::buildBLOBGUI(INDI::Property property) { 0612 IBLOBVectorProperty *ibp = property->getBLOB(); 0613 0614 QString name = ibp->name; 0615 QString label = i18nc(libindi_strings_context, ibp->label); 0616 0617 if (label == "(I18N_EMPTY_MESSAGE)") 0618 label = ibp->label; 0619 0620 if (label.isEmpty()) 0621 label = i18nc(libindi_strings_context, ibp->name); 0622 0623 if (label == "(I18N_EMPTY_MESSAGE)") 0624 label = ibp->name; 0625 0626 text = i18n("INDI DATA STREAM"); 0627 0628 switch (property->getPermission()) 0629 { 0630 case IP_RW: 0631 setupElementRead(ELEMENT_READ_WIDTH); 0632 setupElementWrite(ELEMENT_WRITE_WIDTH); 0633 setupBrowseButton(); 0634 break; 0635 0636 case IP_RO: 0637 setupElementRead(ELEMENT_FULL_WIDTH); 0638 break; 0639 0640 case IP_WO: 0641 setupElementWrite(ELEMENT_FULL_WIDTH); 0642 setupBrowseButton(); 0643 break; 0644 } 0645 0646 guiProp->addLayout(EHBox); 0647 }*/ 0648 0649 void ClientManagerLite::sendNewINDISwitch(const QString &deviceName, const QString &propName, const QString &name) 0650 { 0651 foreach (DeviceInfoLite *devInfo, m_devices) 0652 { 0653 INDI::BaseDevice *device = devInfo->device; 0654 if (device->getDeviceName() == deviceName) 0655 { 0656 auto property = device->getProperty(propName.toLatin1()); 0657 if (property) 0658 { 0659 auto svp = property->getSwitch(); 0660 0661 if (!svp) 0662 return; 0663 0664 auto sp = svp->findWidgetByName(name.toLatin1().constData()); 0665 0666 if (!sp) 0667 return; 0668 0669 if (sp->isNameMatch("CONNECT")) 0670 { 0671 svp->reset(); 0672 sp->setState(ISS_ON); 0673 } 0674 0675 if (svp->getRule() == ISR_1OFMANY) 0676 { 0677 svp->reset(); 0678 sp->setState(ISS_ON); 0679 } 0680 else 0681 { 0682 if (svp->getRule() == ISR_ATMOST1) 0683 { 0684 ISState prev_state = sp->getState(); 0685 svp->reset(); 0686 sp->setState(prev_state); 0687 } 0688 0689 sp->setState(sp->getState() == ISS_ON ? ISS_OFF : ISS_ON); 0690 } 0691 sendNewSwitch(svp); 0692 } 0693 } 0694 } 0695 } 0696 0697 void ClientManagerLite::sendNewINDINumber(const QString &deviceName, const QString &propName, const QString &numberName, 0698 double value) 0699 { 0700 foreach (DeviceInfoLite *devInfo, m_devices) 0701 { 0702 INDI::BaseDevice *device = devInfo->device; 0703 if (device->getDeviceName() == deviceName) 0704 { 0705 auto np = device->getNumber(propName.toLatin1()); 0706 if (np) 0707 { 0708 auto n = np->findWIdgetByName(numberName.toLatin1()); 0709 if (n) 0710 { 0711 n->setValue(value); 0712 sendNewNumber(np); 0713 return; 0714 } 0715 0716 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << numberName; 0717 return; 0718 } 0719 0720 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << numberName; 0721 return; 0722 } 0723 } 0724 } 0725 0726 void ClientManagerLite::sendNewINDIText(const QString &deviceName, const QString &propName, const QString &fieldName, 0727 const QString &text) 0728 { 0729 foreach (DeviceInfoLite *devInfo, m_devices) 0730 { 0731 INDI::BaseDevice *device = devInfo->device; 0732 if (device->getDeviceName() == deviceName) 0733 { 0734 auto tp = device->getText(propName.toLatin1()); 0735 if (tp) 0736 { 0737 auto t = tp->findWidgetByName(fieldName.toLatin1()); 0738 if (t) 0739 { 0740 t.setText(text.toLatin1().data()); 0741 sendNewText(tp); 0742 return; 0743 } 0744 0745 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << fieldName; 0746 return; 0747 } 0748 0749 qDebug() << "Could not find property: " << deviceName << "." << propName << "." << fieldName; 0750 return; 0751 } 0752 } 0753 } 0754 0755 void ClientManagerLite::sendNewINDISwitch(const QString &deviceName, const QString &propName, int index) 0756 { 0757 if (index >= 0) 0758 { 0759 foreach (DeviceInfoLite *devInfo, m_devices) 0760 { 0761 INDI::BaseDevice *device = devInfo->device; 0762 if (device->getDeviceName() == deviceName) 0763 { 0764 auto property = device->getProperty(propName.toStdString().c_str()); 0765 if (property) 0766 { 0767 auto svp = property->getSwitch(); 0768 0769 if (!svp) 0770 return; 0771 0772 if (index >= svp->count()) 0773 return; 0774 0775 auto sp = svp->at(index); 0776 0777 sp->reset(); 0778 sp->setState(ISS_ON); 0779 0780 sendNewSwitch(svp); 0781 } 0782 } 0783 } 0784 } 0785 } 0786 0787 bool ClientManagerLite::saveDisplayImage() 0788 { 0789 QString const dateTime = QDateTime::currentDateTime().toString("dd-MM-yyyy-hh-mm-ss"); 0790 QString const fileEnding = "kstars-lite-" + dateTime; 0791 QDir const dir(KSPaths::writableLocation(QStandardPaths::PicturesLocation) + "/" + qAppName()).path(); 0792 QFileInfo const file(QString("%1/%2.jpeg").arg(dir.path()).arg(fileEnding)); 0793 0794 QString const filename = QFileDialog::getSaveFileName( 0795 QApplication::activeWindow(), i18nc("@title:window", "Save Image"), file.filePath(), 0796 i18n("JPEG (*.jpeg);;JPG (*.jpg);;PNG (*.png);;BMP (*.bmp)")); 0797 0798 if (!filename.isEmpty()) 0799 { 0800 if (displayImage.save(filename)) 0801 { 0802 emit newINDIMessage("File " + filename + " was successfully saved"); 0803 return true; 0804 } 0805 } 0806 emit newINDIMessage("Couldn't save file " + filename); 0807 return false; 0808 } 0809 0810 bool ClientManagerLite::isDeviceConnected(const QString &deviceName) 0811 { 0812 INDI::BaseDevice *device = getDevice(deviceName.toStdString().c_str()); 0813 0814 if (device != nullptr) 0815 { 0816 return device->isConnected(); 0817 } 0818 return false; 0819 } 0820 0821 void ClientManagerLite::connectNewDevice(const QString& device_name) 0822 { 0823 connectDevice(qPrintable(device_name)); 0824 } 0825 0826 void ClientManagerLite::newDevice(INDI::BaseDevice *dp) 0827 { 0828 setBLOBMode(B_ALSO, dp->getDeviceName()); 0829 0830 QString deviceName = dp->getDeviceName(); 0831 0832 if (deviceName.isEmpty()) 0833 { 0834 qWarning() << "Received invalid device with empty name! Ignoring the device..."; 0835 return; 0836 } 0837 0838 if (Options::verboseLogging()) 0839 qDebug() << "Received new device " << deviceName; 0840 emit newINDIDevice(deviceName); 0841 0842 DeviceInfoLite *devInfo = new DeviceInfoLite(dp); 0843 //Think about it! 0844 //devInfo->telescope.reset(new TelescopeLite(dp)); 0845 m_devices.append(devInfo); 0846 // Connect the device automatically 0847 QTimer::singleShot(2000, [=]() { connectNewDevice(deviceName); }); 0848 } 0849 0850 void ClientManagerLite::removeDevice(BaseDevice *dp) 0851 { 0852 emit removeINDIDevice(QString(dp->getDeviceName())); 0853 } 0854 0855 void ClientManagerLite::newProperty(INDI::Property property) 0856 { 0857 QString deviceName = property->getDeviceName(); 0858 QString name = property->getName(); 0859 QString groupName = property->getGroupName(); 0860 QString type = QString(property->getType()); 0861 QString label = property->getLabel(); 0862 DeviceInfoLite *devInfo = nullptr; 0863 0864 foreach (DeviceInfoLite *di, m_devices) 0865 { 0866 if (di->device->getDeviceName() == deviceName) 0867 { 0868 devInfo = di; 0869 } 0870 } 0871 0872 if (devInfo) 0873 { 0874 if ((!strcmp(property->getName(), "EQUATORIAL_EOD_COORD") || 0875 !strcmp(property->getName(), "EQUATORIAL_COORD") || 0876 !strcmp(property->getName(), "HORIZONTAL_COORD"))) 0877 { 0878 devInfo->telescope.reset(new TelescopeLite(devInfo->device)); 0879 m_telescope = devInfo->telescope.get(); 0880 emit telescopeAdded(m_telescope); 0881 // The connected signal must be emitted for already connected scopes otherwise 0882 // the motion control page remains disabled. 0883 if (devInfo->telescope->isConnected()) 0884 { 0885 emit deviceConnected(devInfo->telescope->getDeviceName(), true); 0886 emit telescopeConnected(devInfo->telescope.get()); 0887 } 0888 } 0889 } 0890 0891 emit newINDIProperty(deviceName, name, groupName, type, label); 0892 PGui guiType; 0893 switch (property->getType()) 0894 { 0895 case INDI_SWITCH: 0896 if (property->getSwitch()->r == ISR_NOFMANY) 0897 guiType = PG_RADIO; 0898 else if (property->getSwitch()->nsp > 4) 0899 guiType = PG_MENU; 0900 else 0901 guiType = PG_BUTTONS; 0902 0903 if (guiType == PG_MENU) 0904 buildMenuGUI(property); 0905 else 0906 buildSwitchGUI(property, guiType); 0907 break; 0908 0909 case INDI_TEXT: 0910 buildTextGUI(property); 0911 break; 0912 case INDI_NUMBER: 0913 buildNumberGUI(property); 0914 break; 0915 0916 case INDI_LIGHT: 0917 buildLightGUI(property); 0918 break; 0919 0920 case INDI_BLOB: 0921 //buildBLOBGUI(); 0922 break; 0923 0924 default: 0925 break; 0926 } 0927 } 0928 0929 void ClientManagerLite::removeProperty(INDI::Property property) 0930 { 0931 if (property == nullptr) 0932 return; 0933 0934 emit removeINDIProperty(property->getDeviceName(), property->getGroupName(), property->getName()); 0935 0936 DeviceInfoLite *devInfo = nullptr; 0937 foreach (DeviceInfoLite *di, m_devices) 0938 { 0939 if (di->device == property->getBaseDevice()) 0940 { 0941 devInfo = di; 0942 } 0943 } 0944 0945 if (devInfo) 0946 { 0947 if ((!strcmp(property->getName(), "EQUATORIAL_EOD_COORD") || !strcmp(property->getName(), "HORIZONTAL_COORD"))) 0948 { 0949 if (devInfo->telescope.get() != nullptr) 0950 { 0951 emit telescopeRemoved(devInfo->telescope.get()); 0952 } 0953 KStarsLite::Instance()->map()->update(); // Update SkyMap if position of telescope is changed 0954 } 0955 } 0956 } 0957 0958 void ClientManagerLite::newBLOB(IBLOB *bp) 0959 { 0960 processBLOBasCCD(bp); 0961 emit newLEDState(bp->bvp->device, bp->name); 0962 } 0963 0964 bool ClientManagerLite::processBLOBasCCD(IBLOB *bp) 0965 { 0966 enum blobType 0967 { 0968 BLOB_IMAGE, 0969 BLOB_FITS, 0970 BLOB_CR2, 0971 BLOB_OTHER 0972 } BType; 0973 0974 BType = BLOB_OTHER; 0975 0976 QString format(bp->format); 0977 QString deviceName = bp->bvp->device; 0978 0979 QByteArray fmt = QString(bp->format).toLower().remove('.').toUtf8(); 0980 0981 // If it's not FITS or an image, don't process it. 0982 if ((QImageReader::supportedImageFormats().contains(fmt))) 0983 BType = BLOB_IMAGE; 0984 else if (format.contains("fits")) 0985 BType = BLOB_FITS; 0986 else if (format.contains("cr2")) 0987 BType = BLOB_CR2; 0988 0989 if (BType == BLOB_OTHER) 0990 { 0991 return false; 0992 } 0993 0994 QString currentDir = QDir(KSPaths::writableLocation(QStandardPaths::TempLocation) + "/" + qAppName()).path(); 0995 0996 int nr, n = 0; 0997 QTemporaryFile tmpFile(QDir::tempPath() + "/fitsXXXXXX"); 0998 0999 if (currentDir.endsWith('/')) 1000 currentDir.chop(1); 1001 1002 if (QDir(currentDir).exists() == false) 1003 QDir().mkpath(currentDir); 1004 1005 QString filename(currentDir + '/'); 1006 1007 if (true) 1008 { 1009 tmpFile.setAutoRemove(false); 1010 1011 if (!tmpFile.open()) 1012 { 1013 qDebug() << "ISD:CCD Error: Unable to open " << filename << endl; 1014 //emit BLOBUpdated(nullptr); 1015 return false; 1016 } 1017 1018 QDataStream out(&tmpFile); 1019 1020 for (nr = 0; nr < (int)bp->size; nr += n) 1021 n = out.writeRawData(static_cast<char *>(bp->blob) + nr, bp->size - nr); 1022 1023 tmpFile.close(); 1024 1025 filename = tmpFile.fileName(); 1026 } 1027 else 1028 { 1029 //Add support for batch mode 1030 } 1031 1032 strncpy(BLOBFilename, filename.toLatin1(), MAXINDIFILENAME); 1033 bp->aux2 = BLOBFilename; 1034 1035 /* Test images 1036 BType = BLOB_IMAGE; 1037 filename = "/home/polaris/Pictures/351181_0.jpeg"; 1038 */ 1039 /*Test CR2 1040 BType = BLOB_CR2; 1041 1042 filename = "/home/polaris/test.CR2"; 1043 filename = "/storage/emulated/0/test.CR2";*/ 1044 1045 if (BType == BLOB_IMAGE || BType == BLOB_CR2) 1046 { 1047 if (BType == BLOB_CR2) 1048 { 1049 #ifdef Q_OS_ANDROID 1050 LibRaw RawProcessor; 1051 #define OUT RawProcessor.imgdata.params 1052 OUT.user_qual = 0; // -q 1053 OUT.use_camera_wb = 1; // -w 1054 OUT.highlight = 5; // -H 1055 OUT.bright = 8; // -b 1056 #undef OUT 1057 1058 QString rawFileName = filename; 1059 rawFileName = rawFileName.remove(0, rawFileName.lastIndexOf(QLatin1String("/"))); 1060 QString templateName = QString("%1/%2.XXXXXX").arg(QDir::tempPath()).arg(rawFileName); 1061 QTemporaryFile jpgPreview(templateName); 1062 jpgPreview.setAutoRemove(false); 1063 jpgPreview.open(); 1064 jpgPreview.close(); 1065 QString jpeg_filename = jpgPreview.fileName(); 1066 1067 RawProcessor.open_file(filename.toLatin1()); 1068 RawProcessor.unpack(); 1069 RawProcessor.dcraw_process(); 1070 RawProcessor.dcraw_ppm_tiff_writer(jpeg_filename.toLatin1()); 1071 QFile::remove(filename); 1072 filename = jpeg_filename; 1073 #else 1074 if (QStandardPaths::findExecutable("dcraw").isEmpty() == false && 1075 QStandardPaths::findExecutable("cjpeg").isEmpty() == false) 1076 { 1077 QProcess dcraw; 1078 QString rawFileName = filename; 1079 rawFileName = rawFileName.remove(0, rawFileName.lastIndexOf(QLatin1String("/"))); 1080 QString templateName = QString("%1/%2.XXXXXX").arg(QDir::tempPath()).arg(rawFileName); 1081 QTemporaryFile jpgPreview(templateName); 1082 jpgPreview.setAutoRemove(false); 1083 jpgPreview.open(); 1084 jpgPreview.close(); 1085 QString jpeg_filename = jpgPreview.fileName(); 1086 1087 QString cmd = QString("/bin/sh -c \"dcraw -c -q 0 -w -H 5 -b 8 %1 | cjpeg -quality 80 > %2\"") 1088 .arg(filename) 1089 .arg(jpeg_filename); 1090 dcraw.start(cmd); 1091 dcraw.waitForFinished(); 1092 QFile::remove(filename); //Delete raw 1093 filename = jpeg_filename; 1094 } 1095 else 1096 { 1097 emit newINDIMessage( 1098 i18n("Unable to find dcraw and cjpeg. Please install the required tools to convert CR2 to JPEG.")); 1099 emit newINDIBLOBImage(deviceName, false); 1100 return false; 1101 } 1102 #endif 1103 } 1104 1105 displayImage.load(filename); 1106 QFile::remove(filename); 1107 KStarsLite::Instance()->imageProvider()->addImage("ccdPreview", displayImage); 1108 emit newINDIBLOBImage(deviceName, true); 1109 return true; 1110 } 1111 else if (BType == BLOB_FITS) 1112 { 1113 displayImage = FITSData::FITSToImage(filename); 1114 QFile::remove(filename); 1115 KStarsLite::Instance()->imageProvider()->addImage("ccdPreview", displayImage); 1116 emit newINDIBLOBImage(deviceName, true); 1117 return true; 1118 } 1119 emit newINDIBLOBImage(deviceName, false); 1120 return false; 1121 } 1122 1123 void ClientManagerLite::newSwitch(ISwitchVectorProperty *svp) 1124 { 1125 for (int i = 0; i < svp->nsp; ++i) 1126 { 1127 ISwitch *sw = &(svp->sp[i]); 1128 if (QString(sw->name) == QString("CONNECT")) 1129 { 1130 emit deviceConnected(svp->device, sw->s == ISS_ON); 1131 if (m_telescope && m_telescope->getDeviceName() == svp->device) 1132 { 1133 if (sw->s == ISS_ON) 1134 { 1135 emit telescopeConnected(m_telescope); 1136 } else { 1137 emit telescopeDisconnected(); 1138 } 1139 } 1140 } 1141 if (sw != nullptr) 1142 { 1143 emit newINDISwitch(svp->device, svp->name, sw->name, sw->s == ISS_ON); 1144 emit newLEDState(svp->device, svp->name); 1145 } 1146 } 1147 } 1148 1149 void ClientManagerLite::newNumber(INumberVectorProperty *nvp) 1150 { 1151 if ((!strcmp(nvp->name, "EQUATORIAL_EOD_COORD") || !strcmp(nvp->name, "HORIZONTAL_COORD"))) 1152 { 1153 KStarsLite::Instance()->map()->update(); // Update SkyMap if position of telescope is changed 1154 } 1155 1156 QString deviceName = nvp->device; 1157 QString propName = nvp->name; 1158 for (int i = 0; i < nvp->nnp; ++i) 1159 { 1160 INumber num = nvp->np[i]; 1161 char buf[MAXINDIFORMAT]; 1162 numberFormat(buf, num.format, num.value); 1163 QString numberName = num.name; 1164 1165 emit newINDINumber(deviceName, propName, numberName, QString(buf).trimmed()); 1166 emit newLEDState(deviceName, propName); 1167 } 1168 } 1169 1170 void ClientManagerLite::newText(ITextVectorProperty *tvp) 1171 { 1172 QString deviceName = tvp->device; 1173 QString propName = tvp->name; 1174 for (int i = 0; i < tvp->ntp; ++i) 1175 { 1176 IText text = tvp->tp[i]; 1177 QString fieldName = text.name; 1178 1179 emit newINDIText(deviceName, propName, fieldName, text.text); 1180 emit newLEDState(deviceName, propName); 1181 } 1182 } 1183 1184 void ClientManagerLite::newLight(ILightVectorProperty *lvp) 1185 { 1186 emit newINDILight(lvp->device, lvp->name); 1187 emit newLEDState(lvp->device, lvp->name); 1188 } 1189 1190 void ClientManagerLite::newMessage(INDI::BaseDevice *dp, int messageID) 1191 { 1192 emit newINDIMessage(QString::fromStdString(dp->messageQueue(messageID))); 1193 } 1194 1195 void ClientManagerLite::serverDisconnected(int exit_code) 1196 { 1197 Q_UNUSED(exit_code) 1198 clearDevices(); 1199 setConnected(false); 1200 } 1201 1202 void ClientManagerLite::clearDevices() 1203 { 1204 //Delete all created devices 1205 foreach (DeviceInfoLite *devInfo, m_devices) 1206 { 1207 if (devInfo->telescope.get() != nullptr) 1208 { 1209 emit telescopeRemoved(devInfo->telescope.get()); 1210 } 1211 delete devInfo; 1212 } 1213 m_devices.clear(); 1214 } 1215 1216 QString ClientManagerLite::getLastUsedServer() 1217 { 1218 return Options::lastServer(); 1219 } 1220 1221 void ClientManagerLite::setLastUsedServer(const QString &server) 1222 { 1223 if (getLastUsedServer() != server) 1224 { 1225 Options::setLastServer(server); 1226 lastUsedServerChanged(); 1227 } 1228 } 1229 1230 int ClientManagerLite::getLastUsedPort() 1231 { 1232 return Options::lastServerPort(); 1233 } 1234 1235 void ClientManagerLite::setLastUsedPort(int port) 1236 { 1237 if (getLastUsedPort() != port) 1238 { 1239 Options::setLastServerPort(port); 1240 lastUsedPortChanged(); 1241 } 1242 } 1243 1244 int ClientManagerLite::getLastUsedWebManagerPort() 1245 { 1246 return Options::lastWebManagerPort(); 1247 } 1248 1249 void ClientManagerLite::setLastUsedWebManagerPort(int port) 1250 { 1251 if (getLastUsedWebManagerPort() != port) 1252 { 1253 Options::setLastWebManagerPort(port); 1254 lastUsedWebManagerPortChanged(); 1255 } 1256 }