File indexing completed on 2024-04-21 14:45:04
0001 /* Ekos Live Message 0002 0003 SPDX-FileCopyrightText: 2018 Jasem Mutlaq <mutlaqja@ikarustech.com> 0004 0005 Message Channel 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "message.h" 0011 #include "commands.h" 0012 #include "profileinfo.h" 0013 #include "indi/drivermanager.h" 0014 #include "indi/indilistener.h" 0015 #include "auxiliary/ksmessagebox.h" 0016 #include "ekos/auxiliary/filtermanager.h" 0017 #include "ekos/auxiliary/opticaltrainmanager.h" 0018 #include "ekos/auxiliary/profilesettings.h" 0019 #include "ekos/capture/capture.h" 0020 #include "ekos/guide/guide.h" 0021 #include "ekos/mount/mount.h" 0022 #include "ekos/scheduler/scheduler.h" 0023 #include "ekos/scheduler/schedulermodulestate.h" 0024 #include "kstars.h" 0025 #include "kstarsdata.h" 0026 #include "ekos_debug.h" 0027 #include "ksalmanac.h" 0028 #include "skymapcomposite.h" 0029 #include "catalogobject.h" 0030 #include "ekos/auxiliary/darklibrary.h" 0031 #include "skymap.h" 0032 #include "Options.h" 0033 #include "version.h" 0034 0035 #include <KActionCollection> 0036 #include <basedevice.h> 0037 #include <QUuid> 0038 0039 namespace EkosLive 0040 { 0041 Message::Message(Ekos::Manager *manager, QVector<QSharedPointer<NodeManager>> &nodeManagers): 0042 m_Manager(manager), m_NodeManagers(nodeManagers), m_DSOManager(CatalogsDB::dso_db_path()) 0043 { 0044 for (auto &nodeManager : m_NodeManagers) 0045 { 0046 connect(nodeManager->message(), &Node::connected, this, &Message::onConnected); 0047 connect(nodeManager->message(), &Node::disconnected, this, &Message::onDisconnected); 0048 connect(nodeManager->message(), &Node::onTextReceived, this, &Message::onTextReceived); 0049 } 0050 0051 connect(manager, &Ekos::Manager::newModule, this, &Message::sendModuleState); 0052 0053 m_ThrottleTS = QDateTime::currentDateTime(); 0054 0055 m_PendingPropertiesTimer.setInterval(500); 0056 connect(&m_PendingPropertiesTimer, &QTimer::timeout, this, &Message::sendPendingProperties); 0057 0058 m_DebouncedSend.setInterval(500); 0059 connect(&m_DebouncedSend, &QTimer::timeout, this, &Message::dispatchDebounceQueue); 0060 } 0061 0062 /////////////////////////////////////////////////////////////////////////////////////////// 0063 /// 0064 /////////////////////////////////////////////////////////////////////////////////////////// 0065 void Message::onConnected() 0066 { 0067 auto node = qobject_cast<Node*>(sender()); 0068 if (!node) 0069 return; 0070 0071 qCInfo(KSTARS_EKOS) << "Connected to Message Websocket server at" << node->url().toDisplayString(); 0072 0073 m_PendingPropertiesTimer.start(); 0074 sendConnection(); 0075 sendProfiles(); 0076 emit connected(); 0077 } 0078 0079 /////////////////////////////////////////////////////////////////////////////////////////// 0080 /// 0081 /////////////////////////////////////////////////////////////////////////////////////////// 0082 void Message::onDisconnected() 0083 { 0084 auto node = qobject_cast<Node*>(sender()); 0085 if (!node) 0086 return; 0087 0088 qCInfo(KSTARS_EKOS) << "Disconnected from Message Websocket server at" << node->url().toDisplayString(); 0089 0090 if (isConnected() == false) 0091 { 0092 m_PendingPropertiesTimer.stop(); 0093 emit disconnected(); 0094 } 0095 } 0096 0097 /////////////////////////////////////////////////////////////////////////////////////////// 0098 /// 0099 /////////////////////////////////////////////////////////////////////////////////////////// 0100 void Message::onTextReceived(const QString &message) 0101 { 0102 auto node = qobject_cast<Node*>(sender()); 0103 if (!node || message.isEmpty()) 0104 return; 0105 0106 qCInfo(KSTARS_EKOS) << "Websocket Message" << message; 0107 QJsonParseError error; 0108 auto serverMessage = QJsonDocument::fromJson(message.toUtf8(), &error); 0109 if (error.error != QJsonParseError::NoError) 0110 { 0111 qCWarning(KSTARS_EKOS) << "Ekos Live Parsing Error" << error.errorString(); 0112 return; 0113 } 0114 0115 const QJsonObject msgObj = serverMessage.object(); 0116 const QString command = msgObj["type"].toString(); 0117 const QJsonObject payload = msgObj["payload"].toObject(); 0118 0119 if (command == commands[GET_CONNECTION]) 0120 { 0121 sendConnection(); 0122 } 0123 else if (command == commands[LOGOUT]) 0124 { 0125 emit expired(node->url()); 0126 return; 0127 } 0128 else if (command == commands[SET_CLIENT_STATE]) 0129 { 0130 // If client is connected, make sure clock is ticking 0131 if (payload["state"].toBool(false)) 0132 { 0133 qCInfo(KSTARS_EKOS) << "EkosLive client is connected."; 0134 0135 // If the clock is PAUSED, run it now and sync time as well. 0136 if (KStarsData::Instance()->clock()->isActive() == false) 0137 { 0138 qCInfo(KSTARS_EKOS) << "Resuming and syncing clock."; 0139 KStarsData::Instance()->clock()->start(); 0140 QAction *a = KStars::Instance()->actionCollection()->action("time_to_now"); 0141 if (a) 0142 a->trigger(); 0143 } 0144 } 0145 // Otherwise, if KStars was started in PAUSED state 0146 // then we pause here as well to save power. 0147 else 0148 { 0149 qCInfo(KSTARS_EKOS) << "EkosLive client is disconnected."; 0150 // It was started with paused state, so let's pause IF Ekos is not running 0151 if (KStars::Instance()->isStartedWithClockRunning() == false && m_Manager->ekosStatus() == Ekos::CommunicationStatus::Idle) 0152 { 0153 qCInfo(KSTARS_EKOS) << "Stopping the clock."; 0154 KStarsData::Instance()->clock()->stop(); 0155 } 0156 } 0157 } 0158 else if (command == commands[GET_DRIVERS]) 0159 sendDrivers(); 0160 else if (command == commands[GET_PROFILES]) 0161 sendProfiles(); 0162 else if (command == commands[GET_SCOPES]) 0163 sendScopes(); 0164 else if (command == commands[GET_DSLR_LENSES]) 0165 sendDSLRLenses(); 0166 else if(command == commands[INVOKE_METHOD]) 0167 { 0168 auto object = findObject(payload["object"].toString()); 0169 if (object) 0170 invokeMethod(object, payload); 0171 } 0172 else if(command == commands[SET_PROPERTY]) 0173 { 0174 auto object = findObject(payload["object"].toString()); 0175 if (object) 0176 object->setProperty(payload["name"].toString().toLatin1().constData(), payload["value"].toVariant()); 0177 } 0178 else if(command == commands[GET_PROPERTY]) 0179 { 0180 auto map = QVariantMap(); 0181 map["result"] = false; 0182 auto object = findObject(payload["object"].toString()); 0183 if (object) 0184 { 0185 auto value = object->property(payload["name"].toString().toLatin1().constData()); 0186 if (value.isValid()) 0187 { 0188 map["result"] = true; 0189 map["value"] = value; 0190 } 0191 } 0192 sendResponse(commands[GET_PROPERTY], QJsonObject::fromVariantMap(map)); 0193 } 0194 else if (command.startsWith("scope_")) 0195 processScopeCommands(command, payload); 0196 else if (command.startsWith("profile_")) 0197 processProfileCommands(command, payload); 0198 else if (command.startsWith("astro_")) 0199 processAstronomyCommands(command, payload); 0200 else if (command == commands[DIALOG_GET_RESPONSE]) 0201 processDialogResponse(payload); 0202 else if (command.startsWith("option_")) 0203 processOptionsCommands(command, payload); 0204 else if (command.startsWith("scheduler")) 0205 processSchedulerCommands(command, payload); 0206 else if (command.startsWith("dslr_")) 0207 processDSLRCommands(command, payload); 0208 0209 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 0210 return; 0211 0212 if (command == commands[GET_STATES]) 0213 sendStates(); 0214 else if (command == commands[GET_STELLARSOLVER_PROFILES]) 0215 sendStellarSolverProfiles(); 0216 else if (command == commands[GET_DEVICES]) 0217 sendDevices(); 0218 else if (command.startsWith("capture_")) 0219 processCaptureCommands(command, payload); 0220 else if (command.startsWith("mount_")) 0221 processMountCommands(command, payload); 0222 else if (command.startsWith("focus_")) 0223 processFocusCommands(command, payload); 0224 else if (command.startsWith("guide_")) 0225 processGuideCommands(command, payload); 0226 else if (command.startsWith("align_")) 0227 processAlignCommands(command, payload); 0228 else if (command.startsWith("polar_")) 0229 processPolarCommands(command, payload); 0230 else if (command.startsWith("train_")) 0231 processTrainCommands(command, payload); 0232 else if (command.startsWith("fm_")) 0233 processFilterManagerCommands(command, payload); 0234 else if (command.startsWith("dark_library_")) 0235 processDarkLibraryCommands(command, payload); 0236 else if (command.startsWith("device_")) 0237 processDeviceCommands(command, payload); 0238 0239 } 0240 0241 /////////////////////////////////////////////////////////////////////////////////////////// 0242 /// 0243 /////////////////////////////////////////////////////////////////////////////////////////// 0244 bool Message::isConnected() const 0245 { 0246 return std::any_of(m_NodeManagers.begin(), m_NodeManagers.end(), [](auto & nodeManager) 0247 { 0248 return nodeManager->message()->isConnected(); 0249 }); 0250 } 0251 0252 /////////////////////////////////////////////////////////////////////////////////////////// 0253 /// 0254 /////////////////////////////////////////////////////////////////////////////////////////// 0255 void Message::sendStellarSolverProfiles() 0256 { 0257 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 0258 return; 0259 0260 QJsonObject profiles; 0261 0262 if (m_Manager->focusModule()) 0263 profiles.insert("focus", QJsonArray::fromStringList(m_Manager->focusModule()->getStellarSolverProfiles())); 0264 // TODO 0265 // if (m_Manager->guideModule()) 0266 // profiles.insert("guide", QJsonArray::fromStringList(m_Manager->guideModule()->getStellarSolverProfiles())); 0267 if (m_Manager->alignModule()) 0268 profiles.insert("align", QJsonArray::fromStringList(m_Manager->alignModule()->getStellarSolverProfiles())); 0269 0270 0271 sendResponse(commands[GET_STELLARSOLVER_PROFILES], profiles); 0272 } 0273 0274 /////////////////////////////////////////////////////////////////////////////////////////// 0275 /// 0276 /////////////////////////////////////////////////////////////////////////////////////////// 0277 void Message::sendDrivers() 0278 { 0279 sendResponse(commands[GET_DRIVERS], DriverManager::Instance()->getDriverList()); 0280 } 0281 0282 /////////////////////////////////////////////////////////////////////////////////////////// 0283 /// 0284 /////////////////////////////////////////////////////////////////////////////////////////// 0285 void Message::sendDevices() 0286 { 0287 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 0288 return; 0289 0290 QJsonArray deviceList; 0291 0292 for(auto &gd : INDIListener::devices()) 0293 { 0294 QJsonObject oneDevice = 0295 { 0296 {"name", gd->getDeviceName()}, 0297 {"connected", gd->isConnected()}, 0298 {"version", gd->getDriverVersion()}, 0299 {"interface", static_cast<int>(gd->getDriverInterface())}, 0300 }; 0301 0302 deviceList.append(oneDevice); 0303 } 0304 0305 sendResponse(commands[GET_DEVICES], deviceList); 0306 } 0307 0308 /////////////////////////////////////////////////////////////////////////////////////////// 0309 /// 0310 /////////////////////////////////////////////////////////////////////////////////////////// 0311 void Message::sendTrains() 0312 { 0313 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 0314 return; 0315 0316 QJsonArray trains; 0317 0318 for(auto &train : Ekos::OpticalTrainManager::Instance()->getOpticalTrains()) 0319 trains.append(QJsonObject::fromVariantMap(train)); 0320 0321 sendResponse(commands[TRAIN_GET_ALL], trains); 0322 } 0323 0324 /////////////////////////////////////////////////////////////////////////////////////////// 0325 /// 0326 /////////////////////////////////////////////////////////////////////////////////////////// 0327 void Message::sendTrainProfiles() 0328 { 0329 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 0330 return; 0331 0332 auto profiles = Ekos::ProfileSettings::Instance()->getSettings(); 0333 0334 sendResponse(commands[TRAIN_GET_PROFILES], QJsonObject::fromVariantMap(profiles)); 0335 } 0336 0337 /////////////////////////////////////////////////////////////////////////////////////////// 0338 /// 0339 /////////////////////////////////////////////////////////////////////////////////////////// 0340 void Message::requestOpticalTrains(bool show) 0341 { 0342 sendResponse(commands[TRAIN_CONFIGURATION_REQUESTED], show); 0343 } 0344 0345 /////////////////////////////////////////////////////////////////////////////////////////// 0346 /// 0347 /////////////////////////////////////////////////////////////////////////////////////////// 0348 void Message::sendScopes() 0349 { 0350 QJsonArray scopeList; 0351 0352 QList<OAL::Scope *> allScopes; 0353 KStarsData::Instance()->userdb()->GetAllScopes(allScopes); 0354 0355 for (auto &scope : allScopes) 0356 scopeList.append(scope->toJson()); 0357 0358 sendResponse(commands[GET_SCOPES], scopeList); 0359 } 0360 0361 /////////////////////////////////////////////////////////////////////////////////////////// 0362 /// 0363 /////////////////////////////////////////////////////////////////////////////////////////// 0364 void Message::sendDSLRLenses() 0365 { 0366 QJsonArray dslrList; 0367 0368 QList<OAL::DSLRLens *> allDslrLens; 0369 KStarsData::Instance()->userdb()->GetAllDSLRLenses(allDslrLens); 0370 0371 for (auto &dslrLens : allDslrLens) 0372 dslrList.append(dslrLens->toJson()); 0373 0374 sendResponse(commands[GET_DSLR_LENSES], dslrList); 0375 } 0376 0377 /////////////////////////////////////////////////////////////////////////////////////////// 0378 /// 0379 /////////////////////////////////////////////////////////////////////////////////////////// 0380 void Message::sendTemperature(double value) 0381 { 0382 ISD::Camera *oneCCD = dynamic_cast<ISD::Camera*>(sender()); 0383 0384 if (oneCCD) 0385 { 0386 QJsonObject temperature = 0387 { 0388 {"name", oneCCD->getDeviceName()}, 0389 {"temperature", value} 0390 }; 0391 0392 sendResponse(commands[NEW_CAMERA_STATE], temperature); 0393 } 0394 } 0395 0396 /////////////////////////////////////////////////////////////////////////////////////////// 0397 /// 0398 /////////////////////////////////////////////////////////////////////////////////////////// 0399 void Message::setCapturePresetSettings(const QJsonObject &settings) 0400 { 0401 m_Manager->captureModule()->setPresetSettings(settings); 0402 } 0403 0404 /////////////////////////////////////////////////////////////////////////////////////////// 0405 /// 0406 /////////////////////////////////////////////////////////////////////////////////////////// 0407 void Message::processCaptureCommands(const QString &command, const QJsonObject &payload) 0408 { 0409 Ekos::Capture *capture = m_Manager->captureModule(); 0410 0411 if (capture == nullptr) 0412 { 0413 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as capture module is not available"; 0414 return; 0415 } 0416 0417 if (command == commands[CAPTURE_PREVIEW]) 0418 { 0419 setCapturePresetSettings(payload); 0420 capture->capturePreview(); 0421 } 0422 else if (command == commands[CAPTURE_TOGGLE_VIDEO]) 0423 { 0424 capture->setVideoLimits(payload["maxBufferSize"].toInt(512), payload["maxPreviewFPS"].toInt(10)); 0425 capture->toggleVideo(payload["enabled"].toBool()); 0426 } 0427 else if (command == commands[CAPTURE_START]) 0428 capture->start(); 0429 else if (command == commands[CAPTURE_STOP]) 0430 capture->stop(); 0431 else if (command == commands[CAPTURE_LOOP]) 0432 { 0433 setCapturePresetSettings(payload); 0434 capture->startFraming(); 0435 } 0436 else if (command == commands[CAPTURE_GET_SEQUENCES]) 0437 { 0438 sendCaptureSequence(capture->getSequence()); 0439 } 0440 else if(command == commands[CAPTURE_SET_FILE_SETTINGS]) 0441 { 0442 m_Manager->captureModule()->setFileSettings(payload); 0443 } 0444 else if (command == commands[CAPTURE_ADD_SEQUENCE]) 0445 { 0446 // Set capture settings first 0447 setCapturePresetSettings(payload["preset"].toObject()); 0448 0449 // Then sequence settings 0450 capture->setCount(static_cast<uint16_t>(payload["count"].toInt())); 0451 capture->setDelay(static_cast<uint16_t>(payload["delay"].toInt())); 0452 0453 // File Settings 0454 m_Manager->captureModule()->setFileSettings(payload["file"].toObject()); 0455 0456 // Calibration Settings 0457 m_Manager->captureModule()->setCalibrationSettings(payload["calibration"].toObject()); 0458 0459 // Now add job 0460 capture->createJob(); 0461 } 0462 else if (command == commands[CAPTURE_REMOVE_SEQUENCE]) 0463 { 0464 if (capture->removeJob(payload["index"].toInt()) == false) 0465 sendCaptureSequence(capture->getSequence()); 0466 } 0467 else if (command == commands[CAPTURE_CLEAR_SEQUENCES]) 0468 { 0469 capture->clearSequenceQueue(); 0470 } 0471 else if (command == commands[CAPTURE_SET_LIMITS]) 0472 { 0473 capture->setLimitSettings(payload); 0474 } 0475 else if (command == commands[CAPTURE_GET_LIMITS]) 0476 { 0477 sendResponse(commands[CAPTURE_GET_LIMITS], capture->getLimitSettings()); 0478 } 0479 else if (command == commands[CAPTURE_SAVE_SEQUENCE_FILE]) 0480 { 0481 capture->saveSequenceQueue(payload["filepath"].toString()); 0482 } 0483 else if (command == commands[CAPTURE_LOAD_SEQUENCE_FILE]) 0484 { 0485 capture->loadSequenceQueue(payload["filepath"].toString()); 0486 } 0487 else if (command == commands[CAPTURE_GET_CALIBRATION_SETTINGS]) 0488 { 0489 sendResponse(commands[CAPTURE_GET_CALIBRATION_SETTINGS], capture->getCalibrationSettings()); 0490 } 0491 else if (command == commands[CAPTURE_GET_FILE_SETTINGS]) 0492 { 0493 sendResponse(commands[CAPTURE_GET_FILE_SETTINGS], capture->getFileSettings()); 0494 } 0495 else if (command == commands[CAPTURE_GENERATE_DARK_FLATS]) 0496 { 0497 capture->generateDarkFlats(); 0498 } 0499 } 0500 0501 /////////////////////////////////////////////////////////////////////////////////////////// 0502 /// 0503 /////////////////////////////////////////////////////////////////////////////////////////// 0504 void Message::sendCaptureSequence(const QJsonArray &sequenceArray) 0505 { 0506 sendResponse(commands[CAPTURE_GET_SEQUENCES], sequenceArray); 0507 } 0508 0509 void Message::sendPreviewLabel(const QString &preview) 0510 { 0511 const QJsonObject payload = 0512 { 0513 {"preview", preview} 0514 }; 0515 sendResponse(commands[CAPTURE_GET_PREVIEW_LABEL], payload); 0516 } 0517 0518 /////////////////////////////////////////////////////////////////////////////////////////// 0519 /// 0520 /////////////////////////////////////////////////////////////////////////////////////////// 0521 void Message::sendCaptureSettings(const QJsonObject &settings) 0522 { 0523 sendResponse(commands[CAPTURE_SET_SETTINGS], settings); 0524 } 0525 0526 /////////////////////////////////////////////////////////////////////////////////////////// 0527 /// 0528 /////////////////////////////////////////////////////////////////////////////////////////// 0529 void Message::sendAlignSettings(const QVariantMap &settings) 0530 { 0531 m_DebouncedSend.start(); 0532 m_DebouncedMap[commands[ALIGN_GET_ALL_SETTINGS]] = settings; 0533 } 0534 0535 /////////////////////////////////////////////////////////////////////////////////////////// 0536 /// 0537 /////////////////////////////////////////////////////////////////////////////////////////// 0538 void Message::sendGuideSettings(const QVariantMap &settings) 0539 { 0540 m_DebouncedSend.start(); 0541 m_DebouncedMap[commands[GUIDE_GET_ALL_SETTINGS]] = settings; 0542 0543 } 0544 0545 /////////////////////////////////////////////////////////////////////////////////////////// 0546 /// 0547 /////////////////////////////////////////////////////////////////////////////////////////// 0548 void Message::sendFocusSettings(const QVariantMap &settings) 0549 { 0550 m_DebouncedSend.start(); 0551 m_DebouncedMap[commands[FOCUS_GET_ALL_SETTINGS]] = settings; 0552 } 0553 0554 /////////////////////////////////////////////////////////////////////////////////////////// 0555 /// 0556 /////////////////////////////////////////////////////////////////////////////////////////// 0557 void Message::sendMountSettings(const QVariantMap &settings) 0558 { 0559 m_DebouncedSend.start(); 0560 m_DebouncedMap[commands[MOUNT_GET_ALL_SETTINGS]] = settings; 0561 } 0562 0563 /////////////////////////////////////////////////////////////////////////////////////////// 0564 /// 0565 /////////////////////////////////////////////////////////////////////////////////////////// 0566 void Message::sendDarkLibrarySettings(const QVariantMap &settings) 0567 { 0568 m_DebouncedSend.start(); 0569 m_DebouncedMap[commands[DARK_LIBRARY_GET_ALL_SETTINGS]] = settings; 0570 } 0571 0572 0573 /////////////////////////////////////////////////////////////////////////////////////////// 0574 /// 0575 /////////////////////////////////////////////////////////////////////////////////////////// 0576 void Message::sendSchedulerSettings(const QVariantMap &settings) 0577 { 0578 m_DebouncedSend.start(); 0579 m_DebouncedMap[commands[SCHEDULER_GET_ALL_SETTINGS]] = settings; 0580 } 0581 0582 /////////////////////////////////////////////////////////////////////////////////////////// 0583 /// 0584 /////////////////////////////////////////////////////////////////////////////////////////// 0585 void Message::dispatchDebounceQueue() 0586 { 0587 QMapIterator<QString, QVariantMap> i(m_DebouncedMap); 0588 while (i.hasNext()) 0589 { 0590 i.next(); 0591 sendResponse(i.key(), QJsonObject::fromVariantMap(i.value())); 0592 } 0593 m_DebouncedMap.clear(); 0594 0595 // Save to disk 0596 Options::self()->save(); 0597 } 0598 0599 /////////////////////////////////////////////////////////////////////////////////////////// 0600 /// 0601 /////////////////////////////////////////////////////////////////////////////////////////// 0602 void Message::processGuideCommands(const QString &command, const QJsonObject &payload) 0603 { 0604 Ekos::Guide *guide = m_Manager->guideModule(); 0605 0606 if (guide == nullptr) 0607 { 0608 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as guide module is not available"; 0609 return; 0610 } 0611 0612 if (command == commands[GUIDE_START]) 0613 { 0614 guide->guide(); 0615 } 0616 else if (command == commands[GUIDE_CAPTURE]) 0617 guide->capture(); 0618 else if (command == commands[GUIDE_LOOP]) 0619 guide->loop(); 0620 else if (command == commands[GUIDE_STOP]) 0621 guide->abort(); 0622 else if (command == commands[GUIDE_CLEAR]) 0623 guide->clearCalibration(); 0624 else if (command == commands[GUIDE_SET_ALL_SETTINGS]) 0625 { 0626 auto settings = payload.toVariantMap(); 0627 guide->setAllSettings(settings); 0628 KSUtils::setGlobalSettings(settings); 0629 } 0630 else if (command == commands[GUIDE_GET_ALL_SETTINGS]) 0631 sendGuideSettings(guide->getAllSettings()); 0632 else if(command == commands[GUIDE_SET_CALIBRATION_SETTINGS]) 0633 { 0634 0635 Options::setCalibrationPulseDuration(payload["pulse"].toInt()); 0636 Options::setGuideCalibrationBacklash(payload["max_move"].toInt()); 0637 Options::setTwoAxisEnabled(payload["two_axis"].toBool()); 0638 Options::setGuideAutoSquareSizeEnabled(payload["square_size"].toBool()); 0639 Options::setGuideCalibrationBacklash(payload["calibrationBacklash"].toBool()); 0640 Options::setResetGuideCalibration(payload["resetCalibration"].toBool()); 0641 Options::setReuseGuideCalibration(payload["reuseCalibration"].toBool()); 0642 Options::setReverseDecOnPierSideChange(payload["reverseCalibration"].toBool()); 0643 sendGuideSettings(m_Manager->guideModule()->getAllSettings()); 0644 } 0645 } 0646 0647 /////////////////////////////////////////////////////////////////////////////////////////// 0648 /// 0649 /////////////////////////////////////////////////////////////////////////////////////////// 0650 void Message::processFocusCommands(const QString &command, const QJsonObject &payload) 0651 { 0652 Ekos::Focus *focus = m_Manager->focusModule(); 0653 0654 if (focus == nullptr) 0655 { 0656 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as focus module is not available"; 0657 return; 0658 } 0659 0660 if (command == commands[FOCUS_START]) 0661 focus->start(); 0662 else if (command == commands[FOCUS_CAPTURE]) 0663 { 0664 focus->resetFrame(); 0665 focus->capture(); 0666 } 0667 else if (command == commands[FOCUS_STOP]) 0668 focus->abort(); 0669 else if (command == commands[FOCUS_RESET]) 0670 focus->resetFrame(); 0671 else if (command == commands[FOCUS_IN]) 0672 focus->focusIn(payload["steps"].toInt()); 0673 else if (command == commands[FOCUS_OUT]) 0674 focus->focusOut(payload["steps"].toInt()); 0675 else if (command == commands[FOCUS_LOOP]) 0676 focus->startFraming(); 0677 else if (command == commands[FOCUS_SET_ALL_SETTINGS]) 0678 { 0679 auto settings = payload.toVariantMap(); 0680 focus->setAllSettings(settings); 0681 KSUtils::setGlobalSettings(settings); 0682 } 0683 0684 else if (command == commands[FOCUS_GET_ALL_SETTINGS]) 0685 sendFocusSettings(focus->getAllSettings()); 0686 else if (command == commands[FOCUS_SET_CROSSHAIR]) 0687 { 0688 double x = payload["x"].toDouble(); 0689 double y = payload["y"].toDouble(); 0690 focus->selectFocusStarFraction(x, y); 0691 } 0692 } 0693 0694 /////////////////////////////////////////////////////////////////////////////////////////// 0695 /// 0696 /////////////////////////////////////////////////////////////////////////////////////////// 0697 void Message::processMountCommands(const QString &command, const QJsonObject &payload) 0698 { 0699 Ekos::Mount *mount = m_Manager->mountModule(); 0700 0701 if (mount == nullptr) 0702 { 0703 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as mount module is not available"; 0704 return; 0705 } 0706 0707 if (command == commands[MOUNT_ABORT]) 0708 mount->abort(); 0709 else if (command == commands[MOUNT_PARK]) 0710 mount->park(); 0711 else if (command == commands[MOUNT_UNPARK]) 0712 mount->unpark(); 0713 else if (command == commands[MOUNT_SET_TRACKING]) 0714 mount->setTrackEnabled(payload["enabled"].toBool()); 0715 else if (command == commands[MOUNT_SYNC_RADE]) 0716 { 0717 mount->setJ2000Enabled(payload["isJ2000"].toBool()); 0718 mount->sync(payload["ra"].toString(), payload["de"].toString()); 0719 } 0720 else if (command == commands[MOUNT_SYNC_TARGET]) 0721 { 0722 mount->syncTarget(payload["target"].toString()); 0723 } 0724 else if (command == commands[MOUNT_GOTO_RADE]) 0725 { 0726 mount->setJ2000Enabled(payload["isJ2000"].toBool()); 0727 mount->slew(payload["ra"].toString(), payload["de"].toString()); 0728 } 0729 else if (command == commands[MOUNT_GOTO_TARGET]) 0730 { 0731 mount->gotoTarget(payload["target"].toString()); 0732 } 0733 else if (command == commands[MOUNT_SET_SLEW_RATE]) 0734 { 0735 int rate = payload["rate"].toInt(-1); 0736 if (rate >= 0) 0737 mount->setSlewRate(rate); 0738 } 0739 else if (command == commands[MOUNT_SET_ALL_SETTINGS]) 0740 { 0741 auto settings = payload.toVariantMap(); 0742 mount->setAllSettings(settings); 0743 KSUtils::setGlobalSettings(settings); 0744 } 0745 else if (command == commands[MOUNT_GET_ALL_SETTINGS]) 0746 sendMountSettings(mount->getAllSettings()); 0747 else if (command == commands[MOUNT_SET_MOTION]) 0748 { 0749 QString direction = payload["direction"].toString(); 0750 ISD::Mount::MotionCommand action = payload["action"].toBool(false) ? 0751 ISD::Mount::MOTION_START : ISD::Mount::MOTION_STOP; 0752 0753 if (direction == "N") 0754 mount->motionCommand(action, ISD::Mount::MOTION_NORTH, -1); 0755 else if (direction == "S") 0756 mount->motionCommand(action, ISD::Mount::MOTION_SOUTH, -1); 0757 else if (direction == "E") 0758 mount->motionCommand(action, -1, ISD::Mount::MOTION_EAST); 0759 else if (direction == "W") 0760 mount->motionCommand(action, -1, ISD::Mount::MOTION_WEST); 0761 } 0762 else if (command == commands[MOUNT_GOTO_PIXEL]) 0763 { 0764 const auto name = payload["camera"].toString(); 0765 const auto xFactor = payload["x"].toDouble(); 0766 const auto yFactor = payload["y"].toDouble(); 0767 0768 for(auto &oneDevice : INDIListener::devices()) 0769 { 0770 auto camera = oneDevice->getCamera(); 0771 if (!camera || camera->getDeviceName() != name) 0772 continue; 0773 0774 auto primaryChip = camera->getChip(ISD::CameraChip::PRIMARY_CCD); 0775 0776 if (!primaryChip) 0777 break; 0778 0779 auto imageData = primaryChip->getImageData(); 0780 if (!imageData || imageData->hasWCS() == false) 0781 break; 0782 0783 auto x = xFactor * imageData->width(); 0784 auto y = yFactor * imageData->height(); 0785 0786 QPointF point(x, y); 0787 SkyPoint coord; 0788 if (imageData->pixelToWCS(point, coord)) 0789 { 0790 // J2000 -> JNow 0791 coord.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd()); 0792 mount->gotoTarget(coord); 0793 break; 0794 } 0795 } 0796 } 0797 else if (command == commands[MOUNT_TOGGLE_AUTOPARK]) 0798 mount->setAutoParkEnabled(payload["toggled"].toBool()); 0799 } 0800 0801 /////////////////////////////////////////////////////////////////////////////////////////// 0802 /// 0803 /////////////////////////////////////////////////////////////////////////////////////////// 0804 void Message::processAlignCommands(const QString &command, const QJsonObject &payload) 0805 { 0806 Ekos::Align *align = m_Manager->alignModule(); 0807 0808 if (align == nullptr) 0809 { 0810 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as align module is not available"; 0811 return; 0812 } 0813 0814 if (command == commands[ALIGN_SOLVE]) 0815 { 0816 align->captureAndSolve(); 0817 } 0818 else if (command == commands[ALIGN_SET_ALL_SETTINGS]) 0819 { 0820 auto settings = payload.toVariantMap(); 0821 align->setAllSettings(settings); 0822 KSUtils::setGlobalSettings(settings); 0823 } 0824 else if (command == commands[ALIGN_GET_ALL_SETTINGS]) 0825 sendAlignSettings(align->getAllSettings()); 0826 else if(command == commands[ALIGN_SET_ASTROMETRY_SETTINGS]) 0827 { 0828 Options::setAstrometryRotatorThreshold(payload["threshold"].toInt()); 0829 Options::setAstrometryUseRotator(payload["rotator_control"].toBool()); 0830 Options::setAstrometryUseImageScale(payload["scale"].toBool()); 0831 Options::setAstrometryUsePosition(payload["position"].toBool()); 0832 } 0833 else if (command == commands[ALIGN_STOP]) 0834 align->abort(); 0835 else if (command == commands[ALIGN_LOAD_AND_SLEW]) 0836 { 0837 // Check if we have filename payload first 0838 if (payload.contains("filename")) 0839 { 0840 align->loadAndSlew(payload["filename"].toString()); 0841 } 0842 else 0843 { 0844 QString filename = QDir::tempPath() + QDir::separator() + 0845 QString("XXXXXXloadslew.%1").arg(payload["ext"].toString("fits")); 0846 QTemporaryFile file(filename); 0847 file.setAutoRemove(false); 0848 file.open(); 0849 file.write(QByteArray::fromBase64(payload["data"].toString().toLatin1())); 0850 file.close(); 0851 align->loadAndSlew(file.fileName()); 0852 } 0853 } 0854 else if (command == commands[ALIGN_MANUAL_ROTATOR_TOGGLE]) 0855 { 0856 align->toggleManualRotator(payload["toggled"].toBool()); 0857 } 0858 } 0859 0860 /////////////////////////////////////////////////////////////////////////////////////////// 0861 /// 0862 /////////////////////////////////////////////////////////////////////////////////////////// 0863 void Message::setAlignStatus(Ekos::AlignState newState) 0864 { 0865 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 0866 return; 0867 0868 QJsonObject alignState = 0869 { 0870 {"status", Ekos::alignStates[newState]} 0871 }; 0872 0873 sendResponse(commands[NEW_ALIGN_STATE], alignState); 0874 } 0875 0876 /////////////////////////////////////////////////////////////////////////////////////////// 0877 /// 0878 /////////////////////////////////////////////////////////////////////////////////////////// 0879 void Message::setAlignSolution(const QVariantMap &solution) 0880 { 0881 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 0882 return; 0883 0884 QJsonObject alignState = 0885 { 0886 {"solution", QJsonObject::fromVariantMap(solution)}, 0887 }; 0888 0889 sendResponse(commands[NEW_ALIGN_STATE], alignState); 0890 } 0891 0892 /////////////////////////////////////////////////////////////////////////////////////////// 0893 /// 0894 /////////////////////////////////////////////////////////////////////////////////////////// 0895 void Message::processSchedulerCommands(const QString &command, const QJsonObject &payload) 0896 { 0897 Ekos::Scheduler *scheduler = m_Manager->schedulerModule(); 0898 0899 if (command == commands[SCHEDULER_GET_JOBS]) 0900 { 0901 sendSchedulerJobs(); 0902 } 0903 else if (command == commands[SCHEDULER_ADD_JOBS]) 0904 { 0905 scheduler->addJob(); 0906 } 0907 else if(command == commands[SCHEDULER_REMOVE_JOBS]) 0908 { 0909 int index = payload["index"].toInt(); 0910 scheduler->removeOneJob(index); 0911 } 0912 else if(command == commands[SCHEDULER_GET_ALL_SETTINGS]) 0913 { 0914 sendSchedulerSettings(scheduler->getAllSettings()); 0915 } 0916 else if(command == commands[SCHEDULER_SET_ALL_SETTINGS]) 0917 { 0918 auto settings = payload.toVariantMap(); 0919 scheduler->setAllSettings(settings); 0920 KSUtils::setGlobalSettings(settings); 0921 } 0922 else if(command == commands[SCHEDULER_START_JOB]) 0923 { 0924 scheduler->toggleScheduler(); 0925 } 0926 else if(command == commands[SCHEDULER_IMPORT_MOSAIC]) 0927 { 0928 if (scheduler->importMosaic(payload)) 0929 sendSchedulerJobs(); 0930 else 0931 sendEvent(i18n("Mosaic import failed."), KSNotification::Scheduler, KSNotification::Alert); 0932 } 0933 } 0934 0935 /////////////////////////////////////////////////////////////////////////////////////////// 0936 /// 0937 /////////////////////////////////////////////////////////////////////////////////////////// 0938 void Message::processPolarCommands(const QString &command, const QJsonObject &payload) 0939 { 0940 Ekos::Align *align = m_Manager->alignModule(); 0941 Ekos::PolarAlignmentAssistant *paa = align->polarAlignmentAssistant(); 0942 0943 if (!paa) 0944 return; 0945 0946 if (command == commands[PAH_START]) 0947 { 0948 paa->startPAHProcess(); 0949 } 0950 if (command == commands[PAH_STOP]) 0951 { 0952 paa->stopPAHProcess(); 0953 } 0954 else if (command == commands[PAH_REFRESH]) 0955 { 0956 paa->setPAHRefreshDuration(payload["value"].toDouble(1)); 0957 paa->startPAHRefreshProcess(); 0958 } 0959 else if (command == commands[PAH_SET_ALGORITHM]) 0960 { 0961 auto algorithmCombo = paa->findChild<QComboBox*>("PAHRefreshAlgorithmCombo"); 0962 if (algorithmCombo) 0963 algorithmCombo->setCurrentIndex(static_cast<Ekos::PolarAlignmentAssistant::RefreshAlgorithm>(payload["value"].toInt(1))); 0964 } 0965 else if (command == commands[PAH_RESET_VIEW]) 0966 { 0967 emit resetPolarView(); 0968 } 0969 else if (command == commands[PAH_SET_CROSSHAIR]) 0970 { 0971 double x = payload["x"].toDouble(); 0972 double y = payload["y"].toDouble(); 0973 0974 if (m_BoundingRect.isNull() == false) 0975 { 0976 // #1 Find actual dimension inside the bounding rectangle 0977 // since if we have bounding rectable then x,y fractions are INSIDE it 0978 double boundX = x * m_BoundingRect.width(); 0979 double boundY = y * m_BoundingRect.height(); 0980 0981 // #2 Find fraction of the dimensions above the full image size 0982 // Add to it the bounding rect top left offsets 0983 // factors in the change caused by zoom 0984 x = ((boundX + m_BoundingRect.x()) / (m_CurrentZoom / 100)) / m_ViewSize.width(); 0985 y = ((boundY + m_BoundingRect.y()) / (m_CurrentZoom / 100)) / m_ViewSize.height(); 0986 0987 } 0988 0989 paa->setPAHCorrectionOffsetPercentage(x, y); 0990 } 0991 else if (command == commands[PAH_SELECT_STAR_DONE]) 0992 { 0993 // This button was removed from the desktop PAA scheme. 0994 // Nothing to do. 0995 // TODO: Make sure this works. 0996 } 0997 else if (command == commands[PAH_REFRESHING_DONE]) 0998 { 0999 paa->stopPAHProcess(); 1000 } 1001 else if (command == commands[PAH_SLEW_DONE]) 1002 { 1003 paa->setPAHSlewDone(); 1004 } 1005 else if (command == commands[PAH_PAH_SET_ZOOM]) 1006 { 1007 double scale = payload["scale"].toDouble(); 1008 align->setAlignZoom(scale); 1009 } 1010 1011 } 1012 1013 /////////////////////////////////////////////////////////////////////////////////////////// 1014 /// 1015 /////////////////////////////////////////////////////////////////////////////////////////// 1016 void Message::setPAHStage(Ekos::PolarAlignmentAssistant::Stage stage) 1017 { 1018 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success) 1019 return; 1020 1021 Q_UNUSED(stage) 1022 Ekos::Align *align = m_Manager->alignModule(); 1023 1024 Ekos::PolarAlignmentAssistant *paa = align->polarAlignmentAssistant(); 1025 1026 if (!paa) 1027 return; 1028 1029 QJsonObject polarState = 1030 { 1031 {"stage", paa->getPAHStageString(false)} 1032 }; 1033 1034 1035 // Increase size when select star 1036 if (stage == Ekos::PolarAlignmentAssistant::PAH_STAR_SELECT) 1037 align->zoomAlignView(); 1038 1039 sendResponse(commands[NEW_POLAR_STATE], polarState); 1040 } 1041 1042 /////////////////////////////////////////////////////////////////////////////////////////// 1043 /// 1044 /////////////////////////////////////////////////////////////////////////////////////////// 1045 void Message::setPAHMessage(const QString &message) 1046 { 1047 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success) 1048 return; 1049 1050 QTextDocument doc; 1051 doc.setHtml(message); 1052 QJsonObject polarState = 1053 { 1054 {"message", doc.toPlainText()} 1055 }; 1056 1057 sendResponse(commands[NEW_POLAR_STATE], polarState); 1058 } 1059 1060 /////////////////////////////////////////////////////////////////////////////////////////// 1061 /// 1062 /////////////////////////////////////////////////////////////////////////////////////////// 1063 void Message::setPolarResults(QLineF correctionVector, double polarError, double azError, double altError) 1064 { 1065 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success) 1066 return; 1067 1068 this->correctionVector = correctionVector; 1069 1070 QPointF center = 0.5 * correctionVector.p1() + 0.5 * correctionVector.p2(); 1071 QJsonObject vector = 1072 { 1073 {"center_x", center.x()}, 1074 {"center_y", center.y()}, 1075 {"mag", correctionVector.length()}, 1076 {"pa", correctionVector.angle()}, 1077 {"error", polarError}, 1078 {"azError", azError}, 1079 {"altError", altError} 1080 }; 1081 1082 QJsonObject polarState = 1083 { 1084 {"vector", vector} 1085 }; 1086 1087 sendResponse(commands[NEW_POLAR_STATE], polarState); 1088 } 1089 1090 /////////////////////////////////////////////////////////////////////////////////////////// 1091 /// 1092 /////////////////////////////////////////////////////////////////////////////////////////// 1093 void Message::setUpdatedErrors(double total, double az, double alt) 1094 { 1095 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success) 1096 return; 1097 1098 QJsonObject error = 1099 { 1100 {"updatedError", total}, 1101 {"updatedAZError", az}, 1102 {"updatedALTError", alt} 1103 }; 1104 1105 sendResponse(commands[NEW_POLAR_STATE], error); 1106 } 1107 1108 /////////////////////////////////////////////////////////////////////////////////////////// 1109 /// 1110 /////////////////////////////////////////////////////////////////////////////////////////// 1111 void Message::setPAHEnabled(bool enabled) 1112 { 1113 if (m_Manager->getEkosStartingStatus() != Ekos::Success) 1114 return; 1115 1116 QJsonObject polarState = 1117 { 1118 {"enabled", enabled} 1119 }; 1120 1121 sendResponse(commands[NEW_POLAR_STATE], polarState); 1122 } 1123 1124 /////////////////////////////////////////////////////////////////////////////////////////// 1125 /// 1126 /////////////////////////////////////////////////////////////////////////////////////////// 1127 void Message::processProfileCommands(const QString &command, const QJsonObject &payload) 1128 { 1129 if (command == commands[START_PROFILE]) 1130 { 1131 if (m_Manager->getEkosStartingStatus() != Ekos::Idle) 1132 m_Manager->stop(); 1133 1134 m_Manager->setProfile(payload["name"].toString()); 1135 // Always Sync time before we start 1136 KStarsData::Instance()->changeDateTime(KStarsDateTime::currentDateTimeUtc()); 1137 m_Manager->start(); 1138 } 1139 else if (command == commands[STOP_PROFILE]) 1140 { 1141 m_Manager->stop(); 1142 1143 // Close all FITS Viewers 1144 KStars::Instance()->clearAllViewers(); 1145 1146 m_PropertySubscriptions.clear(); 1147 } 1148 else if (command == commands[ADD_PROFILE]) 1149 { 1150 m_Manager->addNamedProfile(payload); 1151 sendProfiles(); 1152 } 1153 else if (command == commands[UPDATE_PROFILE]) 1154 { 1155 m_Manager->editNamedProfile(payload); 1156 sendProfiles(); 1157 } 1158 else if (command == commands[GET_PROFILE]) 1159 { 1160 m_Manager->getNamedProfile(payload["name"].toString()); 1161 } 1162 else if (command == commands[DELETE_PROFILE]) 1163 { 1164 m_Manager->deleteNamedProfile(payload["name"].toString()); 1165 sendProfiles(); 1166 } 1167 else if (command == commands[SET_PROFILE_MAPPING]) 1168 { 1169 m_Manager->setProfileMapping(payload); 1170 } 1171 else if (command == commands[SET_PROFILE_PORT_SELECTION]) 1172 { 1173 requestPortSelection(false); 1174 m_Manager->acceptPortSelection(); 1175 } 1176 } 1177 1178 /////////////////////////////////////////////////////////////////////////////////////////// 1179 /// 1180 /////////////////////////////////////////////////////////////////////////////////////////// 1181 void Message::sendProfiles() 1182 { 1183 QJsonArray profileArray; 1184 1185 QSharedPointer<ProfileInfo> profile; 1186 if (!m_Manager->getCurrentProfile(profile)) 1187 return; 1188 1189 for (auto &oneProfile : m_Manager->profiles) 1190 profileArray.append(oneProfile->toJson()); 1191 1192 QJsonObject profiles = 1193 { 1194 {"selectedProfile", profile->name}, 1195 {"profiles", profileArray} 1196 }; 1197 sendResponse(commands[GET_PROFILES], profiles); 1198 } 1199 1200 /////////////////////////////////////////////////////////////////////////////////////////// 1201 /// 1202 /////////////////////////////////////////////////////////////////////////////////////////// 1203 void Message::sendSchedulerJobs() 1204 { 1205 QJsonObject jobs = 1206 { 1207 {"jobs", m_Manager->schedulerModule()->moduleState()->getJSONJobs()} 1208 }; 1209 sendResponse(commands[SCHEDULER_GET_JOBS], jobs); 1210 } 1211 1212 /////////////////////////////////////////////////////////////////////////////////////////// 1213 /// 1214 /////////////////////////////////////////////////////////////////////////////////////////// 1215 void Message::sendSchedulerJobList(QJsonArray jobsList) 1216 { 1217 QJsonObject jobs = 1218 { 1219 {"jobs", jobsList} 1220 }; 1221 sendResponse(commands[SCHEDULER_GET_JOBS], jobs); 1222 } 1223 1224 /////////////////////////////////////////////////////////////////////////////////////////// 1225 /// 1226 /////////////////////////////////////////////////////////////////////////////////////////// 1227 void Message::sendSchedulerStatus(const QJsonObject &status) 1228 { 1229 sendResponse(commands[NEW_SCHEDULER_STATE], status); 1230 } 1231 1232 1233 /////////////////////////////////////////////////////////////////////////////////////////// 1234 /// 1235 /////////////////////////////////////////////////////////////////////////////////////////// 1236 void Message::setEkosStatingStatus(Ekos::CommunicationStatus status) 1237 { 1238 if (status == Ekos::Pending) 1239 return; 1240 1241 QJsonObject connectionState = 1242 { 1243 {"connected", true}, 1244 {"online", status == Ekos::Success} 1245 }; 1246 sendResponse(commands[NEW_CONNECTION_STATE], connectionState); 1247 } 1248 1249 /////////////////////////////////////////////////////////////////////////////////////////// 1250 /// 1251 /////////////////////////////////////////////////////////////////////////////////////////// 1252 void Message::setINDIStatus(Ekos::CommunicationStatus status) 1253 { 1254 QJsonObject connectionState = 1255 { 1256 {"status", status}, 1257 }; 1258 1259 sendResponse(commands[NEW_INDI_STATE], connectionState); 1260 } 1261 1262 /////////////////////////////////////////////////////////////////////////////////////////// 1263 /// 1264 /////////////////////////////////////////////////////////////////////////////////////////// 1265 void Message::processOptionsCommands(const QString &command, const QJsonObject &payload) 1266 { 1267 if (command == commands[OPTION_SET]) 1268 { 1269 const QJsonArray options = payload["options"].toArray(); 1270 for (const auto &oneOption : options) 1271 Options::self()->setProperty(oneOption["name"].toString().toLatin1(), oneOption["value"].toVariant()); 1272 1273 Options::self()->save(); 1274 emit optionsUpdated(); 1275 } 1276 else if (command == commands[OPTION_GET]) 1277 { 1278 const QJsonArray options = payload["options"].toArray(); 1279 QJsonArray result; 1280 for (const auto &oneOption : options) 1281 { 1282 const auto name = oneOption["name"].toString(); 1283 QVariant value = Options::self()->property(name.toLatin1()); 1284 QVariantMap map; 1285 map["name"] = name; 1286 map["value"] = value; 1287 result.append(QJsonObject::fromVariantMap(map)); 1288 } 1289 sendResponse(commands[OPTION_GET], result); 1290 } 1291 } 1292 1293 /////////////////////////////////////////////////////////////////////////////////////////// 1294 /// 1295 /////////////////////////////////////////////////////////////////////////////////////////// 1296 void Message::processScopeCommands(const QString &command, const QJsonObject &payload) 1297 { 1298 if (command == commands[ADD_SCOPE]) 1299 { 1300 KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(), 1301 payload["type"].toString(), payload["aperture"].toDouble(), payload["focal_length"].toDouble()); 1302 } 1303 else if (command == commands[UPDATE_SCOPE]) 1304 { 1305 KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(), 1306 payload["type"].toString(), payload["aperture"].toDouble(), payload["focal_length"].toDouble(), payload["id"].toString()); 1307 } 1308 else if (command == commands[DELETE_SCOPE]) 1309 { 1310 KStarsData::Instance()->userdb()->DeleteEquipment("telescope", payload["id"].toString()); 1311 } 1312 1313 sendScopes(); 1314 } 1315 1316 /////////////////////////////////////////////////////////////////////////////////////////// 1317 /// 1318 /////////////////////////////////////////////////////////////////////////////////////////// 1319 void Message::processDSLRCommands(const QString &command, const QJsonObject &payload) 1320 { 1321 if (command == commands[DSLR_SET_INFO]) 1322 { 1323 if (m_Manager->captureModule()) 1324 m_Manager->captureModule()->addDSLRInfo( 1325 payload["model"].toString(), 1326 payload["width"].toInt(), 1327 payload["height"].toInt(), 1328 payload["pixelw"].toDouble(), 1329 payload["pixelh"].toDouble()); 1330 1331 } 1332 else if(command == commands[DSLR_ADD_LENS]) 1333 { 1334 KStarsData::Instance()->userdb()->AddDSLRLens(payload["model"].toString(), payload["vendor"].toString(), 1335 payload["focal_length"].toDouble(), payload["focal_ratio"].toDouble()); 1336 } 1337 else if (command == commands[DSLR_DELETE_LENS]) 1338 { 1339 KStarsData::Instance()->userdb()->DeleteEquipment("dslrlens", payload["id"].toString()); 1340 } 1341 else if (command == commands[DSLR_UPDATE_LENS]) 1342 { 1343 KStarsData::Instance()->userdb()->AddDSLRLens(payload["model"].toString(), payload["vendor"].toString(), 1344 payload["focal_length"].toDouble(), payload["focal_ratio"].toDouble(), payload["id"].toString()); 1345 } 1346 1347 sendDSLRLenses(); 1348 } 1349 1350 /////////////////////////////////////////////////////////////////////////////////////////// 1351 /// 1352 /////////////////////////////////////////////////////////////////////////////////////////// 1353 void Message::processTrainCommands(const QString &command, const QJsonObject &payload) 1354 { 1355 if (command == commands[TRAIN_GET_ALL]) 1356 sendTrains(); 1357 else if (command == commands[TRAIN_GET_PROFILES]) 1358 sendTrainProfiles(); 1359 else if (command == commands[TRAIN_SET]) 1360 { 1361 auto module = payload["module"].toString(); 1362 auto name = payload["name"].toString(); 1363 1364 if (module == "capture") 1365 { 1366 if (m_Manager->captureModule()) 1367 m_Manager->captureModule()->setOpticalTrain(name); 1368 } 1369 else if (module == "focus") 1370 { 1371 if (m_Manager->focusModule()) 1372 m_Manager->focusModule()->setOpticalTrain(name); 1373 } 1374 else if (module == "guide") 1375 { 1376 if (m_Manager->guideModule()) 1377 m_Manager->guideModule()->setOpticalTrain(name); 1378 } 1379 else if (module == "align") 1380 { 1381 if (m_Manager->alignModule()) 1382 m_Manager->alignModule()->setOpticalTrain(name); 1383 } 1384 else if (module == "mount") 1385 { 1386 if (m_Manager->mountModule()) 1387 m_Manager->mountModule()->setOpticalTrain(name); 1388 } 1389 else if (module == "darklibrary") 1390 { 1391 Ekos::DarkLibrary::Instance()->setOpticalTrain(name); 1392 } 1393 } 1394 else if (command == commands[TRAIN_ADD]) 1395 { 1396 Ekos::OpticalTrainManager::Instance()->addOpticalTrain(payload); 1397 } 1398 else if (command == commands[TRAIN_UPDATE]) 1399 { 1400 Ekos::OpticalTrainManager::Instance()->setOpticalTrain(payload); 1401 } 1402 else if (command == commands[TRAIN_DELETE]) 1403 { 1404 Ekos::OpticalTrainManager::Instance()->removeOpticalTrain(payload["name"].toString()); 1405 } 1406 else if (command == commands[TRAIN_RESET]) 1407 { 1408 Ekos::OpticalTrainManager::Instance()->reset(); 1409 } 1410 else if (command == commands[TRAIN_ACCEPT]) 1411 { 1412 requestOpticalTrains(false); 1413 Ekos::OpticalTrainManager::Instance()->accept(); 1414 } 1415 1416 } 1417 1418 /////////////////////////////////////////////////////////////////////////////////////////// 1419 /// 1420 /////////////////////////////////////////////////////////////////////////////////////////// 1421 void Message::processFilterManagerCommands(const QString &command, const QJsonObject &payload) 1422 { 1423 QSharedPointer<Ekos::FilterManager> manager; 1424 if (m_Manager->captureModule()) 1425 manager = m_Manager->captureModule()->filterManager(); 1426 1427 if (manager.isNull()) 1428 return; 1429 1430 if (command == commands[FM_GET_DATA]) 1431 { 1432 QJsonObject data = manager->toJSON(); 1433 sendResponse(commands[FM_GET_DATA], data); 1434 } 1435 else if (command == commands[FM_SET_DATA]) 1436 { 1437 manager->setFilterData(payload); 1438 } 1439 } 1440 1441 /////////////////////////////////////////////////////////////////////////////////////////// 1442 /// 1443 /////////////////////////////////////////////////////////////////////////////////////////// 1444 void Message::processDarkLibraryCommands(const QString &command, const QJsonObject &payload) 1445 { 1446 if (command == commands[DARK_LIBRARY_START]) 1447 Ekos::DarkLibrary::Instance()->start(); 1448 else if(command == commands[DARK_LIBRARY_SET_ALL_SETTINGS]) 1449 { 1450 auto settings = payload.toVariantMap(); 1451 Ekos::DarkLibrary::Instance()->setAllSettings(settings); 1452 KSUtils::setGlobalSettings(settings); 1453 } 1454 else if(command == commands[DARK_LIBRARY_GET_ALL_SETTINGS]) 1455 sendDarkLibrarySettings(Ekos::DarkLibrary::Instance()->getAllSettings()); 1456 else if(command == commands[DARK_LIBRARY_GET_DEFECT_SETTINGS]) 1457 sendResponse(commands[DARK_LIBRARY_GET_DEFECT_SETTINGS], Ekos::DarkLibrary::Instance()->getDefectSettings()); 1458 else if(command == commands[DARK_LIBRARY_SET_CAMERA_PRESETS]) 1459 { 1460 Ekos::DarkLibrary::Instance()->setCameraPresets(payload); 1461 } 1462 else if (command == commands[DARK_LIBRARY_STOP]) 1463 { 1464 Ekos::DarkLibrary::Instance()->stop(); 1465 } 1466 else if (command == commands[DARK_LIBRARY_GET_MASTERS_IMAGE]) 1467 { 1468 const int row = payload["row"].toInt(); 1469 Ekos::DarkLibrary::Instance()->loadIndexInView(row); 1470 } 1471 else if (command == commands[DARK_LIBRARY_GET_CAMERA_PRESETS]) 1472 { 1473 sendResponse(commands[DARK_LIBRARY_GET_CAMERA_PRESETS], Ekos::DarkLibrary::Instance()->getCameraPresets()); 1474 } 1475 else if (command == commands[DARK_LIBRARY_SET_DEFECT_PIXELS]) 1476 { 1477 Ekos::DarkLibrary::Instance()->setDefectPixels(payload); 1478 } 1479 else if (command == commands[DARK_LIBRARY_SAVE_MAP]) 1480 { 1481 Ekos::DarkLibrary::Instance()->saveMapB->click(); 1482 } 1483 else if (command == commands[DARK_LIBRARY_SET_DEFECT_FRAME]) 1484 { 1485 Ekos::DarkLibrary::Instance()->setDefectMapEnabled(false); 1486 } 1487 else if (command == commands[DARK_LIBRARY_GET_VIEW_MASTERS]) 1488 { 1489 sendResponse(commands[DARK_LIBRARY_GET_VIEW_MASTERS], Ekos::DarkLibrary::Instance()->getViewMasters()); 1490 } 1491 else if (command == commands[DARK_LIBRARY_CLEAR_MASTERS_ROW]) 1492 { 1493 const int rowIndex = payload["row"].toInt(); 1494 Ekos::DarkLibrary::Instance()->clearRow(rowIndex); 1495 } 1496 } 1497 1498 /////////////////////////////////////////////////////////////////////////////////////////// 1499 /// 1500 /////////////////////////////////////////////////////////////////////////////////////////// 1501 void Message::processDeviceCommands(const QString &command, const QJsonObject &payload) 1502 { 1503 QString device = payload["device"].toString(); 1504 1505 // In case we want to UNSUBSCRIBE from all at once 1506 if (device.isEmpty() && command == commands[DEVICE_PROPERTY_UNSUBSCRIBE]) 1507 { 1508 m_PropertySubscriptions.clear(); 1509 return; 1510 } 1511 1512 QSharedPointer<ISD::GenericDevice> oneDevice; 1513 if (!INDIListener::findDevice(device, oneDevice)) 1514 return; 1515 1516 // Get specific property 1517 if (command == commands[DEVICE_PROPERTY_GET]) 1518 { 1519 QJsonObject propObject; 1520 if (oneDevice->getJSONProperty(payload["property"].toString(), propObject, payload["compact"].toBool(true))) 1521 sendResponse(commands[DEVICE_PROPERTY_GET], propObject); 1522 } 1523 // Set specific property 1524 else if (command == commands[DEVICE_PROPERTY_SET]) 1525 { 1526 oneDevice->setJSONProperty(payload["property"].toString(), payload["elements"].toArray()); 1527 } 1528 // Return ALL properties 1529 else if (command == commands[DEVICE_GET]) 1530 { 1531 QJsonArray properties; 1532 for (const auto &oneProp : *oneDevice->getProperties()) 1533 { 1534 QJsonObject singleProp; 1535 if (oneDevice->getJSONProperty(oneProp.getName(), singleProp, payload["compact"].toBool(false))) 1536 properties.append(singleProp); 1537 } 1538 1539 QJsonObject response = 1540 { 1541 {"device", device}, 1542 {"properties", properties} 1543 }; 1544 1545 sendResponse(commands[DEVICE_GET], response); 1546 } 1547 // Subscribe to one or more properties 1548 // When subscribed, the updates are immediately pushed as soon as they are received. 1549 else if (command == commands[DEVICE_PROPERTY_SUBSCRIBE]) 1550 { 1551 const QJsonArray properties = payload["properties"].toArray(); 1552 const QJsonArray groups = payload["groups"].toArray(); 1553 1554 // Get existing subscribed props for this device 1555 QSet<QString> props; 1556 if (m_PropertySubscriptions.contains(device)) 1557 props = m_PropertySubscriptions[device]; 1558 1559 // If it is just a single property, let's insert it to props. 1560 if (properties.isEmpty() == false) 1561 { 1562 for (const auto &oneProp : properties) 1563 props.insert(oneProp.toString()); 1564 } 1565 // If group is specified, then we need to add ALL properties belonging to this group. 1566 else if (groups.isEmpty() == false) 1567 { 1568 QVariantList indiGroups = groups.toVariantList(); 1569 for (auto &oneProp : *oneDevice->getProperties()) 1570 { 1571 if (indiGroups.contains(oneProp.getGroupName())) 1572 props.insert(oneProp.getName()); 1573 } 1574 } 1575 // Otherwise, subscribe to ALL property in this device 1576 else 1577 { 1578 for (auto &oneProp : *oneDevice->getProperties()) 1579 props.insert(oneProp.getName()); 1580 } 1581 1582 m_PropertySubscriptions[device] = props; 1583 } 1584 else if (command == commands[DEVICE_PROPERTY_UNSUBSCRIBE]) 1585 { 1586 const QJsonArray properties = payload["properties"].toArray(); 1587 const QJsonArray groups = payload["groups"].toArray(); 1588 1589 // Get existing subscribed props for this device 1590 QSet<QString> props; 1591 if (m_PropertySubscriptions.contains(device)) 1592 props = m_PropertySubscriptions[device]; 1593 1594 // If it is just a single property, let's insert it to props. 1595 // If it is just a single property, let's insert it to props. 1596 if (properties.isEmpty() == false) 1597 { 1598 for (const auto &oneProp : properties) 1599 props.remove(oneProp.toString()); 1600 } 1601 // If group is specified, then we need to add ALL properties belonging to this group. 1602 else if (groups.isEmpty() == false) 1603 { 1604 QVariantList indiGroups = groups.toVariantList(); 1605 for (auto &oneProp : *oneDevice->getProperties()) 1606 { 1607 if (indiGroups.contains(oneProp.getGroupName())) 1608 props.remove(oneProp.getName()); 1609 } 1610 } 1611 // Otherwise, subscribe to ALL property in this device 1612 else 1613 { 1614 for (auto &oneProp : *oneDevice->getProperties()) 1615 props.remove(oneProp.getName()); 1616 } 1617 1618 m_PropertySubscriptions[device] = props; 1619 } 1620 } 1621 1622 /////////////////////////////////////////////////////////////////////////////////////////// 1623 /// 1624 /////////////////////////////////////////////////////////////////////////////////////////// 1625 void Message::processAstronomyCommands(const QString &command, const QJsonObject &payload) 1626 { 1627 if (command == commands[ASTRO_GET_ALMANC]) 1628 { 1629 // Today's date 1630 const KStarsDateTime localTime = KStarsData::Instance()->lt(); 1631 // Local Midnight 1632 const KStarsDateTime midnight = KStarsDateTime(localTime.date(), QTime(0, 0), Qt::LocalTime); 1633 1634 KSAlmanac almanac(midnight, KStarsData::Instance()->geo()); 1635 1636 QJsonObject response = 1637 { 1638 {"SunRise", almanac.getSunRise()}, 1639 {"SunSet", almanac.getSunSet()}, 1640 {"SunMaxAlt", almanac.getSunMaxAlt()}, 1641 {"SunMinAlt", almanac.getSunMinAlt()}, 1642 {"MoonRise", almanac.getMoonRise()}, 1643 {"MoonSet", almanac.getMoonSet()}, 1644 {"MoonPhase", almanac.getMoonPhase()}, 1645 {"MoonIllum", almanac.getMoonIllum()}, 1646 {"Dawn", almanac.getDawnAstronomicalTwilight()}, 1647 {"Dusk", almanac.getDuskAstronomicalTwilight()}, 1648 1649 }; 1650 1651 sendResponse(commands[ASTRO_GET_ALMANC], response); 1652 } 1653 // Get a list of object based on criteria 1654 else if (command == commands[ASTRO_SEARCH_OBJECTS]) 1655 { 1656 // Set time if required. 1657 if (payload.contains("jd")) 1658 { 1659 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble()); 1660 KStarsData::Instance()->clock()->setUTC(jd); 1661 } 1662 1663 // Search Criteria 1664 // Object Type 1665 auto objectType = static_cast<SkyObject::TYPE>(payload["type"].toInt(SkyObject::GALAXY)); 1666 // Azimuth restriction 1667 auto objectDirection = static_cast<Direction>(payload["direction"].toInt(All)); 1668 // Maximum Object Magnitude 1669 auto objectMaxMagnitude = payload["maxMagnitude"].toDouble(10); 1670 // Minimum Object Altitude 1671 auto objectMinAlt = payload["minAlt"].toDouble(15); 1672 // Minimum Duration that the object must be above the altitude (if any) seconds. 1673 auto objectMinDuration = payload["minDuration"].toInt(3600); 1674 // Minimum FOV in arcmins. 1675 auto objectMinFOV = payload["minFOV"].toDouble(0); 1676 // Data instance 1677 auto *data = KStarsData::Instance(); 1678 // Geo Location 1679 auto *geo = KStarsData::Instance()->geo(); 1680 // If we are before dawn, we check object altitude restrictions 1681 // Otherwise, all objects are welcome 1682 auto start = KStarsData::Instance()->lt(); 1683 auto end = getNextDawn(); 1684 if (start > end) 1685 // Add 1 day 1686 end = end.addDays(1); 1687 1688 QVector<QPair<QString, const SkyObject *>> allObjects; 1689 CatalogsDB::CatalogObjectList dsoObjects; 1690 bool isDSO = false; 1691 1692 switch (objectType) 1693 { 1694 // Stars 1695 case SkyObject::STAR: 1696 case SkyObject::CATALOG_STAR: 1697 allObjects.append(data->skyComposite()->objectLists(SkyObject::STAR)); 1698 allObjects.append(data->skyComposite()->objectLists(SkyObject::CATALOG_STAR)); 1699 break; 1700 // Planets & Moon 1701 case SkyObject::PLANET: 1702 case SkyObject::MOON: 1703 allObjects.append(data->skyComposite()->objectLists(SkyObject::PLANET)); 1704 allObjects.append(data->skyComposite()->objectLists(SkyObject::MOON)); 1705 break; 1706 // Comets & Asteroids 1707 case SkyObject::COMET: 1708 allObjects.append(data->skyComposite()->objectLists(SkyObject::COMET)); 1709 break; 1710 case SkyObject::ASTEROID: 1711 allObjects.append(data->skyComposite()->objectLists(SkyObject::ASTEROID)); 1712 break; 1713 // Clusters 1714 case SkyObject::OPEN_CLUSTER: 1715 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::OPEN_CLUSTER, objectMaxMagnitude)); 1716 isDSO = true; 1717 break; 1718 case SkyObject::GLOBULAR_CLUSTER: 1719 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GLOBULAR_CLUSTER, objectMaxMagnitude)); 1720 isDSO = true; 1721 break; 1722 // Nebuale 1723 case SkyObject::GASEOUS_NEBULA: 1724 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GASEOUS_NEBULA, objectMaxMagnitude)); 1725 isDSO = true; 1726 break; 1727 case SkyObject::PLANETARY_NEBULA: 1728 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::PLANETARY_NEBULA, objectMaxMagnitude)); 1729 isDSO = true; 1730 break; 1731 case SkyObject::GALAXY: 1732 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GALAXY, objectMaxMagnitude)); 1733 isDSO = true; 1734 break; 1735 case SkyObject::SUPERNOVA: 1736 { 1737 if (!Options::showSupernovae()) 1738 { 1739 Options::setShowSupernovae(true); 1740 data->setFullTimeUpdate(); 1741 KStars::Instance()->map()->forceUpdate(); 1742 } 1743 allObjects.append(data->skyComposite()->objectLists(SkyObject::SUPERNOVA)); 1744 } 1745 break; 1746 case SkyObject::SATELLITE: 1747 { 1748 if (!Options::showSatellites()) 1749 { 1750 Options::setShowSatellites(true); 1751 data->setFullTimeUpdate(); 1752 KStars::Instance()->map()->forceUpdate(); 1753 } 1754 allObjects.append(data->skyComposite()->objectLists(SkyObject::SATELLITE)); 1755 } 1756 break; 1757 default: 1758 break; 1759 } 1760 1761 // Sort by magnitude 1762 std::sort(allObjects.begin(), allObjects.end(), [](const auto & a, const auto & b) 1763 { 1764 return a.second->mag() < b.second->mag(); 1765 }); 1766 1767 QMutableVectorIterator<QPair<QString, const SkyObject *>> objectIterator(allObjects); 1768 1769 // Filter direction, if specified. 1770 if (objectDirection != All) 1771 { 1772 QPair<int, int> Quardent1(270, 360), Quardent2(0, 90), Quardent3(90, 180), Quardent4(180, 270); 1773 QPair<int, int> minAZ, maxAZ; 1774 switch (objectDirection) 1775 { 1776 case North: 1777 minAZ = Quardent1; 1778 maxAZ = Quardent2; 1779 break; 1780 case East: 1781 minAZ = Quardent2; 1782 maxAZ = Quardent3; 1783 break; 1784 case South: 1785 minAZ = Quardent3; 1786 maxAZ = Quardent4; 1787 break; 1788 case West: 1789 minAZ = Quardent4; 1790 maxAZ = Quardent1; 1791 break; 1792 default: 1793 break; 1794 } 1795 1796 if (isDSO) 1797 { 1798 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin(); 1799 while (dsoIterator != dsoObjects.end()) 1800 { 1801 // If there a more efficient way to do this? 1802 const double az = (*dsoIterator).recomputeHorizontalCoords(start, geo).az().Degrees(); 1803 if (! ((minAZ.first <= az && az <= minAZ.second) || (maxAZ.first <= az && az <= maxAZ.second))) 1804 dsoIterator = dsoObjects.erase(dsoIterator); 1805 else 1806 ++dsoIterator; 1807 } 1808 } 1809 else 1810 { 1811 while (objectIterator.hasNext()) 1812 { 1813 const auto az = objectIterator.next().second->recomputeHorizontalCoords(start, geo).az().Degrees(); 1814 if (! ((minAZ.first <= az && az <= minAZ.second) || (maxAZ.first <= az && az <= maxAZ.second))) 1815 objectIterator.remove(); 1816 } 1817 } 1818 } 1819 1820 // Maximum Magnitude 1821 if (!isDSO) 1822 { 1823 objectIterator.toFront(); 1824 while (objectIterator.hasNext()) 1825 { 1826 auto magnitude = objectIterator.next().second->mag(); 1827 // Only filter for objects that have valid magnitude, otherwise, they're automatically included. 1828 if (magnitude != NaN::f && magnitude > objectMaxMagnitude) 1829 objectIterator.remove(); 1830 } 1831 } 1832 1833 // Altitude 1834 if (isDSO) 1835 { 1836 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin(); 1837 while (dsoIterator != dsoObjects.end()) 1838 { 1839 double duration = 0; 1840 for (KStarsDateTime t = start; t < end; t = t.addSecs(3600.0)) 1841 { 1842 dms LST = geo->GSTtoLST(t.gst()); 1843 (*dsoIterator).EquatorialToHorizontal(&LST, geo->lat()); 1844 if ((*dsoIterator).alt().Degrees() >= objectMinAlt) 1845 duration += 3600; 1846 } 1847 1848 if (duration < objectMinDuration) 1849 dsoIterator = dsoObjects.erase(dsoIterator); 1850 else 1851 ++dsoIterator; 1852 } 1853 } 1854 else 1855 { 1856 objectIterator.toFront(); 1857 while (objectIterator.hasNext()) 1858 { 1859 auto oneObject = objectIterator.next().second; 1860 double duration = 0; 1861 1862 for (KStarsDateTime t = start; t < end; t = t.addSecs(3600.0)) 1863 { 1864 auto LST = geo->GSTtoLST(t.gst()); 1865 const_cast<SkyObject *>(oneObject)->EquatorialToHorizontal(&LST, geo->lat()); 1866 if (oneObject->alt().Degrees() >= objectMinAlt) 1867 duration += 3600; 1868 } 1869 1870 if (duration < objectMinDuration) 1871 objectIterator.remove(); 1872 } 1873 } 1874 1875 // For DSOs, check minimum required FOV, if any. 1876 if (isDSO && objectMinFOV > 0) 1877 { 1878 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin(); 1879 while (dsoIterator != dsoObjects.end()) 1880 { 1881 if ((*dsoIterator).a() < objectMinFOV) 1882 dsoIterator = dsoObjects.erase(dsoIterator); 1883 else 1884 ++dsoIterator; 1885 } 1886 } 1887 1888 QStringList searchObjects; 1889 for (auto &oneObject : allObjects) 1890 searchObjects.append(oneObject.second->name()); 1891 for (auto &oneObject : dsoObjects) 1892 searchObjects.append(oneObject.name()); 1893 1894 searchObjects.removeDuplicates(); 1895 QJsonArray response = QJsonArray::fromStringList(searchObjects); 1896 1897 sendResponse(commands[ASTRO_SEARCH_OBJECTS], response); 1898 } 1899 else if(command == commands[ASTRO_GET_OBJECT_INFO]) 1900 { 1901 const auto name = payload["object"].toString(); 1902 QJsonObject info; 1903 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, false); 1904 if(oneObject) 1905 { 1906 info = 1907 { 1908 {"name", name}, 1909 {"designations", QJsonArray::fromStringList(oneObject->longname().split(", "))}, 1910 {"magnitude", oneObject->mag()}, 1911 {"ra0", oneObject->ra0().Hours()}, 1912 {"de0", oneObject->dec0().Degrees()}, 1913 {"ra", oneObject->ra().Hours()}, 1914 {"de", oneObject->dec().Degrees()}, 1915 {"object", true} 1916 }; 1917 sendResponse(commands[ASTRO_GET_OBJECT_INFO], info); 1918 } 1919 else 1920 { 1921 info = 1922 { 1923 {"name", name}, 1924 {"object", false}, 1925 }; 1926 sendResponse(commands[ASTRO_GET_OBJECT_INFO], info ); 1927 } 1928 1929 } 1930 // Get a list of object based on criteria 1931 else if (command == commands[ASTRO_GET_OBJECTS_INFO]) 1932 { 1933 // Set time if required. 1934 if (payload.contains("jd")) 1935 { 1936 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble()); 1937 KStarsData::Instance()->clock()->setUTC(jd); 1938 } 1939 1940 // Object Names 1941 QVariantList objectNames = payload["names"].toArray().toVariantList(); 1942 QJsonArray objectsArray; 1943 1944 for (auto &oneName : objectNames) 1945 { 1946 const QString name = oneName.toString(); 1947 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, false); 1948 if (oneObject) 1949 { 1950 QJsonObject info = 1951 { 1952 {"name", name}, 1953 {"designations", QJsonArray::fromStringList(oneObject->longname().split(", "))}, 1954 {"magnitude", oneObject->mag()}, 1955 {"ra0", oneObject->ra0().Hours()}, 1956 {"de0", oneObject->dec0().Degrees()}, 1957 {"ra", oneObject->ra().Hours()}, 1958 {"de", oneObject->dec().Degrees()}, 1959 }; 1960 1961 // If DSO, add angular size. 1962 CatalogObject *dsoObject = dynamic_cast<CatalogObject*>(oneObject); 1963 if (dsoObject) 1964 { 1965 info["a"] = dsoObject->a(); 1966 info["b"] = dsoObject->b(); 1967 info["pa"] = dsoObject->pa(); 1968 } 1969 1970 objectsArray.append(info); 1971 } 1972 } 1973 1974 sendResponse(commands[ASTRO_GET_OBJECTS_INFO], objectsArray); 1975 } 1976 // Get a object observability alt/az/ha 1977 else if (command == commands[ASTRO_GET_OBJECTS_OBSERVABILITY]) 1978 { 1979 // Set time if required. 1980 if (payload.contains("jd")) 1981 { 1982 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble()); 1983 KStarsData::Instance()->clock()->setUTC(jd); 1984 } 1985 1986 // Object Names 1987 QVariantList objectNames = payload["names"].toArray().toVariantList(); 1988 QJsonArray objectsArray; 1989 1990 // Data instance 1991 auto *data = KStarsData::Instance(); 1992 // Geo Location 1993 auto *geo = KStarsData::Instance()->geo(); 1994 // UT 1995 auto ut = data->ut(); 1996 1997 for (auto &oneName : objectNames) 1998 { 1999 const QString name = oneName.toString(); 2000 SkyObject *oneObject = data->skyComposite()->findByName(name, false); 2001 if (oneObject) 2002 { 2003 oneObject->EquatorialToHorizontal(data->lst(), geo->lat()); 2004 dms ha(data->lst()->Degrees() - oneObject->ra().Degrees()); 2005 QJsonObject info = 2006 { 2007 {"name", name}, 2008 {"az", oneObject->az().Degrees()}, 2009 {"alt", oneObject->alt().Degrees()}, 2010 {"ha", ha.Hours()}, 2011 }; 2012 2013 objectsArray.append(info); 2014 } 2015 } 2016 2017 sendResponse(commands[ASTRO_GET_OBJECTS_OBSERVABILITY], objectsArray); 2018 } 2019 else if (command == commands[ASTRO_GET_OBJECTS_RISESET]) 2020 { 2021 // Set time if required. 2022 if (payload.contains("jd")) 2023 { 2024 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble()); 2025 KStarsData::Instance()->clock()->setUTC(jd); 2026 } 2027 2028 // Object Names 2029 QVariantList objectNames = payload["names"].toArray().toVariantList(); 2030 QJsonArray objectsArray; 2031 2032 // Data instance 2033 auto *data = KStarsData::Instance(); 2034 // Geo Location 2035 auto *geo = KStarsData::Instance()->geo(); 2036 // UT 2037 QDateTime midnight = QDateTime(data->lt().date(), QTime()); 2038 KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight)); 2039 2040 int DayOffset = 0; 2041 if (data->lt().time().hour() > 12) 2042 DayOffset = 1; 2043 2044 for (auto &oneName : objectNames) 2045 { 2046 const QString name = oneName.toString(); 2047 SkyObject *oneObject = data->skyComposite()->findByName(name, false); 2048 if (oneObject) 2049 { 2050 QJsonObject info; 2051 //Prepare time/position variables 2052 //true = use rise time 2053 QTime riseTime = oneObject->riseSetTime(ut, geo, true); 2054 2055 //If transit time is before rise time, use transit time for tomorrow 2056 QTime transitTime = oneObject->transitTime(ut, geo); 2057 if (transitTime < riseTime) 2058 transitTime = oneObject->transitTime(ut.addDays(1), geo); 2059 2060 //If set time is before rise time, use set time for tomorrow 2061 //false = use set time 2062 QTime setTime = oneObject->riseSetTime(ut, geo, false); 2063 //false = use set time 2064 if (setTime < riseTime) 2065 setTime = oneObject->riseSetTime(ut.addDays(1), geo, false); 2066 2067 info["name"] = name; 2068 if (riseTime.isValid()) 2069 { 2070 info["rise"] = QString::asprintf("%02d:%02d", riseTime.hour(), riseTime.minute()); 2071 info["set"] = QString::asprintf("%02d:%02d", setTime.hour(), setTime.minute()); 2072 } 2073 else 2074 { 2075 if (oneObject->alt().Degrees() > 0.0) 2076 { 2077 info["rise"] = "Circumpolar"; 2078 info["set"] = "Circumpolar"; 2079 } 2080 else 2081 { 2082 info["rise"] = "Never rises"; 2083 info["set"] = "Never rises"; 2084 } 2085 } 2086 2087 info["transit"] = QString::asprintf("%02d:%02d", transitTime.hour(), transitTime.minute()); 2088 2089 QJsonArray altitudes; 2090 for (double h = -12.0; h <= 12.0; h += 0.5) 2091 { 2092 double hour = h + (24.0 * DayOffset); 2093 KStarsDateTime offset = ut.addSecs(hour * 3600.0); 2094 CachingDms LST = geo->GSTtoLST(offset.gst()); 2095 oneObject->EquatorialToHorizontal(&LST, geo->lat()); 2096 altitudes.append(oneObject->alt().Degrees()); 2097 } 2098 2099 info["altitudes"] = altitudes; 2100 2101 objectsArray.append(info); 2102 } 2103 } 2104 2105 sendResponse(commands[ASTRO_GET_OBJECTS_RISESET], objectsArray); 2106 } 2107 } 2108 2109 /////////////////////////////////////////////////////////////////////////////////////////// 2110 /// 2111 /////////////////////////////////////////////////////////////////////////////////////////// 2112 KStarsDateTime Message::getNextDawn() 2113 { 2114 // Today's date 2115 const KStarsDateTime localTime = KStarsData::Instance()->lt(); 2116 // Local Midnight 2117 const KStarsDateTime midnight = KStarsDateTime(localTime.date(), QTime(0, 0), Qt::LocalTime); 2118 // Almanac 2119 KSAlmanac almanac(midnight, KStarsData::Instance()->geo()); 2120 // Next Dawn 2121 KStarsDateTime nextDawn = midnight.addSecs(almanac.getDawnAstronomicalTwilight() * 24.0 * 3600.0); 2122 // If dawn is earliar than now, add a day 2123 if (nextDawn < localTime) 2124 nextDawn.addDays(1); 2125 2126 return nextDawn; 2127 } 2128 2129 /////////////////////////////////////////////////////////////////////////////////////////// 2130 /// 2131 /////////////////////////////////////////////////////////////////////////////////////////// 2132 void Message::requestDSLRInfo(const QString &cameraName) 2133 { 2134 sendResponse(commands[DSLR_GET_INFO], cameraName); 2135 } 2136 2137 /////////////////////////////////////////////////////////////////////////////////////////// 2138 /// 2139 /////////////////////////////////////////////////////////////////////////////////////////// 2140 void Message::requestPortSelection(bool show) 2141 { 2142 sendResponse(commands[GET_PROFILE_PORT_SELECTION], show); 2143 } 2144 2145 /////////////////////////////////////////////////////////////////////////////////////////// 2146 /// 2147 /////////////////////////////////////////////////////////////////////////////////////////// 2148 void Message::sendDialog(const QJsonObject &message) 2149 { 2150 sendResponse(commands[DIALOG_GET_INFO], message); 2151 } 2152 2153 /////////////////////////////////////////////////////////////////////////////////////////// 2154 /// 2155 /////////////////////////////////////////////////////////////////////////////////////////// 2156 void Message::sendResponse(const QString &command, const QJsonObject &payload) 2157 { 2158 for (auto &nodeManager : m_NodeManagers) 2159 { 2160 nodeManager->message()->sendResponse(command, payload); 2161 } 2162 } 2163 2164 /////////////////////////////////////////////////////////////////////////////////////////// 2165 /// 2166 /////////////////////////////////////////////////////////////////////////////////////////// 2167 void Message::sendResponse(const QString &command, const QJsonArray &payload) 2168 { 2169 for (auto &nodeManager : m_NodeManagers) 2170 { 2171 nodeManager->message()->sendResponse(command, payload); 2172 } 2173 } 2174 2175 /////////////////////////////////////////////////////////////////////////////////////////// 2176 /// 2177 /////////////////////////////////////////////////////////////////////////////////////////// 2178 void Message::sendResponse(const QString &command, const QString &payload) 2179 { 2180 for (auto &nodeManager : m_NodeManagers) 2181 { 2182 nodeManager->message()->sendResponse(command, payload); 2183 } 2184 } 2185 2186 /////////////////////////////////////////////////////////////////////////////////////////// 2187 /// 2188 /////////////////////////////////////////////////////////////////////////////////////////// 2189 void Message::sendResponse(const QString &command, bool payload) 2190 { 2191 for (auto &nodeManager : m_NodeManagers) 2192 { 2193 nodeManager->message()->sendResponse(command, payload); 2194 } 2195 } 2196 2197 /////////////////////////////////////////////////////////////////////////////////////////// 2198 /// 2199 /////////////////////////////////////////////////////////////////////////////////////////// 2200 void Message::autofocusAborted() 2201 { 2202 QJsonObject cStatus = 2203 { 2204 {"status", "Aborted"} 2205 }; 2206 sendResponse(commands[NEW_FOCUS_STATE], cStatus); 2207 } 2208 2209 /////////////////////////////////////////////////////////////////////////////////////////// 2210 /// 2211 /////////////////////////////////////////////////////////////////////////////////////////// 2212 void Message::updateMountStatus(const QJsonObject &status, bool throttle) 2213 { 2214 if (throttle) 2215 { 2216 QDateTime now = QDateTime::currentDateTime(); 2217 if (m_ThrottleTS.msecsTo(now) >= THROTTLE_INTERVAL) 2218 { 2219 m_ThrottleTS = now; 2220 sendResponse(commands[NEW_MOUNT_STATE], status); 2221 } 2222 } 2223 else 2224 sendResponse(commands[NEW_MOUNT_STATE], status); 2225 } 2226 2227 /////////////////////////////////////////////////////////////////////////////////////////// 2228 /// 2229 /////////////////////////////////////////////////////////////////////////////////////////// 2230 void Message::updateCaptureStatus(const QJsonObject &status) 2231 { 2232 sendResponse(commands[NEW_CAPTURE_STATE], status); 2233 } 2234 2235 /////////////////////////////////////////////////////////////////////////////////////////// 2236 /// 2237 /////////////////////////////////////////////////////////////////////////////////////////// 2238 void Message::updateFocusStatus(const QJsonObject &status) 2239 { 2240 sendResponse(commands[NEW_FOCUS_STATE], status); 2241 } 2242 2243 /////////////////////////////////////////////////////////////////////////////////////////// 2244 /// 2245 /////////////////////////////////////////////////////////////////////////////////////////// 2246 void Message::updateGuideStatus(const QJsonObject &status) 2247 { 2248 sendResponse(commands[NEW_GUIDE_STATE], status); 2249 } 2250 2251 /////////////////////////////////////////////////////////////////////////////////////////// 2252 /// 2253 /////////////////////////////////////////////////////////////////////////////////////////// 2254 void Message::updateDomeStatus(const QJsonObject &status) 2255 { 2256 sendResponse(commands[NEW_DOME_STATE], status); 2257 } 2258 2259 /////////////////////////////////////////////////////////////////////////////////////////// 2260 /// 2261 /////////////////////////////////////////////////////////////////////////////////////////// 2262 void Message::updateCapStatus(const QJsonObject &status) 2263 { 2264 sendResponse(commands[NEW_CAP_STATE], status); 2265 } 2266 2267 /////////////////////////////////////////////////////////////////////////////////////////// 2268 /// 2269 /////////////////////////////////////////////////////////////////////////////////////////// 2270 void Message::sendConnection() 2271 { 2272 QJsonObject connectionState = 2273 { 2274 {"connected", true}, 2275 {"online", m_Manager->getEkosStartingStatus() == Ekos::Success} 2276 }; 2277 2278 sendResponse(commands[NEW_CONNECTION_STATE], connectionState); 2279 } 2280 2281 /////////////////////////////////////////////////////////////////////////////////////////// 2282 /// 2283 /////////////////////////////////////////////////////////////////////////////////////////// 2284 void Message::sendStates() 2285 { 2286 // Send capture sequence if one exists 2287 if (m_Manager->captureModule()) 2288 { 2289 QJsonObject captureState = {{ "status", getCaptureStatusString(m_Manager->captureModule()->status(), false)}}; 2290 sendResponse(commands[NEW_CAPTURE_STATE], captureState); 2291 sendCaptureSequence(m_Manager->captureModule()->getSequence()); 2292 } 2293 2294 if (m_Manager->mountModule()) 2295 { 2296 QJsonObject mountState = 2297 { 2298 {"status", m_Manager->mountModule()->statusString(false)}, 2299 {"target", m_Manager->capturePreview->mountTarget->text()}, 2300 {"slewRate", m_Manager->mountModule()->slewRate()}, 2301 {"pierSide", m_Manager->mountModule()->pierSide()} 2302 }; 2303 2304 sendResponse(commands[NEW_MOUNT_STATE], mountState); 2305 } 2306 2307 if (m_Manager->focusModule()) 2308 { 2309 QJsonObject focusState = {{ "status", getFocusStatusString(m_Manager->focusModule()->status(), false)}}; 2310 sendResponse(commands[NEW_FOCUS_STATE], focusState); 2311 } 2312 2313 if (m_Manager->guideModule()) 2314 { 2315 QJsonObject guideState = {{ "status", getGuideStatusString(m_Manager->guideModule()->status(), false)}}; 2316 sendResponse(commands[NEW_GUIDE_STATE], guideState); 2317 } 2318 2319 if (m_Manager->alignModule()) 2320 { 2321 // Align State 2322 QJsonObject alignState = 2323 { 2324 {"status", getAlignStatusString(m_Manager->alignModule()->status(), false)} 2325 }; 2326 sendResponse(commands[NEW_ALIGN_STATE], alignState); 2327 2328 // Align settings 2329 sendAlignSettings(m_Manager->alignModule()->getAllSettings()); 2330 2331 Ekos::PolarAlignmentAssistant *paa = m_Manager->alignModule()->polarAlignmentAssistant(); 2332 if (paa) 2333 { 2334 // Polar State 2335 QTextDocument doc; 2336 doc.setHtml(paa->getPAHMessage()); 2337 QJsonObject polarState = 2338 { 2339 {"stage", paa->getPAHStageString(false)}, 2340 {"enabled", paa->isEnabled()}, 2341 {"message", doc.toPlainText()}, 2342 }; 2343 sendResponse(commands[NEW_POLAR_STATE], polarState); 2344 } 2345 } 2346 } 2347 2348 /////////////////////////////////////////////////////////////////////////////////////////// 2349 /// 2350 /////////////////////////////////////////////////////////////////////////////////////////// 2351 void Message::sendEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event) 2352 { 2353 if (Options::ekosLiveNotifications() == false) 2354 return; 2355 2356 QJsonObject newEvent = 2357 { 2358 {"source", source}, 2359 {"severity", event}, 2360 {"message", message}, 2361 {"uuid", QUuid::createUuid().toString()} 2362 }; 2363 2364 sendResponse(commands[NEW_NOTIFICATION], newEvent); 2365 } 2366 2367 /////////////////////////////////////////////////////////////////////////////////////////// 2368 /// 2369 /////////////////////////////////////////////////////////////////////////////////////////// 2370 void Message::sendManualRotatorStatus(double currentPA, double targetPA, double threshold) 2371 { 2372 QJsonObject request = {{ "currentPA", currentPA}, {"targetPA", targetPA}, {"threshold", threshold}}; 2373 sendResponse(commands[ALIGN_MANUAL_ROTATOR_STATUS], request); 2374 } 2375 2376 /////////////////////////////////////////////////////////////////////////////////////////// 2377 /// 2378 /////////////////////////////////////////////////////////////////////////////////////////// 2379 void Message::setBoundingRect(QRect rect, QSize view, double currentZoom) 2380 { 2381 m_BoundingRect = rect; 2382 m_ViewSize = view; 2383 m_CurrentZoom = currentZoom; 2384 } 2385 2386 /////////////////////////////////////////////////////////////////////////////////////////// 2387 /// 2388 /////////////////////////////////////////////////////////////////////////////////////////// 2389 void Message::processDialogResponse(const QJsonObject &payload) 2390 { 2391 KSMessageBox::Instance()->selectResponse(payload["button"].toString()); 2392 } 2393 2394 /////////////////////////////////////////////////////////////////////////////////////////// 2395 /// 2396 /////////////////////////////////////////////////////////////////////////////////////////// 2397 void Message::processNewProperty(INDI::Property prop) 2398 { 2399 // Do not send new properties until all properties settle down 2400 // then send any properties that appears afterwards since the initial bunch 2401 // would cause a heavy message congestion. 2402 if (m_Manager->settleStatus() != Ekos::CommunicationStatus::Success) 2403 return; 2404 2405 QJsonObject propObject; 2406 ISD::propertyToJson(prop, propObject, false); 2407 sendResponse(commands[DEVICE_PROPERTY_ADD], propObject); 2408 } 2409 2410 /////////////////////////////////////////////////////////////////////////////////////////// 2411 /// 2412 /////////////////////////////////////////////////////////////////////////////////////////// 2413 void Message::processDeleteProperty(INDI::Property prop) 2414 { 2415 QJsonObject payload = 2416 { 2417 {"device", prop.getDeviceName()}, 2418 {"name", prop.getName()} 2419 }; 2420 2421 sendResponse(commands[DEVICE_PROPERTY_REMOVE], payload); 2422 } 2423 2424 /////////////////////////////////////////////////////////////////////////////////////////// 2425 /// 2426 /////////////////////////////////////////////////////////////////////////////////////////// 2427 void Message::processMessage(const QSharedPointer<ISD::GenericDevice> &device, int id) 2428 { 2429 if (Options::ekosLiveNotifications() == false) 2430 return; 2431 2432 auto message = QString::fromStdString(device->getBaseDevice().messageQueue(id)); 2433 QJsonObject payload = 2434 { 2435 {"device", device->getDeviceName()}, 2436 {"message", message} 2437 }; 2438 2439 sendResponse(commands[DEVICE_MESSAGE], payload); 2440 } 2441 2442 /////////////////////////////////////////////////////////////////////////////////////////// 2443 /// 2444 /////////////////////////////////////////////////////////////////////////////////////////// 2445 void Message::processUpdateProperty(INDI::Property prop) 2446 { 2447 if (m_PropertySubscriptions.contains(prop.getDeviceName())) 2448 { 2449 QSet<QString> subProps = m_PropertySubscriptions[prop.getDeviceName()]; 2450 if (subProps.contains(prop.getName())) 2451 { 2452 m_PendingProperties.remove(prop); 2453 m_PendingProperties.insert(prop); 2454 } 2455 } 2456 } 2457 2458 /////////////////////////////////////////////////////////////////////////////////////////// 2459 /// 2460 /////////////////////////////////////////////////////////////////////////////////////////// 2461 void Message::clearPendingProperties() 2462 { 2463 m_PendingProperties.clear(); 2464 } 2465 2466 /////////////////////////////////////////////////////////////////////////////////////////// 2467 /// 2468 /////////////////////////////////////////////////////////////////////////////////////////// 2469 void Message::sendPendingProperties() 2470 { 2471 for (auto &prop : m_PendingProperties) 2472 { 2473 if (prop->isValid()) 2474 { 2475 QJsonObject propObject; 2476 ISD::propertyToJson(*prop, propObject); 2477 sendResponse(commands[DEVICE_PROPERTY_GET], propObject); 2478 } 2479 } 2480 2481 m_PendingProperties.clear(); 2482 } 2483 2484 /////////////////////////////////////////////////////////////////////////////////////////// 2485 /// 2486 /////////////////////////////////////////////////////////////////////////////////////////// 2487 void Message::sendModuleState(const QString &name) 2488 { 2489 if (name == "Capture") 2490 { 2491 QJsonObject captureState = {{ "status", getCaptureStatusString(m_Manager->captureModule()->status(), false)}}; 2492 sendResponse(commands[NEW_CAPTURE_STATE], captureState); 2493 sendCaptureSequence(m_Manager->captureModule()->getSequence()); 2494 } 2495 else if (name == "Mount") 2496 { 2497 QJsonObject mountState = 2498 { 2499 {"status", m_Manager->mountStatus->getStatusText()}, 2500 {"target", m_Manager->capturePreview->mountTarget->text()}, 2501 {"slewRate", m_Manager->mountModule()->slewRate()}, 2502 {"pierSide", m_Manager->mountModule()->pierSide()} 2503 }; 2504 2505 sendResponse(commands[NEW_MOUNT_STATE], mountState); 2506 } 2507 else if (name == "Focus") 2508 { 2509 QJsonObject focusState = {{ "status", getFocusStatusString(m_Manager->focusModule()->status(), false)}}; 2510 sendResponse(commands[NEW_FOCUS_STATE], focusState); 2511 } 2512 else if (name == "Guide") 2513 { 2514 QJsonObject guideState = {{ "status", getGuideStatusString(m_Manager->guideModule()->status(), false)}}; 2515 sendResponse(commands[NEW_GUIDE_STATE], guideState); 2516 } 2517 else if (name == "Align") 2518 { 2519 // Align State 2520 QJsonObject alignState = 2521 { 2522 {"status", getAlignStatusString(m_Manager->alignModule()->status(), false)} 2523 }; 2524 sendResponse(commands[NEW_ALIGN_STATE], alignState); 2525 2526 // Align settings 2527 sendAlignSettings(m_Manager->alignModule()->getAllSettings()); 2528 2529 Ekos::PolarAlignmentAssistant *paa = m_Manager->alignModule()->polarAlignmentAssistant(); 2530 if (paa) 2531 { 2532 // Polar State 2533 QTextDocument doc; 2534 doc.setHtml(paa->getPAHMessage()); 2535 QJsonObject polarState = 2536 { 2537 {"stage", paa->getPAHStageString(false)}, 2538 {"enabled", paa->isEnabled()}, 2539 {"message", doc.toPlainText()}, 2540 }; 2541 sendResponse(commands[NEW_POLAR_STATE], polarState); 2542 } 2543 } 2544 } 2545 2546 /////////////////////////////////////////////////////////////////////////////////////////// 2547 /// 2548 /////////////////////////////////////////////////////////////////////////////////////////// 2549 QObject *Message::findObject(const QString &name) 2550 { 2551 QObject *object {nullptr}; 2552 // Check for manager itself 2553 if (name == "Manager") 2554 return m_Manager; 2555 // Try Manager first 2556 object = m_Manager->findChild<QObject *>(name); 2557 if (object) 2558 return object; 2559 // Then INDI Listener 2560 object = INDIListener::Instance()->findChild<QObject *>(name); 2561 if (object) 2562 return object; 2563 // Finally KStars 2564 // N.B. This does not include indepdent objects with their parent set to null (e.g. FITSViewer) 2565 object = KStars::Instance()->findChild<QObject *>(name); 2566 return object; 2567 } 2568 2569 /////////////////////////////////////////////////////////////////////////////////////////// 2570 /// 2571 /////////////////////////////////////////////////////////////////////////////////////////// 2572 bool Message::parseArgument(QVariant::Type type, const QVariant &arg, QGenericArgument &genericArg, SimpleTypes &types) 2573 { 2574 QGenericArgument genericArgument; 2575 2576 switch (type) 2577 { 2578 case QVariant::Type::Int: 2579 types.number_integer = arg.toInt(); 2580 genericArg = Q_ARG(int, types.number_integer); 2581 return true; 2582 case QVariant::Type::UInt: 2583 types.number_unsigned_integer = arg.toUInt(); 2584 genericArg = Q_ARG(uint, types.number_unsigned_integer); 2585 return true; 2586 case QVariant::Type::LongLong: 2587 types.number_integer = arg.toLongLong(); 2588 genericArg = Q_ARG(int, types.number_integer); 2589 return true; 2590 case QVariant::Type::ULongLong: 2591 types.number_unsigned_integer = arg.toULongLong(); 2592 genericArg = Q_ARG(uint, types.number_unsigned_integer); 2593 return true; 2594 case QVariant::Type::Double: 2595 types.number_double = arg.toDouble(); 2596 genericArg = Q_ARG(double, types.number_double); 2597 return true; 2598 case QVariant::Type::Bool: 2599 types.boolean = arg.toBool(); 2600 genericArg = Q_ARG(bool, types.boolean); 2601 return true; 2602 case QVariant::Type::String: 2603 types.text = arg.toString(); 2604 genericArg = Q_ARG(QString, types.text); 2605 return true; 2606 case QVariant::Type::Url: 2607 types.url = arg.toUrl(); 2608 genericArg = Q_ARG(QUrl, types.url); 2609 return true; 2610 default: 2611 break; 2612 } 2613 2614 return false; 2615 } 2616 2617 /////////////////////////////////////////////////////////////////////////////////////////// 2618 /// 2619 /////////////////////////////////////////////////////////////////////////////////////////// 2620 void Message::invokeMethod(QObject *context, const QJsonObject &payload) 2621 { 2622 QList<QGenericArgument> argsList; 2623 QList<SimpleTypes> typesList; 2624 2625 auto name = payload["name"].toString().toLatin1(); 2626 2627 if (payload.contains("args")) 2628 { 2629 QJsonArray args = payload["args"].toArray(); 2630 2631 for (auto oneArg : args) 2632 { 2633 auto argObject = oneArg.toObject(); 2634 QGenericArgument genericArgument; 2635 SimpleTypes genericType; 2636 argsList.append(genericArgument); 2637 typesList.append(genericType); 2638 if (parseArgument(static_cast<QVariant::Type>(argObject["type"].toInt()), argObject["value"].toVariant(), argsList.back(), 2639 typesList.last()) == false) 2640 { 2641 argsList.pop_back(); 2642 typesList.pop_back(); 2643 } 2644 } 2645 2646 switch (argsList.size()) 2647 { 2648 case 1: 2649 QMetaObject::invokeMethod(context, name, argsList[0]); 2650 break; 2651 case 2: 2652 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1]); 2653 break; 2654 case 3: 2655 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1], argsList[2]); 2656 break; 2657 case 4: 2658 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1], argsList[2], argsList[3]); 2659 break; 2660 default: 2661 break; 2662 } 2663 } 2664 else 2665 { 2666 QMetaObject::invokeMethod(context, name); 2667 } 2668 } 2669 2670 }