Kstars

message.cpp
1/* Ekos Live Message
2
3 SPDX-FileCopyrightText: 2018 Jasem Mutlaq <mutlaqja@ikarustech.com>
4
5 Message Channel
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "message.h"
11#include "commands.h"
12#include "profileinfo.h"
13#include "indi/drivermanager.h"
14#include "indi/indilistener.h"
15#include "auxiliary/ksmessagebox.h"
16#include "ekos/auxiliary/filtermanager.h"
17#include "ekos/auxiliary/opticaltrainmanager.h"
18#include "ekos/auxiliary/profilesettings.h"
19#include "ekos/capture/capture.h"
20#include "ekos/guide/guide.h"
21#include "ekos/mount/mount.h"
22#include "ekos/scheduler/scheduler.h"
23#include "ekos/scheduler/schedulermodulestate.h"
24#include "kstars.h"
25#include "kstarsdata.h"
26#include "ekos_debug.h"
27#include "ksalmanac.h"
28#include "skymapcomposite.h"
29#include "catalogobject.h"
30#include "fitsviewer/fitsviewer.h"
31#include "fitsviewer/fitstab.h"
32#include "ekos/auxiliary/darklibrary.h"
33#include "skymap.h"
34#include "Options.h"
35#include "version.h"
36
37#include <KActionCollection>
38#include <basedevice.h>
39#include <QUuid>
40
41namespace EkosLive
42{
43Message::Message(Ekos::Manager *manager, QVector<QSharedPointer<NodeManager>> &nodeManagers):
44 m_Manager(manager), m_NodeManagers(nodeManagers), m_DSOManager(CatalogsDB::dso_db_path())
45{
46 for (auto &nodeManager : m_NodeManagers)
47 {
48 connect(nodeManager->message(), &Node::connected, this, &Message::onConnected);
49 connect(nodeManager->message(), &Node::disconnected, this, &Message::onDisconnected);
50 connect(nodeManager->message(), &Node::onTextReceived, this, &Message::onTextReceived);
51 }
52
53 connect(manager, &Ekos::Manager::newModule, this, &Message::sendModuleState);
54
55 m_ThrottleTS = QDateTime::currentDateTime();
56
57 m_PendingPropertiesTimer.setInterval(500);
58 connect(&m_PendingPropertiesTimer, &QTimer::timeout, this, &Message::sendPendingProperties);
59
60 m_DebouncedSend.setInterval(500);
61 connect(&m_DebouncedSend, &QTimer::timeout, this, &Message::dispatchDebounceQueue);
62}
63
64///////////////////////////////////////////////////////////////////////////////////////////
65///
66///////////////////////////////////////////////////////////////////////////////////////////
67void Message::onConnected()
68{
69 auto node = qobject_cast<Node*>(sender());
70 if (!node)
71 return;
72
73 qCInfo(KSTARS_EKOS) << "Connected to Message Websocket server at" << node->url().toDisplayString();
74
75 m_PendingPropertiesTimer.start();
76 sendConnection();
77 sendProfiles();
78 emit connected();
79}
80
81///////////////////////////////////////////////////////////////////////////////////////////
82///
83///////////////////////////////////////////////////////////////////////////////////////////
84void Message::onDisconnected()
85{
86 auto node = qobject_cast<Node*>(sender());
87 if (!node)
88 return;
89
90 qCInfo(KSTARS_EKOS) << "Disconnected from Message Websocket server at" << node->url().toDisplayString();
91
92 if (isConnected() == false)
93 {
94 m_PendingPropertiesTimer.stop();
95 emit disconnected();
96 }
97}
98
99///////////////////////////////////////////////////////////////////////////////////////////
100///
101///////////////////////////////////////////////////////////////////////////////////////////
102void Message::onTextReceived(const QString &message)
103{
104 auto node = qobject_cast<Node*>(sender());
105 if (!node || message.isEmpty())
106 return;
107
108 qCInfo(KSTARS_EKOS) << "Websocket Message" << message;
110 auto serverMessage = QJsonDocument::fromJson(message.toUtf8(), &error);
111 if (error.error != QJsonParseError::NoError)
112 {
113 qCWarning(KSTARS_EKOS) << "Ekos Live Parsing Error" << error.errorString();
114 return;
115 }
116
117 const QJsonObject msgObj = serverMessage.object();
118 const QString command = msgObj["type"].toString();
119 const QJsonObject payload = msgObj["payload"].toObject();
120
121 if (command == commands[GET_CONNECTION])
122 {
123 sendConnection();
124 }
125 else if (command == commands[LOGOUT])
126 {
127 emit expired(node->url());
128 return;
129 }
130 else if (command == commands[SET_CLIENT_STATE])
131 {
132 // If client is connected, make sure clock is ticking
133 if (payload["state"].toBool(false))
134 {
135 qCInfo(KSTARS_EKOS) << "EkosLive client is connected.";
136
137 // If the clock is PAUSED, run it now and sync time as well.
138 if (KStarsData::Instance()->clock()->isActive() == false)
139 {
140 qCInfo(KSTARS_EKOS) << "Resuming and syncing clock.";
141 KStarsData::Instance()->clock()->start();
142 QAction *a = KStars::Instance()->actionCollection()->action("time_to_now");
143 if (a)
144 a->trigger();
145 }
146 }
147 // Otherwise, if KStars was started in PAUSED state
148 // then we pause here as well to save power.
149 else
150 {
151 qCInfo(KSTARS_EKOS) << "EkosLive client is disconnected.";
152 // It was started with paused state, so let's pause IF Ekos is not running
153 if (KStars::Instance()->isStartedWithClockRunning() == false && m_Manager->ekosStatus() == Ekos::CommunicationStatus::Idle)
154 {
155 qCInfo(KSTARS_EKOS) << "Stopping the clock.";
156 KStarsData::Instance()->clock()->stop();
157 }
158 }
159 }
160 else if (command == commands[GET_DRIVERS])
161 sendDrivers();
162 else if (command == commands[GET_PROFILES])
163 sendProfiles();
164 else if (command == commands[GET_SCOPES])
165 sendScopes();
166 else if (command == commands[GET_DSLR_LENSES])
167 sendDSLRLenses();
168 else if(command == commands[INVOKE_METHOD])
169 {
170 auto object = findObject(payload["object"].toString());
171 if (object)
172 invokeMethod(object, payload);
173 }
174 else if(command == commands[SET_PROPERTY])
175 {
176 auto object = findObject(payload["object"].toString());
177 if (object)
178 object->setProperty(payload["name"].toString().toLatin1().constData(), payload["value"].toVariant());
179 }
180 else if(command == commands[GET_PROPERTY])
181 {
182 auto map = QVariantMap();
183 map["result"] = false;
184 auto object = findObject(payload["object"].toString());
185 if (object)
186 {
187 auto value = object->property(payload["name"].toString().toLatin1().constData());
188 if (value.isValid())
189 {
190 map["result"] = true;
191 map["value"] = value;
192 }
193 }
194 sendResponse(commands[GET_PROPERTY], QJsonObject::fromVariantMap(map));
195 }
196 else if (command.startsWith("scope_"))
197 processScopeCommands(command, payload);
198 else if (command.startsWith("profile_"))
199 processProfileCommands(command, payload);
200 else if (command.startsWith("astro_"))
201 processAstronomyCommands(command, payload);
202 else if (command == commands[DIALOG_GET_RESPONSE])
203 processDialogResponse(payload);
204 else if (command.startsWith("option_"))
205 processOptionsCommands(command, payload);
206 else if (command.startsWith("scheduler"))
207 processSchedulerCommands(command, payload);
208 else if (command.startsWith("dslr_"))
209 processDSLRCommands(command, payload);
210
211 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
212 return;
213
214 if (command == commands[GET_STATES])
215 sendStates();
216 else if (command == commands[GET_STELLARSOLVER_PROFILES])
217 sendStellarSolverProfiles();
218 else if (command == commands[GET_DEVICES])
219 sendDevices();
220 else if (command.startsWith("capture_"))
221 processCaptureCommands(command, payload);
222 else if (command.startsWith("mount_"))
223 processMountCommands(command, payload);
224 else if (command.startsWith("focus_"))
225 processFocusCommands(command, payload);
226 else if (command.startsWith("guide_"))
227 processGuideCommands(command, payload);
228 else if (command.startsWith("align_"))
229 processAlignCommands(command, payload);
230 else if (command.startsWith("polar_"))
231 processPolarCommands(command, payload);
232 else if (command.startsWith("train_"))
233 processTrainCommands(command, payload);
234 else if (command.startsWith("fm_"))
235 processFilterManagerCommands(command, payload);
236 else if (command.startsWith("dark_library_"))
237 processDarkLibraryCommands(command, payload);
238 else if (command.startsWith("device_"))
239 processDeviceCommands(command, payload);
240
241}
242
243///////////////////////////////////////////////////////////////////////////////////////////
244///
245///////////////////////////////////////////////////////////////////////////////////////////
246bool Message::isConnected() const
247{
248 return std::any_of(m_NodeManagers.begin(), m_NodeManagers.end(), [](auto & nodeManager)
249 {
250 return nodeManager->message()->isConnected();
251 });
252}
253
254///////////////////////////////////////////////////////////////////////////////////////////
255///
256///////////////////////////////////////////////////////////////////////////////////////////
257void Message::sendStellarSolverProfiles()
258{
259 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
260 return;
261
262 QJsonObject profiles;
263
264 if (m_Manager->focusModule())
265 profiles.insert("focus", QJsonArray::fromStringList(m_Manager->focusModule()->getStellarSolverProfiles()));
266 // TODO
267 // if (m_Manager->guideModule())
268 // profiles.insert("guide", QJsonArray::fromStringList(m_Manager->guideModule()->getStellarSolverProfiles()));
269 if (m_Manager->alignModule())
270 profiles.insert("align", QJsonArray::fromStringList(m_Manager->alignModule()->getStellarSolverProfiles()));
271
272
273 sendResponse(commands[GET_STELLARSOLVER_PROFILES], profiles);
274}
275
276///////////////////////////////////////////////////////////////////////////////////////////
277///
278///////////////////////////////////////////////////////////////////////////////////////////
279void Message::sendDrivers()
280{
281 sendResponse(commands[GET_DRIVERS], DriverManager::Instance()->getDriverList());
282}
283
284///////////////////////////////////////////////////////////////////////////////////////////
285///
286///////////////////////////////////////////////////////////////////////////////////////////
287void Message::sendDevices()
288{
289 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
290 return;
291
293
294 for(auto &gd : INDIListener::devices())
295 {
297 {
298 {"name", gd->getDeviceName()},
299 {"connected", gd->isConnected()},
300 {"version", gd->getDriverVersion()},
301 {"interface", static_cast<int>(gd->getDriverInterface())},
302 };
303
304 deviceList.append(oneDevice);
305 }
306
307 sendResponse(commands[GET_DEVICES], deviceList);
308}
309
310///////////////////////////////////////////////////////////////////////////////////////////
311///
312///////////////////////////////////////////////////////////////////////////////////////////
313void Message::sendTrains()
314{
315 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
316 return;
317
319
320 for(auto &train : Ekos::OpticalTrainManager::Instance()->getOpticalTrains())
321 trains.append(QJsonObject::fromVariantMap(train));
322
323 sendResponse(commands[TRAIN_GET_ALL], trains);
324}
325
326///////////////////////////////////////////////////////////////////////////////////////////
327///
328///////////////////////////////////////////////////////////////////////////////////////////
329void Message::sendTrainProfiles()
330{
331 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
332 return;
333
334 auto profiles = Ekos::ProfileSettings::Instance()->getSettings();
335
336 sendResponse(commands[TRAIN_GET_PROFILES], QJsonObject::fromVariantMap(profiles));
337}
338
339///////////////////////////////////////////////////////////////////////////////////////////
340///
341///////////////////////////////////////////////////////////////////////////////////////////
342void Message::requestOpticalTrains(bool show)
343{
344 sendResponse(commands[TRAIN_CONFIGURATION_REQUESTED], show);
345}
346
347///////////////////////////////////////////////////////////////////////////////////////////
348///
349///////////////////////////////////////////////////////////////////////////////////////////
350void Message::sendScopes()
351{
352 QJsonArray scopeList;
353
355 KStarsData::Instance()->userdb()->GetAllScopes(allScopes);
356
357 for (auto &scope : allScopes)
358 scopeList.append(scope->toJson());
359
360 sendResponse(commands[GET_SCOPES], scopeList);
361}
362
363///////////////////////////////////////////////////////////////////////////////////////////
364///
365///////////////////////////////////////////////////////////////////////////////////////////
366void Message::sendDSLRLenses()
367{
369
371 KStarsData::Instance()->userdb()->GetAllDSLRLenses(allDslrLens);
372
373 for (auto &dslrLens : allDslrLens)
374 dslrList.append(dslrLens->toJson());
375
376 sendResponse(commands[GET_DSLR_LENSES], dslrList);
377}
378
379///////////////////////////////////////////////////////////////////////////////////////////
380///
381///////////////////////////////////////////////////////////////////////////////////////////
382void Message::sendTemperature(double value)
383{
384 ISD::Camera *oneCCD = dynamic_cast<ISD::Camera*>(sender());
385
386 if (oneCCD)
387 {
388 QJsonObject temperature =
389 {
390 {"name", oneCCD->getDeviceName()},
391 {"temperature", value}
392 };
393
394 sendResponse(commands[NEW_CAMERA_STATE], temperature);
395 }
396}
397
398///////////////////////////////////////////////////////////////////////////////////////////
399///
400///////////////////////////////////////////////////////////////////////////////////////////
401void Message::processCaptureCommands(const QString &command, const QJsonObject &payload)
402{
403 Ekos::Capture *capture = m_Manager->captureModule();
404
405 if (capture == nullptr)
406 {
407 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as capture module is not available";
408 return;
409 }
410
411 if (command == commands[CAPTURE_PREVIEW])
412 {
413 capture->cameraUI->capturePreview();
414 }
415 else if (command == commands[CAPTURE_TOGGLE_VIDEO])
416 {
417 capture->setVideoLimits(payload["maxBufferSize"].toInt(512), payload["maxPreviewFPS"].toInt(10));
418 capture->toggleVideo(payload["enabled"].toBool());
419 }
420 else if (command == commands[CAPTURE_START])
421 capture->start();
422 else if (command == commands[CAPTURE_STOP])
423 capture->stop();
424 else if (command == commands[CAPTURE_LOOP])
425 {
426 capture->cameraUI->startFraming();
427 }
428 else if (command == commands[CAPTURE_GET_SEQUENCES])
429 {
430 sendCaptureSequence(capture->getSequence());
431 }
432 else if (command == commands[CAPTURE_ADD_SEQUENCE])
433 {
434 // Now add job
435 capture->cameraUI->createJob();
436 }
437 else if (command == commands[CAPTURE_REMOVE_SEQUENCE])
438 {
439 if (capture->cameraUI->removeJob(payload["index"].toInt()) == false)
440 sendCaptureSequence(capture->getSequence());
441 }
442 else if (command == commands[CAPTURE_CLEAR_SEQUENCES])
443 {
444 capture->clearSequenceQueue();
445 }
446 else if (command == commands[CAPTURE_SAVE_SEQUENCE_FILE])
447 {
448 if (capture->saveSequenceQueue(payload["filepath"].toString()))
449 sendResponse(commands[CAPTURE_SAVE_SEQUENCE_FILE], QString::fromUtf8(QFile(payload["filepath"].toString()).readAll()));
450 }
451 else if (command == commands[CAPTURE_LOAD_SEQUENCE_FILE])
452 {
454 if (payload.contains("filedata"))
455 {
456 QTemporaryFile file;
457 if (file.open())
458 {
459 file.setAutoRemove(false);
460 path = file.fileName();
461 file.write(payload["filedata"].toString().toUtf8());
462 file.close();
463 }
464 }
465 else
466 path = payload["filepath"].toString();
467
468 if (!path.isEmpty())
469 {
470 auto result = capture->loadSequenceQueue(path);
472 {
473 {"result", result},
474 {"path", path}
475 };
476 sendResponse(commands[CAPTURE_LOAD_SEQUENCE_FILE], response);
477 }
478 }
479 else if (command == commands[CAPTURE_GET_ALL_SETTINGS])
480 {
481 sendCaptureSettings(capture->cameraUI->getAllSettings());
482 }
483 else if (command == commands[CAPTURE_SET_ALL_SETTINGS])
484 {
485 auto settings = payload.toVariantMap();
486 capture->cameraUI->setAllSettings(settings);
487 KSUtils::setGlobalSettings(settings);
488 }
489 else if (command == commands[CAPTURE_GENERATE_DARK_FLATS])
490 {
491 capture->cameraUI->generateDarkFlats();
492 }
493}
494
495///////////////////////////////////////////////////////////////////////////////////////////
496///
497///////////////////////////////////////////////////////////////////////////////////////////
498void Message::sendCaptureSequence(const QJsonArray &sequenceArray)
499{
500 sendResponse(commands[CAPTURE_GET_SEQUENCES], sequenceArray);
501}
502
503void Message::sendPreviewLabel(const QString &preview)
504{
505 const QJsonObject payload =
506 {
507 {"preview", preview}
508 };
509 sendResponse(commands[CAPTURE_GET_PREVIEW_LABEL], payload);
510}
511
512///////////////////////////////////////////////////////////////////////////////////////////
513///
514///////////////////////////////////////////////////////////////////////////////////////////
515void Message::sendCaptureSettings(const QVariantMap &settings)
516{
517 m_DebouncedSend.start();
518 m_DebouncedMap[commands[CAPTURE_GET_ALL_SETTINGS]] = settings;
519}
520
521///////////////////////////////////////////////////////////////////////////////////////////
522///
523///////////////////////////////////////////////////////////////////////////////////////////
524void Message::sendAlignSettings(const QVariantMap &settings)
525{
526 m_DebouncedSend.start();
527 m_DebouncedMap[commands[ALIGN_GET_ALL_SETTINGS]] = settings;
528}
529
530///////////////////////////////////////////////////////////////////////////////////////////
531///
532///////////////////////////////////////////////////////////////////////////////////////////
533void Message::sendGuideSettings(const QVariantMap &settings)
534{
535 m_DebouncedSend.start();
536 m_DebouncedMap[commands[GUIDE_GET_ALL_SETTINGS]] = settings;
537
538}
539
540///////////////////////////////////////////////////////////////////////////////////////////
541///
542///////////////////////////////////////////////////////////////////////////////////////////
543void Message::sendFocusSettings(const QVariantMap &settings)
544{
545 m_DebouncedSend.start();
546 m_DebouncedMap[commands[FOCUS_GET_ALL_SETTINGS]] = settings;
547}
548
549///////////////////////////////////////////////////////////////////////////////////////////
550///
551///////////////////////////////////////////////////////////////////////////////////////////
552void Message::sendMountSettings(const QVariantMap &settings)
553{
554 m_DebouncedSend.start();
555 m_DebouncedMap[commands[MOUNT_GET_ALL_SETTINGS]] = settings;
556}
557
558///////////////////////////////////////////////////////////////////////////////////////////
559///
560///////////////////////////////////////////////////////////////////////////////////////////
561void Message::sendDarkLibrarySettings(const QVariantMap &settings)
562{
563 m_DebouncedSend.start();
564 m_DebouncedMap[commands[DARK_LIBRARY_GET_ALL_SETTINGS]] = settings;
565}
566
567
568///////////////////////////////////////////////////////////////////////////////////////////
569///
570///////////////////////////////////////////////////////////////////////////////////////////
571void Message::sendSchedulerSettings(const QVariantMap &settings)
572{
573 m_DebouncedSend.start();
574 m_DebouncedMap[commands[SCHEDULER_GET_ALL_SETTINGS]] = settings;
575}
576
577///////////////////////////////////////////////////////////////////////////////////////////
578///
579///////////////////////////////////////////////////////////////////////////////////////////
580void Message::dispatchDebounceQueue()
581{
582 QMapIterator<QString, QVariantMap> i(m_DebouncedMap);
583 while (i.hasNext())
584 {
585 i.next();
586 sendResponse(i.key(), QJsonObject::fromVariantMap(i.value()));
587 }
588 m_DebouncedMap.clear();
589
590 // Save to disk
591 Options::self()->save();
592}
593
594///////////////////////////////////////////////////////////////////////////////////////////
595///
596///////////////////////////////////////////////////////////////////////////////////////////
597void Message::processGuideCommands(const QString &command, const QJsonObject &payload)
598{
599 Ekos::Guide *guide = m_Manager->guideModule();
600
601 if (guide == nullptr)
602 {
603 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as guide module is not available";
604 return;
605 }
606
607 if (command == commands[GUIDE_START])
608 {
609 guide->guide();
610 }
611 else if (command == commands[GUIDE_CAPTURE])
612 guide->capture();
613 else if (command == commands[GUIDE_LOOP])
614 guide->loop();
615 else if (command == commands[GUIDE_STOP])
616 guide->abort();
617 else if (command == commands[GUIDE_CLEAR])
618 guide->clearCalibration();
619 else if (command == commands[GUIDE_SET_ALL_SETTINGS])
620 {
621 auto settings = payload.toVariantMap();
622 guide->setAllSettings(settings);
623 KSUtils::setGlobalSettings(settings);
624 }
625 else if (command == commands[GUIDE_GET_ALL_SETTINGS])
626 sendGuideSettings(guide->getAllSettings());
627 else if(command == commands[GUIDE_SET_CALIBRATION_SETTINGS])
628 {
629
630 Options::setCalibrationPulseDuration(payload["pulse"].toInt());
631 Options::setGuideCalibrationBacklash(payload["max_move"].toInt());
632 Options::setTwoAxisEnabled(payload["two_axis"].toBool());
633 Options::setGuideAutoSquareSizeEnabled(payload["square_size"].toBool());
634 Options::setGuideCalibrationBacklash(payload["calibrationBacklash"].toBool());
635 Options::setResetGuideCalibration(payload["resetCalibration"].toBool());
636 Options::setReuseGuideCalibration(payload["reuseCalibration"].toBool());
637 Options::setReverseDecOnPierSideChange(payload["reverseCalibration"].toBool());
638 sendGuideSettings(m_Manager->guideModule()->getAllSettings());
639 }
640}
641
642///////////////////////////////////////////////////////////////////////////////////////////
643///
644///////////////////////////////////////////////////////////////////////////////////////////
645void Message::processFocusCommands(const QString &command, const QJsonObject &payload)
646{
647 Ekos::Focus *focus = m_Manager->focusModule();
648
649 if (focus == nullptr)
650 {
651 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as focus module is not available";
652 return;
653 }
654
655 if (command == commands[FOCUS_START])
656 focus->start();
657 else if (command == commands[FOCUS_CAPTURE])
658 {
659 focus->resetFrame();
660 focus->capture();
661 }
662 else if (command == commands[FOCUS_STOP])
663 focus->abort();
664 else if (command == commands[FOCUS_RESET])
665 focus->resetFrame();
666 else if (command == commands[FOCUS_IN])
667 focus->focusIn(payload["steps"].toInt());
668 else if (command == commands[FOCUS_OUT])
669 focus->focusOut(payload["steps"].toInt());
670 else if (command == commands[FOCUS_LOOP])
671 focus->startFraming();
672 else if (command == commands[FOCUS_SET_ALL_SETTINGS])
673 {
674 auto settings = payload.toVariantMap();
675 focus->setAllSettings(settings);
676 KSUtils::setGlobalSettings(settings);
677 }
678
679 else if (command == commands[FOCUS_GET_ALL_SETTINGS])
680 sendFocusSettings(focus->getAllSettings());
681 else if (command == commands[FOCUS_SET_CROSSHAIR])
682 {
683 double x = payload["x"].toDouble();
684 double y = payload["y"].toDouble();
685 focus->selectFocusStarFraction(x, y);
686 }
687}
688
689///////////////////////////////////////////////////////////////////////////////////////////
690///
691///////////////////////////////////////////////////////////////////////////////////////////
692void Message::processMountCommands(const QString &command, const QJsonObject &payload)
693{
694 Ekos::Mount *mount = m_Manager->mountModule();
695
696 if (mount == nullptr)
697 {
698 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as mount module is not available";
699 return;
700 }
701
702 if (command == commands[MOUNT_ABORT])
703 mount->abort();
704 else if (command == commands[MOUNT_PARK])
705 mount->park();
706 else if (command == commands[MOUNT_UNPARK])
707 mount->unpark();
708 else if (command == commands[MOUNT_SET_TRACKING])
709 mount->setTrackEnabled(payload["enabled"].toBool());
710 else if (command == commands[MOUNT_SYNC_RADE])
711 {
712 mount->setJ2000Enabled(payload["isJ2000"].toBool());
713 mount->sync(payload["ra"].toString(), payload["de"].toString());
714 }
715 else if (command == commands[MOUNT_SYNC_TARGET])
716 {
717 mount->syncTarget(payload["target"].toString());
718 }
719 else if (command == commands[MOUNT_GOTO_RADE])
720 {
721 mount->setJ2000Enabled(payload["isJ2000"].toBool());
722 mount->slew(payload["ra"].toString(), payload["de"].toString());
723 }
724 else if (command == commands[MOUNT_GOTO_TARGET])
725 {
726 mount->gotoTarget(payload["target"].toString());
727 }
728 else if (command == commands[MOUNT_SET_SLEW_RATE])
729 {
730 int rate = payload["rate"].toInt(-1);
731 if (rate >= 0)
732 mount->setSlewRate(rate);
733 }
734 else if (command == commands[MOUNT_SET_ALL_SETTINGS])
735 {
736 auto settings = payload.toVariantMap();
737 mount->setAllSettings(settings);
738 KSUtils::setGlobalSettings(settings);
739 }
740 else if (command == commands[MOUNT_GET_ALL_SETTINGS])
741 sendMountSettings(mount->getAllSettings());
742 else if (command == commands[MOUNT_SET_MOTION])
743 {
744 QString direction = payload["direction"].toString();
745 ISD::Mount::MotionCommand action = payload["action"].toBool(false) ?
746 ISD::Mount::MOTION_START : ISD::Mount::MOTION_STOP;
747
748 if (direction == "N")
749 mount->motionCommand(action, ISD::Mount::MOTION_NORTH, -1);
750 else if (direction == "S")
751 mount->motionCommand(action, ISD::Mount::MOTION_SOUTH, -1);
752 else if (direction == "E")
753 mount->motionCommand(action, -1, ISD::Mount::MOTION_EAST);
754 else if (direction == "W")
755 mount->motionCommand(action, -1, ISD::Mount::MOTION_WEST);
756 }
757 else if (command == commands[MOUNT_GOTO_PIXEL])
758 {
759 const auto name = payload["camera"].toString();
760 const auto xFactor = payload["x"].toDouble();
761 const auto yFactor = payload["y"].toDouble();
762
763 for(auto &oneDevice : INDIListener::devices())
764 {
765 auto camera = oneDevice->getCamera();
766 if (!camera || camera->getDeviceName() != name)
767 continue;
768
769 auto primaryChip = camera->getChip(ISD::CameraChip::PRIMARY_CCD);
770
771 if (!primaryChip)
772 break;
773
774 auto imageData = primaryChip->getImageData();
775 if (!imageData || imageData->hasWCS() == false)
776 break;
777
778 auto x = xFactor * imageData->width();
779 auto y = yFactor * imageData->height();
780
781 QPointF point(x, y);
783 if (imageData->pixelToWCS(point, coord))
784 {
785 // J2000 -> JNow
786 coord.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd());
787 mount->gotoTarget(coord);
788 break;
789 }
790 }
791 }
792 else if (command == commands[MOUNT_TOGGLE_AUTOPARK])
793 mount->setAutoParkEnabled(payload["toggled"].toBool());
794}
795
796///////////////////////////////////////////////////////////////////////////////////////////
797///
798///////////////////////////////////////////////////////////////////////////////////////////
799void Message::processAlignCommands(const QString &command, const QJsonObject &payload)
800{
801 Ekos::Align *align = m_Manager->alignModule();
802
803 if (align == nullptr)
804 {
805 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as align module is not available";
806 return;
807 }
808
809 if (command == commands[ALIGN_SOLVE])
810 {
811 align->captureAndSolve();
812 }
813 else if (command == commands[ALIGN_SET_ALL_SETTINGS])
814 {
815 auto settings = payload.toVariantMap();
816 align->setAllSettings(settings);
817 KSUtils::setGlobalSettings(settings);
818 }
819 else if (command == commands[ALIGN_GET_ALL_SETTINGS])
820 sendAlignSettings(align->getAllSettings());
821 else if(command == commands[ALIGN_SET_ASTROMETRY_SETTINGS])
822 {
823 Options::setAstrometryRotatorThreshold(payload["threshold"].toInt());
824 Options::setAstrometryUseRotator(payload["rotator_control"].toBool());
825 Options::setAstrometryUseImageScale(payload["scale"].toBool());
826 Options::setAstrometryUsePosition(payload["position"].toBool());
827 }
828 else if (command == commands[ALIGN_STOP])
829 align->abort();
830 else if (command == commands[ALIGN_LOAD_AND_SLEW])
831 {
832 // Check if we have filename payload first
833 if (payload.contains("filename"))
834 {
835 align->loadAndSlew(payload["filename"].toString());
836 }
837 else
838 {
839 QString filename = QDir::tempPath() + QDir::separator() +
840 QString("XXXXXXloadslew.%1").arg(payload["ext"].toString("fits"));
841 QTemporaryFile file(filename);
842 file.setAutoRemove(false);
843 file.open();
844 file.write(QByteArray::fromBase64(payload["data"].toString().toLatin1()));
845 file.close();
846 align->loadAndSlew(file.fileName());
847 }
848 }
849 else if (command == commands[ALIGN_MANUAL_ROTATOR_TOGGLE])
850 {
851 align->toggleManualRotator(payload["toggled"].toBool());
852 }
853}
854
855///////////////////////////////////////////////////////////////////////////////////////////
856///
857///////////////////////////////////////////////////////////////////////////////////////////
858void Message::setAlignStatus(Ekos::AlignState newState)
859{
860 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
861 return;
862
863 QJsonObject alignState =
864 {
865 {"status", Ekos::alignStates[newState]}
866 };
867
868 sendResponse(commands[NEW_ALIGN_STATE], alignState);
869}
870
871///////////////////////////////////////////////////////////////////////////////////////////
872///
873///////////////////////////////////////////////////////////////////////////////////////////
874void Message::setAlignSolution(const QVariantMap &solution)
875{
876 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
877 return;
878
879 QJsonObject alignState =
880 {
881 {"solution", QJsonObject::fromVariantMap(solution)},
882 };
883
884 sendResponse(commands[NEW_ALIGN_STATE], alignState);
885}
886
887///////////////////////////////////////////////////////////////////////////////////////////
888///
889///////////////////////////////////////////////////////////////////////////////////////////
890void Message::processSchedulerCommands(const QString &command, const QJsonObject &payload)
891{
892 Ekos::Scheduler *scheduler = m_Manager->schedulerModule();
893
894 if (command == commands[SCHEDULER_GET_JOBS])
895 {
896 sendSchedulerJobs();
897 }
898 else if (command == commands[SCHEDULER_ADD_JOBS])
899 {
900 scheduler->addJob();
901 }
902 else if(command == commands[SCHEDULER_REMOVE_JOBS])
903 {
904 int index = payload["index"].toInt();
905 scheduler->removeOneJob(index);
906 }
907 else if(command == commands[SCHEDULER_GET_ALL_SETTINGS])
908 {
909 sendSchedulerSettings(scheduler->getAllSettings());
910 }
911 else if(command == commands[SCHEDULER_SET_ALL_SETTINGS])
912 {
913 auto settings = payload.toVariantMap();
914 scheduler->setAllSettings(settings);
915 KSUtils::setGlobalSettings(settings);
916 }
917 else if (command == commands[SCHEDULER_SAVE_FILE])
918 {
919 if (scheduler->saveFile(QUrl::fromLocalFile(payload["filepath"].toString())))
920 sendResponse(commands[SCHEDULER_SAVE_FILE], QString::fromUtf8(QFile(payload["filepath"].toString()).readAll()));
921 }
922 else if (command == commands[SCHEDULER_LOAD_FILE])
923 {
925 if (payload.contains("filedata"))
926 {
927 QTemporaryFile file;
928 if (file.open())
929 {
930 file.setAutoRemove(false);
931 path = file.fileName();
932 file.write(payload["filedata"].toString().toUtf8());
933 file.close();
934 }
935 }
936 else
937 path = payload["filepath"].toString();
938
939 if (!path.isEmpty())
940 {
941 auto result = scheduler->loadFile(QUrl::fromLocalFile(path));
943 {
944 {"result", result},
945 {"path", path}
946 };
947 sendResponse(commands[SCHEDULER_LOAD_FILE], response);
948 }
949 }
950 else if(command == commands[SCHEDULER_START_JOB])
951 {
952 scheduler->toggleScheduler();
953 }
954 else if(command == commands[SCHEDULER_IMPORT_MOSAIC])
955 {
956 if (scheduler->importMosaic(payload))
957 sendSchedulerJobs();
958 else
959 sendEvent(i18n("Mosaic import failed."), KSNotification::Scheduler, KSNotification::Alert);
960 }
961}
962
963///////////////////////////////////////////////////////////////////////////////////////////
964///
965///////////////////////////////////////////////////////////////////////////////////////////
966void Message::processPolarCommands(const QString &command, const QJsonObject &payload)
967{
968 Ekos::Align *align = m_Manager->alignModule();
969 Ekos::PolarAlignmentAssistant *paa = align->polarAlignmentAssistant();
970
971 if (!paa)
972 return;
973
974 if (command == commands[PAH_START])
975 {
976 paa->startPAHProcess();
977 }
978 if (command == commands[PAH_STOP])
979 {
980 paa->stopPAHProcess();
981 }
982 else if (command == commands[PAH_REFRESH])
983 {
984 paa->setPAHRefreshDuration(payload["value"].toDouble(1));
985 paa->startPAHRefreshProcess();
986 }
987 else if (command == commands[PAH_SET_ALGORITHM])
988 {
989 auto algorithmCombo = paa->findChild<QComboBox*>("PAHRefreshAlgorithmCombo");
990 if (algorithmCombo)
991 algorithmCombo->setCurrentIndex(static_cast<Ekos::PolarAlignmentAssistant::RefreshAlgorithm>(payload["value"].toInt(1)));
992 }
993 else if (command == commands[PAH_RESET_VIEW])
994 {
995 emit resetPolarView();
996 }
997 else if (command == commands[PAH_SET_CROSSHAIR])
998 {
999 double x = payload["x"].toDouble();
1000 double y = payload["y"].toDouble();
1001
1002 if (m_BoundingRect.isNull() == false)
1003 {
1004 // #1 Find actual dimension inside the bounding rectangle
1005 // since if we have bounding rectable then x,y fractions are INSIDE it
1006 double boundX = x * m_BoundingRect.width();
1007 double boundY = y * m_BoundingRect.height();
1008
1009 // #2 Find fraction of the dimensions above the full image size
1010 // Add to it the bounding rect top left offsets
1011 // factors in the change caused by zoom
1012 x = ((boundX + m_BoundingRect.x()) / (m_CurrentZoom / 100)) / m_ViewSize.width();
1013 y = ((boundY + m_BoundingRect.y()) / (m_CurrentZoom / 100)) / m_ViewSize.height();
1014
1015 }
1016
1017 paa->setPAHCorrectionOffsetPercentage(x, y);
1018 }
1019 else if (command == commands[PAH_SELECT_STAR_DONE])
1020 {
1021 // This button was removed from the desktop PAA scheme.
1022 // Nothing to do.
1023 // TODO: Make sure this works.
1024 }
1025 else if (command == commands[PAH_REFRESHING_DONE])
1026 {
1027 paa->stopPAHProcess();
1028 }
1029 else if (command == commands[PAH_SLEW_DONE])
1030 {
1031 paa->setPAHSlewDone();
1032 }
1033 else if (command == commands[PAH_PAH_SET_ZOOM])
1034 {
1035 double scale = payload["scale"].toDouble();
1036 align->setAlignZoom(scale);
1037 }
1038
1039}
1040
1041///////////////////////////////////////////////////////////////////////////////////////////
1042///
1043///////////////////////////////////////////////////////////////////////////////////////////
1044void Message::setPAHStage(Ekos::PolarAlignmentAssistant::Stage stage)
1045{
1046 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1047 return;
1048
1049 Q_UNUSED(stage)
1050 Ekos::Align *align = m_Manager->alignModule();
1051
1052 Ekos::PolarAlignmentAssistant *paa = align->polarAlignmentAssistant();
1053
1054 if (!paa)
1055 return;
1056
1058 {
1059 {"stage", paa->getPAHStageString(false)}
1060 };
1061
1062
1063 // Increase size when select star
1064 if (stage == Ekos::PolarAlignmentAssistant::PAH_STAR_SELECT)
1065 align->zoomAlignView();
1066
1067 sendResponse(commands[NEW_POLAR_STATE], polarState);
1068}
1069
1070///////////////////////////////////////////////////////////////////////////////////////////
1071///
1072///////////////////////////////////////////////////////////////////////////////////////////
1073void Message::setPAHMessage(const QString &message)
1074{
1075 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1076 return;
1077
1078 QTextDocument doc;
1079 doc.setHtml(message);
1081 {
1082 {"message", doc.toPlainText()}
1083 };
1084
1085 sendResponse(commands[NEW_POLAR_STATE], polarState);
1086}
1087
1088///////////////////////////////////////////////////////////////////////////////////////////
1089///
1090///////////////////////////////////////////////////////////////////////////////////////////
1091void Message::setPolarResults(QLineF correctionVector, double polarError, double azError, double altError)
1092{
1093 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1094 return;
1095
1096 this->correctionVector = correctionVector;
1097
1098 QPointF center = 0.5 * correctionVector.p1() + 0.5 * correctionVector.p2();
1099 QJsonObject vector =
1100 {
1101 {"center_x", center.x()},
1102 {"center_y", center.y()},
1103 {"mag", correctionVector.length()},
1104 {"pa", correctionVector.angle()},
1105 {"error", polarError},
1106 {"azError", azError},
1107 {"altError", altError}
1108 };
1109
1111 {
1112 {"vector", vector}
1113 };
1114
1115 sendResponse(commands[NEW_POLAR_STATE], polarState);
1116}
1117
1118///////////////////////////////////////////////////////////////////////////////////////////
1119///
1120///////////////////////////////////////////////////////////////////////////////////////////
1121void Message::setUpdatedErrors(double total, double az, double alt)
1122{
1123 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1124 return;
1125
1127 {
1128 {"updatedError", total},
1129 {"updatedAZError", az},
1130 {"updatedALTError", alt}
1131 };
1132
1133 sendResponse(commands[NEW_POLAR_STATE], error);
1134}
1135
1136///////////////////////////////////////////////////////////////////////////////////////////
1137///
1138///////////////////////////////////////////////////////////////////////////////////////////
1139void Message::setPAHEnabled(bool enabled)
1140{
1141 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
1142 return;
1143
1145 {
1146 {"enabled", enabled}
1147 };
1148
1149 sendResponse(commands[NEW_POLAR_STATE], polarState);
1150}
1151
1152///////////////////////////////////////////////////////////////////////////////////////////
1153///
1154///////////////////////////////////////////////////////////////////////////////////////////
1155void Message::processProfileCommands(const QString &command, const QJsonObject &payload)
1156{
1157 if (command == commands[START_PROFILE])
1158 {
1159 if (m_Manager->getEkosStartingStatus() != Ekos::Idle)
1160 m_Manager->stop();
1161
1162 m_Manager->setProfile(payload["name"].toString());
1163 // Always Sync time before we start
1164 KStarsData::Instance()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
1165 m_Manager->start();
1166 }
1167 else if (command == commands[STOP_PROFILE])
1168 {
1169 m_Manager->stop();
1170
1171 // Close all FITS Viewers
1172 KStars::Instance()->clearAllViewers();
1173
1174 m_PropertySubscriptions.clear();
1175 }
1176 else if (command == commands[ADD_PROFILE])
1177 {
1178 m_Manager->addNamedProfile(payload);
1179 sendProfiles();
1180 }
1181 else if (command == commands[UPDATE_PROFILE])
1182 {
1183 m_Manager->editNamedProfile(payload);
1184 sendProfiles();
1185 }
1186 else if (command == commands[GET_PROFILE])
1187 {
1188 m_Manager->getNamedProfile(payload["name"].toString());
1189 }
1190 else if (command == commands[DELETE_PROFILE])
1191 {
1192 m_Manager->deleteNamedProfile(payload["name"].toString());
1193 sendProfiles();
1194 }
1195 else if (command == commands[SET_PROFILE_MAPPING])
1196 {
1197 m_Manager->setProfileMapping(payload);
1198 }
1199 else if (command == commands[SET_PROFILE_PORT_SELECTION])
1200 {
1201 requestPortSelection(false);
1202 m_Manager->acceptPortSelection();
1203 }
1204}
1205
1206///////////////////////////////////////////////////////////////////////////////////////////
1207///
1208///////////////////////////////////////////////////////////////////////////////////////////
1209void Message::sendProfiles()
1210{
1212
1214 if (!m_Manager->getCurrentProfile(profile))
1215 return;
1216
1217 for (auto &oneProfile : m_Manager->profiles)
1218 profileArray.append(oneProfile->toJson());
1219
1220 QJsonObject profiles =
1221 {
1222 {"selectedProfile", profile->name},
1223 {"profiles", profileArray}
1224 };
1225 sendResponse(commands[GET_PROFILES], profiles);
1226}
1227
1228///////////////////////////////////////////////////////////////////////////////////////////
1229///
1230///////////////////////////////////////////////////////////////////////////////////////////
1231void Message::sendSchedulerJobs()
1232{
1233 QJsonObject jobs =
1234 {
1235 {"jobs", m_Manager->schedulerModule()->moduleState()->getJSONJobs()}
1236 };
1237 sendResponse(commands[SCHEDULER_GET_JOBS], jobs);
1238}
1239
1240///////////////////////////////////////////////////////////////////////////////////////////
1241///
1242///////////////////////////////////////////////////////////////////////////////////////////
1243void Message::sendSchedulerJobList(QJsonArray jobsList)
1244{
1245 QJsonObject jobs =
1246 {
1247 {"jobs", jobsList}
1248 };
1249 sendResponse(commands[SCHEDULER_GET_JOBS], jobs);
1250}
1251
1252///////////////////////////////////////////////////////////////////////////////////////////
1253///
1254///////////////////////////////////////////////////////////////////////////////////////////
1255void Message::sendSchedulerStatus(const QJsonObject &status)
1256{
1257 sendResponse(commands[NEW_SCHEDULER_STATE], status);
1258}
1259
1260
1261///////////////////////////////////////////////////////////////////////////////////////////
1262///
1263///////////////////////////////////////////////////////////////////////////////////////////
1264void Message::setEkosStatingStatus(Ekos::CommunicationStatus status)
1265{
1266 if (status == Ekos::Pending)
1267 return;
1268
1270 {
1271 {"connected", true},
1272 {"online", status == Ekos::Success}
1273 };
1274 sendResponse(commands[NEW_CONNECTION_STATE], connectionState);
1275}
1276
1277///////////////////////////////////////////////////////////////////////////////////////////
1278///
1279///////////////////////////////////////////////////////////////////////////////////////////
1280void Message::setINDIStatus(Ekos::CommunicationStatus status)
1281{
1283 {
1284 {"status", status},
1285 };
1286
1287 sendResponse(commands[NEW_INDI_STATE], connectionState);
1288}
1289
1290///////////////////////////////////////////////////////////////////////////////////////////
1291///
1292///////////////////////////////////////////////////////////////////////////////////////////
1293void Message::processOptionsCommands(const QString &command, const QJsonObject &payload)
1294{
1295 if (command == commands[OPTION_SET])
1296 {
1297 const QJsonArray options = payload["options"].toArray();
1298 for (const auto &oneOption : options)
1299 Options::self()->setProperty(oneOption["name"].toString().toLatin1(), oneOption["value"].toVariant());
1300
1301 Options::self()->save();
1302 emit optionsUpdated();
1303 }
1304 else if (command == commands[OPTION_GET])
1305 {
1306 const QJsonArray options = payload["options"].toArray();
1307 QJsonArray result;
1308 for (const auto &oneOption : options)
1309 {
1310 const auto name = oneOption["name"].toString();
1311 QVariant value = Options::self()->property(name.toLatin1());
1312 QVariantMap map;
1313 map["name"] = name;
1314 map["value"] = value;
1316 }
1317 sendResponse(commands[OPTION_GET], result);
1318 }
1319}
1320
1321///////////////////////////////////////////////////////////////////////////////////////////
1322///
1323///////////////////////////////////////////////////////////////////////////////////////////
1324void Message::processScopeCommands(const QString &command, const QJsonObject &payload)
1325{
1326 if (command == commands[ADD_SCOPE])
1327 {
1328 KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(),
1329 payload["type"].toString(), payload["aperture"].toDouble(), payload["focal_length"].toDouble());
1330 }
1331 else if (command == commands[UPDATE_SCOPE])
1332 {
1333 KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(),
1334 payload["type"].toString(), payload["aperture"].toDouble(), payload["focal_length"].toDouble(), payload["id"].toString());
1335 }
1336 else if (command == commands[DELETE_SCOPE])
1337 {
1338 KStarsData::Instance()->userdb()->DeleteEquipment("telescope", payload["id"].toString());
1339 }
1340
1341 sendScopes();
1342}
1343
1344///////////////////////////////////////////////////////////////////////////////////////////
1345///
1346///////////////////////////////////////////////////////////////////////////////////////////
1347void Message::processDSLRCommands(const QString &command, const QJsonObject &payload)
1348{
1349 if (command == commands[DSLR_SET_INFO])
1350 {
1351 if (m_Manager->captureModule())
1352 m_Manager->captureModule()->cameraUI->addDSLRInfo(
1353 payload["model"].toString(),
1354 payload["width"].toInt(),
1355 payload["height"].toInt(),
1356 payload["pixelw"].toDouble(),
1357 payload["pixelh"].toDouble());
1358
1359 }
1360 else if(command == commands[DSLR_ADD_LENS])
1361 {
1362 KStarsData::Instance()->userdb()->AddDSLRLens(payload["model"].toString(), payload["vendor"].toString(),
1363 payload["focal_length"].toDouble(), payload["focal_ratio"].toDouble());
1364 }
1365 else if (command == commands[DSLR_DELETE_LENS])
1366 {
1367 KStarsData::Instance()->userdb()->DeleteEquipment("dslrlens", payload["id"].toString());
1368 }
1369 else if (command == commands[DSLR_UPDATE_LENS])
1370 {
1371 KStarsData::Instance()->userdb()->AddDSLRLens(payload["model"].toString(), payload["vendor"].toString(),
1372 payload["focal_length"].toDouble(), payload["focal_ratio"].toDouble(), payload["id"].toString());
1373 }
1374
1375 sendDSLRLenses();
1376}
1377
1378///////////////////////////////////////////////////////////////////////////////////////////
1379///
1380///////////////////////////////////////////////////////////////////////////////////////////
1381void Message::processTrainCommands(const QString &command, const QJsonObject &payload)
1382{
1383 if (command == commands[TRAIN_GET_ALL])
1384 sendTrains();
1385 else if (command == commands[TRAIN_GET_PROFILES])
1386 sendTrainProfiles();
1387 else if (command == commands[TRAIN_SET])
1388 {
1389 auto module = payload["module"].toString();
1390 auto name = payload["name"].toString();
1391
1392 if (module == "capture")
1393 {
1394 if (m_Manager->captureModule())
1395 m_Manager->captureModule()->setOpticalTrain(name);
1396 }
1397 else if (module == "focus")
1398 {
1399 if (m_Manager->focusModule())
1400 m_Manager->focusModule()->setOpticalTrain(name);
1401 }
1402 else if (module == "guide")
1403 {
1404 if (m_Manager->guideModule())
1405 m_Manager->guideModule()->setOpticalTrain(name);
1406 }
1407 else if (module == "align")
1408 {
1409 if (m_Manager->alignModule())
1410 m_Manager->alignModule()->setOpticalTrain(name);
1411 }
1412 else if (module == "mount")
1413 {
1414 if (m_Manager->mountModule())
1415 m_Manager->mountModule()->setOpticalTrain(name);
1416 }
1417 else if (module == "darklibrary")
1418 {
1419 Ekos::DarkLibrary::Instance()->setOpticalTrain(name);
1420 }
1421 }
1422 else if (command == commands[TRAIN_ADD])
1423 {
1424 Ekos::OpticalTrainManager::Instance()->addOpticalTrain(payload);
1425 }
1426 else if (command == commands[TRAIN_UPDATE])
1427 {
1428 Ekos::OpticalTrainManager::Instance()->setOpticalTrain(payload);
1429 }
1430 else if (command == commands[TRAIN_DELETE])
1431 {
1432 Ekos::OpticalTrainManager::Instance()->removeOpticalTrain(payload["name"].toString());
1433 }
1434 else if (command == commands[TRAIN_RESET])
1435 {
1436 Ekos::OpticalTrainManager::Instance()->reset();
1437 }
1438 else if (command == commands[TRAIN_ACCEPT])
1439 {
1440 requestOpticalTrains(false);
1441 Ekos::OpticalTrainManager::Instance()->accept();
1442 }
1443
1444}
1445
1446///////////////////////////////////////////////////////////////////////////////////////////
1447///
1448///////////////////////////////////////////////////////////////////////////////////////////
1449void Message::processFilterManagerCommands(const QString &command, const QJsonObject &payload)
1450{
1452 if (m_Manager->captureModule())
1453 manager = m_Manager->captureModule()->cameraUI->filterManager();
1454
1455 if (manager.isNull())
1456 return;
1457
1458 if (command == commands[FM_GET_DATA])
1459 {
1460 QJsonObject data = manager->toJSON();
1461 sendResponse(commands[FM_GET_DATA], data);
1462 }
1463 else if (command == commands[FM_SET_DATA])
1464 {
1465 manager->setFilterData(payload);
1466 }
1467}
1468
1469///////////////////////////////////////////////////////////////////////////////////////////
1470///
1471///////////////////////////////////////////////////////////////////////////////////////////
1472void Message::processDarkLibraryCommands(const QString &command, const QJsonObject &payload)
1473{
1474 if (command == commands[DARK_LIBRARY_START])
1475 Ekos::DarkLibrary::Instance()->start();
1476 else if(command == commands[DARK_LIBRARY_SET_ALL_SETTINGS])
1477 {
1478 auto settings = payload.toVariantMap();
1479 Ekos::DarkLibrary::Instance()->setAllSettings(settings);
1480 KSUtils::setGlobalSettings(settings);
1481 }
1482 else if(command == commands[DARK_LIBRARY_GET_ALL_SETTINGS])
1483 sendDarkLibrarySettings(Ekos::DarkLibrary::Instance()->getAllSettings());
1484 else if(command == commands[DARK_LIBRARY_GET_DEFECT_SETTINGS])
1485 sendResponse(commands[DARK_LIBRARY_GET_DEFECT_SETTINGS], Ekos::DarkLibrary::Instance()->getDefectSettings());
1486 else if(command == commands[DARK_LIBRARY_SET_CAMERA_PRESETS])
1487 {
1488 Ekos::DarkLibrary::Instance()->setCameraPresets(payload);
1489 }
1490 else if (command == commands[DARK_LIBRARY_STOP])
1491 {
1492 Ekos::DarkLibrary::Instance()->stop();
1493 }
1494 else if (command == commands[DARK_LIBRARY_GET_MASTERS_IMAGE])
1495 {
1496 const int row = payload["row"].toInt();
1497 Ekos::DarkLibrary::Instance()->loadIndexInView(row);
1498 }
1499 else if (command == commands[DARK_LIBRARY_GET_CAMERA_PRESETS])
1500 {
1501 sendResponse(commands[DARK_LIBRARY_GET_CAMERA_PRESETS], Ekos::DarkLibrary::Instance()->getCameraPresets());
1502 }
1503 else if (command == commands[DARK_LIBRARY_SET_DEFECT_PIXELS])
1504 {
1505 Ekos::DarkLibrary::Instance()->setDefectPixels(payload);
1506 }
1507 else if (command == commands[DARK_LIBRARY_SAVE_MAP])
1508 {
1509 Ekos::DarkLibrary::Instance()->saveMapB->click();
1510 }
1511 else if (command == commands[DARK_LIBRARY_SET_DEFECT_FRAME])
1512 {
1513 Ekos::DarkLibrary::Instance()->setDefectMapEnabled(false);
1514 }
1515 else if (command == commands[DARK_LIBRARY_GET_VIEW_MASTERS])
1516 {
1517 sendResponse(commands[DARK_LIBRARY_GET_VIEW_MASTERS], Ekos::DarkLibrary::Instance()->getViewMasters());
1518 }
1519 else if (command == commands[DARK_LIBRARY_CLEAR_MASTERS_ROW])
1520 {
1521 const int rowIndex = payload["row"].toInt();
1522 Ekos::DarkLibrary::Instance()->clearRow(rowIndex);
1523 }
1524}
1525
1526///////////////////////////////////////////////////////////////////////////////////////////
1527///
1528///////////////////////////////////////////////////////////////////////////////////////////
1529void Message::processDeviceCommands(const QString &command, const QJsonObject &payload)
1530{
1531 QString device = payload["device"].toString();
1532
1533 // In case we want to UNSUBSCRIBE from all at once
1534 if (device.isEmpty() && command == commands[DEVICE_PROPERTY_UNSUBSCRIBE])
1535 {
1536 m_PropertySubscriptions.clear();
1537 return;
1538 }
1539
1541 if (!INDIListener::findDevice(device, oneDevice))
1542 return;
1543
1544 // Get specific property
1545 if (command == commands[DEVICE_PROPERTY_GET])
1546 {
1548 if (oneDevice->getJSONProperty(payload["property"].toString(), propObject, payload["compact"].toBool(true)))
1549 sendResponse(commands[DEVICE_PROPERTY_GET], propObject);
1550 }
1551 // Set specific property
1552 else if (command == commands[DEVICE_PROPERTY_SET])
1553 {
1554 oneDevice->setJSONProperty(payload["property"].toString(), payload["elements"].toArray());
1555 }
1556 // Return ALL properties
1557 else if (command == commands[DEVICE_GET])
1558 {
1560 for (const auto &oneProp : *oneDevice->getProperties())
1561 {
1563 if (oneDevice->getJSONProperty(oneProp.getName(), singleProp, payload["compact"].toBool(false)))
1564 properties.append(singleProp);
1565 }
1566
1568 {
1569 {"device", device},
1570 {"properties", properties}
1571 };
1572
1573 sendResponse(commands[DEVICE_GET], response);
1574 }
1575 // Subscribe to one or more properties
1576 // When subscribed, the updates are immediately pushed as soon as they are received.
1577 else if (command == commands[DEVICE_PROPERTY_SUBSCRIBE])
1578 {
1579 const QJsonArray properties = payload["properties"].toArray();
1580 const QJsonArray groups = payload["groups"].toArray();
1581
1582 // Get existing subscribed props for this device
1583 QSet<QString> props;
1584 if (m_PropertySubscriptions.contains(device))
1585 props = m_PropertySubscriptions[device];
1586
1587 // If it is just a single property, let's insert it to props.
1588 if (properties.isEmpty() == false)
1589 {
1590 for (const auto &oneProp : properties)
1591 props.insert(oneProp.toString());
1592 }
1593 // If group is specified, then we need to add ALL properties belonging to this group.
1594 else if (groups.isEmpty() == false)
1595 {
1596 QVariantList indiGroups = groups.toVariantList();
1597 for (auto &oneProp : *oneDevice->getProperties())
1598 {
1599 if (indiGroups.contains(oneProp.getGroupName()))
1600 props.insert(oneProp.getName());
1601 }
1602 }
1603 // Otherwise, subscribe to ALL property in this device
1604 else
1605 {
1606 for (auto &oneProp : *oneDevice->getProperties())
1607 props.insert(oneProp.getName());
1608 }
1609
1610 m_PropertySubscriptions[device] = props;
1611 }
1612 else if (command == commands[DEVICE_PROPERTY_UNSUBSCRIBE])
1613 {
1614 const QJsonArray properties = payload["properties"].toArray();
1615 const QJsonArray groups = payload["groups"].toArray();
1616
1617 // Get existing subscribed props for this device
1618 QSet<QString> props;
1619 if (m_PropertySubscriptions.contains(device))
1620 props = m_PropertySubscriptions[device];
1621
1622 // If it is just a single property, let's insert it to props.
1623 // If it is just a single property, let's insert it to props.
1624 if (properties.isEmpty() == false)
1625 {
1626 for (const auto &oneProp : properties)
1627 props.remove(oneProp.toString());
1628 }
1629 // If group is specified, then we need to add ALL properties belonging to this group.
1630 else if (groups.isEmpty() == false)
1631 {
1632 QVariantList indiGroups = groups.toVariantList();
1633 for (auto &oneProp : *oneDevice->getProperties())
1634 {
1635 if (indiGroups.contains(oneProp.getGroupName()))
1636 props.remove(oneProp.getName());
1637 }
1638 }
1639 // Otherwise, subscribe to ALL property in this device
1640 else
1641 {
1642 for (auto &oneProp : *oneDevice->getProperties())
1643 props.remove(oneProp.getName());
1644 }
1645
1646 m_PropertySubscriptions[device] = props;
1647 }
1648}
1649
1650///////////////////////////////////////////////////////////////////////////////////////////
1651///
1652///////////////////////////////////////////////////////////////////////////////////////////
1653void Message::processAstronomyCommands(const QString &command, const QJsonObject &payload)
1654{
1655 if (command == commands[ASTRO_GET_ALMANC])
1656 {
1657 // Today's date
1658 const KStarsDateTime localTime = KStarsData::Instance()->lt();
1659 // Local Midnight
1661
1662 KSAlmanac almanac(midnight, KStarsData::Instance()->geo());
1663
1665 {
1666 {"SunRise", almanac.getSunRise()},
1667 {"SunSet", almanac.getSunSet()},
1668 {"SunMaxAlt", almanac.getSunMaxAlt()},
1669 {"SunMinAlt", almanac.getSunMinAlt()},
1670 {"MoonRise", almanac.getMoonRise()},
1671 {"MoonSet", almanac.getMoonSet()},
1672 {"MoonPhase", almanac.getMoonPhase()},
1673 {"MoonIllum", almanac.getMoonIllum()},
1674 {"Dawn", almanac.getDawnAstronomicalTwilight()},
1675 {"Dusk", almanac.getDuskAstronomicalTwilight()},
1676
1677 };
1678
1679 sendResponse(commands[ASTRO_GET_ALMANC], response);
1680 }
1681 else if (command == commands[ASTRO_GET_NAMES])
1682 {
1683 auto composite = KStarsData::Instance()->skyComposite();
1686 CatalogsDB::CatalogObjectList dsoObjects;
1687
1688 allObjects.append(composite->objectLists(SkyObject::STAR));
1689 allObjects.append(composite->objectLists(SkyObject::CATALOG_STAR));
1690 allObjects.append(composite->objectLists(SkyObject::PLANET));
1691 allObjects.append(composite->objectLists(SkyObject::MOON));
1692 allObjects.append(composite->objectLists(SkyObject::COMET));
1693 allObjects.append(composite->objectLists(SkyObject::ASTEROID));
1694 allObjects.append(composite->objectLists(SkyObject::SUPERNOVA));
1695 allObjects.append(composite->objectLists(SkyObject::SATELLITE));
1696 dsoObjects = m_DSOManager.get_objects_all();
1697
1698 for (auto &oneObject : allObjects)
1699 all << oneObject.second->name() << oneObject.second->longname().split(", ");
1700
1701 for (auto &oneObject : dsoObjects)
1702 all << oneObject.name() << oneObject.longname().split(", ");
1703
1704 all.removeDuplicates();
1706 sendResponse(commands[ASTRO_GET_NAMES], QJsonArray::fromStringList(all));
1707 }
1708 else if (command == commands[ASTRO_GET_DESIGNATIONS])
1709 {
1711
1712 for (auto &oneObject : m_DSOManager.get_objects_all())
1713 {
1715 {
1716 {"primary", oneObject.name()},
1717 {"designations", QJsonArray::fromStringList(oneObject.longname().split(", "))}
1718 };
1719
1721 }
1722
1723 sendResponse(commands[ASTRO_GET_DESIGNATIONS], designations);
1724 }
1725 else if (command == commands[ASTRO_GET_LOCATION])
1726 {
1727 auto geo = KStarsData::Instance()->geo();
1729 {
1730 {"name", geo->name()},
1731 {"longitude", geo->lng()->Degrees()},
1732 {"latitude", geo->lat()->Degrees()},
1733 {"elevation", geo->elevation()},
1734 {"tz", geo->TZ()},
1735 {"tz0", geo->TZ0()}
1736 };
1737
1738 sendResponse(commands[ASTRO_GET_LOCATION], location);
1739 }
1740 // Get a list of object based on criteria
1741 else if (command == commands[ASTRO_SEARCH_OBJECTS])
1742 {
1743 // Set time if required.
1744 if (payload.contains("jd"))
1745 {
1746 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble());
1747 KStarsData::Instance()->clock()->setUTC(jd);
1748 }
1749
1750 // Search Criteria
1751 // Object Type
1752 auto objectType = static_cast<SkyObject::TYPE>(payload["type"].toInt(SkyObject::GALAXY));
1753 // Azimuth restriction
1754 auto objectDirection = static_cast<Direction>(payload["direction"].toInt(All));
1755 // Maximum Object Magnitude
1756 auto objectMaxMagnitude = payload["maxMagnitude"].toDouble(10);
1757 // Minimum Object Altitude
1758 auto objectMinAlt = payload["minAlt"].toDouble(15);
1759 // Minimum Duration that the object must be above the altitude (if any) seconds.
1760 auto objectMinDuration = payload["minDuration"].toInt(3600);
1761 // Minimum FOV in arcmins.
1762 auto objectMinFOV = payload["minFOV"].toDouble(0);
1763 // Data instance
1764 auto *data = KStarsData::Instance();
1765 // Geo Location
1766 auto *geo = KStarsData::Instance()->geo();
1767 // If we are before dawn, we check object altitude restrictions
1768 // Otherwise, all objects are welcome
1769 auto start = KStarsData::Instance()->lt();
1770 auto end = getNextDawn();
1771 if (start > end)
1772 // Add 1 day
1773 end = end.addDays(1);
1774
1776 CatalogsDB::CatalogObjectList dsoObjects;
1777 bool isDSO = false;
1778
1779 switch (objectType)
1780 {
1781 // Stars
1782 case SkyObject::STAR:
1783 case SkyObject::CATALOG_STAR:
1784 allObjects.append(data->skyComposite()->objectLists(SkyObject::STAR));
1785 allObjects.append(data->skyComposite()->objectLists(SkyObject::CATALOG_STAR));
1786 break;
1787 // Planets & Moon
1788 case SkyObject::PLANET:
1789 case SkyObject::MOON:
1790 allObjects.append(data->skyComposite()->objectLists(SkyObject::PLANET));
1791 allObjects.append(data->skyComposite()->objectLists(SkyObject::MOON));
1792 break;
1793 // Comets & Asteroids
1794 case SkyObject::COMET:
1795 allObjects.append(data->skyComposite()->objectLists(SkyObject::COMET));
1796 break;
1797 case SkyObject::ASTEROID:
1798 allObjects.append(data->skyComposite()->objectLists(SkyObject::ASTEROID));
1799 break;
1800 // Clusters
1801 case SkyObject::OPEN_CLUSTER:
1802 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::OPEN_CLUSTER, objectMaxMagnitude));
1803 isDSO = true;
1804 break;
1805 case SkyObject::GLOBULAR_CLUSTER:
1806 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GLOBULAR_CLUSTER, objectMaxMagnitude));
1807 isDSO = true;
1808 break;
1809 // Nebuale
1810 case SkyObject::GASEOUS_NEBULA:
1811 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GASEOUS_NEBULA, objectMaxMagnitude));
1812 isDSO = true;
1813 break;
1814 case SkyObject::PLANETARY_NEBULA:
1815 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::PLANETARY_NEBULA, objectMaxMagnitude));
1816 isDSO = true;
1817 break;
1818 case SkyObject::GALAXY:
1819 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GALAXY, objectMaxMagnitude));
1820 isDSO = true;
1821 break;
1822 case SkyObject::SUPERNOVA:
1823 {
1824 if (!Options::showSupernovae())
1825 {
1826 Options::setShowSupernovae(true);
1827 data->setFullTimeUpdate();
1828 KStars::Instance()->map()->forceUpdate();
1829 }
1830 allObjects.append(data->skyComposite()->objectLists(SkyObject::SUPERNOVA));
1831 }
1832 break;
1833 case SkyObject::SATELLITE:
1834 {
1835 if (!Options::showSatellites())
1836 {
1837 Options::setShowSatellites(true);
1838 data->setFullTimeUpdate();
1839 KStars::Instance()->map()->forceUpdate();
1840 }
1841 allObjects.append(data->skyComposite()->objectLists(SkyObject::SATELLITE));
1842 }
1843 break;
1844 default:
1845 break;
1846 }
1847
1848 // Sort by magnitude
1849 std::sort(allObjects.begin(), allObjects.end(), [](const auto & a, const auto & b)
1850 {
1851 return a.second->mag() < b.second->mag();
1852 });
1853
1855
1856 // Filter direction, if specified.
1857 if (objectDirection != All)
1858 {
1859 QPair<int, int> Quardent1(270, 360), Quardent2(0, 90), Quardent3(90, 180), Quardent4(180, 270);
1861 switch (objectDirection)
1862 {
1863 case North:
1864 minAZ = Quardent1;
1865 maxAZ = Quardent2;
1866 break;
1867 case East:
1868 minAZ = Quardent2;
1869 maxAZ = Quardent3;
1870 break;
1871 case South:
1872 minAZ = Quardent3;
1873 maxAZ = Quardent4;
1874 break;
1875 case West:
1876 minAZ = Quardent4;
1877 maxAZ = Quardent1;
1878 break;
1879 default:
1880 break;
1881 }
1882
1883 if (isDSO)
1884 {
1885 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin();
1886 while (dsoIterator != dsoObjects.end())
1887 {
1888 // If there a more efficient way to do this?
1889 const double az = (*dsoIterator).recomputeHorizontalCoords(start, geo).az().Degrees();
1890 if (! ((minAZ.first <= az && az <= minAZ.second) || (maxAZ.first <= az && az <= maxAZ.second)))
1892 else
1893 ++dsoIterator;
1894 }
1895 }
1896 else
1897 {
1898 while (objectIterator.hasNext())
1899 {
1900 const auto az = objectIterator.next().second->recomputeHorizontalCoords(start, geo).az().Degrees();
1901 if (! ((minAZ.first <= az && az <= minAZ.second) || (maxAZ.first <= az && az <= maxAZ.second)))
1902 objectIterator.remove();
1903 }
1904 }
1905 }
1906
1907 // Maximum Magnitude
1908 if (!isDSO)
1909 {
1910 objectIterator.toFront();
1911 while (objectIterator.hasNext())
1912 {
1913 auto magnitude = objectIterator.next().second->mag();
1914 // Only filter for objects that have valid magnitude, otherwise, they're automatically included.
1915 if (magnitude != NaN::f && magnitude > objectMaxMagnitude)
1916 objectIterator.remove();
1917 }
1918 }
1919
1920 // Altitude
1921 if (isDSO)
1922 {
1923 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin();
1924 while (dsoIterator != dsoObjects.end())
1925 {
1926 double duration = 0;
1927 for (KStarsDateTime t = start; t < end; t = t.addSecs(3600.0))
1928 {
1929 dms LST = geo->GSTtoLST(t.gst());
1930 (*dsoIterator).EquatorialToHorizontal(&LST, geo->lat());
1931 if ((*dsoIterator).alt().Degrees() >= objectMinAlt)
1932 duration += 3600;
1933 }
1934
1935 if (duration < objectMinDuration)
1937 else
1938 ++dsoIterator;
1939 }
1940 }
1941 else
1942 {
1943 objectIterator.toFront();
1944 while (objectIterator.hasNext())
1945 {
1946 auto oneObject = objectIterator.next().second;
1947 double duration = 0;
1948
1949 for (KStarsDateTime t = start; t < end; t = t.addSecs(3600.0))
1950 {
1951 auto LST = geo->GSTtoLST(t.gst());
1952 const_cast<SkyObject *>(oneObject)->EquatorialToHorizontal(&LST, geo->lat());
1953 if (oneObject->alt().Degrees() >= objectMinAlt)
1954 duration += 3600;
1955 }
1956
1957 if (duration < objectMinDuration)
1958 objectIterator.remove();
1959 }
1960 }
1961
1962 // For DSOs, check minimum required FOV, if any.
1963 if (isDSO && objectMinFOV > 0)
1964 {
1965 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin();
1966 while (dsoIterator != dsoObjects.end())
1967 {
1968 if ((*dsoIterator).a() < objectMinFOV)
1970 else
1971 ++dsoIterator;
1972 }
1973 }
1974
1976 for (auto &oneObject : allObjects)
1977 searchObjects.append(oneObject.second->name());
1978 for (auto &oneObject : dsoObjects)
1979 searchObjects.append(oneObject.name());
1980
1981 searchObjects.removeDuplicates();
1983
1984 sendResponse(commands[ASTRO_SEARCH_OBJECTS], response);
1985 }
1986 else if(command == commands[ASTRO_GET_OBJECT_INFO])
1987 {
1988 const auto name = payload["object"].toString();
1989 bool exact = payload["exact"].toBool(false);
1990 QJsonObject info;
1991 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, exact);
1992 if(oneObject)
1993 {
1994 info =
1995 {
1996 {"name", exact ? name : oneObject->name()},
1997 {"designations", QJsonArray::fromStringList(oneObject->longname().split(", "))},
1998 {"magnitude", oneObject->mag()},
1999 {"ra0", oneObject->ra0().Hours()},
2000 {"de0", oneObject->dec0().Degrees()},
2001 {"ra", oneObject->ra().Hours()},
2002 {"de", oneObject->dec().Degrees()},
2003 {"object", true}
2004 };
2005 sendResponse(commands[ASTRO_GET_OBJECT_INFO], info);
2006 }
2007 else
2008 {
2009 info =
2010 {
2011 {"name", name},
2012 {"object", false},
2013 };
2014 sendResponse(commands[ASTRO_GET_OBJECT_INFO], info );
2015 }
2016
2017 }
2018 // Get a list of object based on criteria
2019 else if (command == commands[ASTRO_GET_OBJECTS_INFO])
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 bool exact = payload["exact"].toBool(false);
2030 QVariantList objectNames = payload["names"].toArray().toVariantList();
2032
2033 for (auto &oneName : objectNames)
2034 {
2035 const QString name = oneName.toString();
2036 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, exact);
2037 if (oneObject)
2038 {
2039 QJsonObject info =
2040 {
2041 {"name", exact ? name : oneObject->name()},
2042 {"designations", QJsonArray::fromStringList(oneObject->longname().split(", "))},
2043 {"magnitude", oneObject->mag()},
2044 {"ra0", oneObject->ra0().Hours()},
2045 {"de0", oneObject->dec0().Degrees()},
2046 {"ra", oneObject->ra().Hours()},
2047 {"de", oneObject->dec().Degrees()},
2048 };
2049
2050 // If DSO, add angular size.
2052 if (dsoObject)
2053 {
2054 info["a"] = dsoObject->a();
2055 info["b"] = dsoObject->b();
2056 info["pa"] = dsoObject->pa();
2057 }
2058
2059 objectsArray.append(info);
2060 }
2061 }
2062
2063 sendResponse(commands[ASTRO_GET_OBJECTS_INFO], objectsArray);
2064 }
2065 // Get a object observability alt/az/ha
2066 else if (command == commands[ASTRO_GET_OBJECTS_OBSERVABILITY])
2067 {
2068 // Set time if required.
2069 if (payload.contains("jd"))
2070 {
2071 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble());
2072 KStarsData::Instance()->clock()->setUTC(jd);
2073 }
2074
2075 // Object Names
2076 QVariantList objectNames = payload["names"].toArray().toVariantList();
2078
2079 bool exact = payload["exact"].toBool(false);
2080 // Data instance
2081 auto *data = KStarsData::Instance();
2082 // Geo Location
2083 auto *geo = KStarsData::Instance()->geo();
2084 // UT
2085 auto ut = data->ut();
2086
2087 for (auto &oneName : objectNames)
2088 {
2089 const QString name = oneName.toString();
2090 SkyObject *oneObject = data->skyComposite()->findByName(name, exact);
2091 if (oneObject)
2092 {
2093 oneObject->EquatorialToHorizontal(data->lst(), geo->lat());
2094 dms ha(data->lst()->Degrees() - oneObject->ra().Degrees());
2095 QJsonObject info =
2096 {
2097 {"name", exact ? name : oneObject->name()},
2098 {"az", oneObject->az().Degrees()},
2099 {"alt", oneObject->alt().Degrees()},
2100 {"ha", ha.Hours()},
2101 };
2102
2103 objectsArray.append(info);
2104 }
2105 }
2106
2107 sendResponse(commands[ASTRO_GET_OBJECTS_OBSERVABILITY], objectsArray);
2108 }
2109 else if (command == commands[ASTRO_GET_OBJECTS_RISESET])
2110 {
2111 // Set time if required.
2112 if (payload.contains("jd"))
2113 {
2114 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble());
2115 KStarsData::Instance()->clock()->setUTC(jd);
2116 }
2117
2118 // Object Names
2119 QVariantList objectNames = payload["names"].toArray().toVariantList();
2121
2122 bool exact = payload["exact"].toBool(false);
2123 // Data instance
2124 auto *data = KStarsData::Instance();
2125 // Geo Location
2126 auto *geo = KStarsData::Instance()->geo();
2127 // UT
2128 QDateTime midnight = QDateTime(data->lt().date(), QTime());
2130
2131 int DayOffset = 0;
2132 if (data->lt().time().hour() > 12)
2133 DayOffset = 1;
2134
2135 for (auto &oneName : objectNames)
2136 {
2137 const QString name = oneName.toString();
2138 SkyObject *oneObject = data->skyComposite()->findByName(name, exact);
2139 if (oneObject)
2140 {
2141 QJsonObject info;
2142 //Prepare time/position variables
2143 //true = use rise time
2144 QTime riseTime = oneObject->riseSetTime(ut, geo, true);
2145
2146 //If transit time is before rise time, use transit time for tomorrow
2147 QTime transitTime = oneObject->transitTime(ut, geo);
2148 if (transitTime < riseTime)
2149 transitTime = oneObject->transitTime(ut.addDays(1), geo);
2150
2151 //If set time is before rise time, use set time for tomorrow
2152 //false = use set time
2153 QTime setTime = oneObject->riseSetTime(ut, geo, false);
2154 //false = use set time
2155 if (setTime < riseTime)
2156 setTime = oneObject->riseSetTime(ut.addDays(1), geo, false);
2157
2158 info["name"] = exact ? name : oneObject->name();
2159 if (riseTime.isValid())
2160 {
2161 info["rise"] = QString::asprintf("%02d:%02d", riseTime.hour(), riseTime.minute());
2162 info["set"] = QString::asprintf("%02d:%02d", setTime.hour(), setTime.minute());
2163 }
2164 else
2165 {
2166 if (oneObject->alt().Degrees() > 0.0)
2167 {
2168 info["rise"] = "Circumpolar";
2169 info["set"] = "Circumpolar";
2170 }
2171 else
2172 {
2173 info["rise"] = "Never rises";
2174 info["set"] = "Never rises";
2175 }
2176 }
2177
2178 info["transit"] = QString::asprintf("%02d:%02d", transitTime.hour(), transitTime.minute());
2179
2181 for (double h = -12.0; h <= 12.0; h += 0.5)
2182 {
2183 double hour = h + (24.0 * DayOffset);
2184 KStarsDateTime offset = ut.addSecs(hour * 3600.0);
2185 CachingDms LST = geo->GSTtoLST(offset.gst());
2186 oneObject->EquatorialToHorizontal(&LST, geo->lat());
2187 altitudes.append(oneObject->alt().Degrees());
2188 }
2189
2190 info["altitudes"] = altitudes;
2191
2192 objectsArray.append(info);
2193 }
2194 }
2195
2196 sendResponse(commands[ASTRO_GET_OBJECTS_RISESET], objectsArray);
2197 }
2198}
2199
2200///////////////////////////////////////////////////////////////////////////////////////////
2201///
2202///////////////////////////////////////////////////////////////////////////////////////////
2203KStarsDateTime Message::getNextDawn()
2204{
2205 // Today's date
2206 const KStarsDateTime localTime = KStarsData::Instance()->lt();
2207 // Local Midnight
2209 // Almanac
2210 KSAlmanac almanac(midnight, KStarsData::Instance()->geo());
2211 // Next Dawn
2212 KStarsDateTime nextDawn = midnight.addSecs(almanac.getDawnAstronomicalTwilight() * 24.0 * 3600.0);
2213 // If dawn is earliar than now, add a day
2214 if (nextDawn < localTime)
2215 nextDawn.addDays(1);
2216
2217 return nextDawn;
2218}
2219
2220///////////////////////////////////////////////////////////////////////////////////////////
2221///
2222///////////////////////////////////////////////////////////////////////////////////////////
2223void Message::requestDSLRInfo(const QString &cameraName)
2224{
2225 sendResponse(commands[DSLR_GET_INFO], cameraName);
2226}
2227
2228///////////////////////////////////////////////////////////////////////////////////////////
2229///
2230///////////////////////////////////////////////////////////////////////////////////////////
2231void Message::requestPortSelection(bool show)
2232{
2233 sendResponse(commands[GET_PROFILE_PORT_SELECTION], show);
2234}
2235
2236///////////////////////////////////////////////////////////////////////////////////////////
2237///
2238///////////////////////////////////////////////////////////////////////////////////////////
2239void Message::sendDialog(const QJsonObject &message)
2240{
2241 sendResponse(commands[DIALOG_GET_INFO], message);
2242}
2243
2244///////////////////////////////////////////////////////////////////////////////////////////
2245///
2246///////////////////////////////////////////////////////////////////////////////////////////
2247void Message::sendResponse(const QString &command, const QJsonObject &payload)
2248{
2249 for (auto &nodeManager : m_NodeManagers)
2250 {
2251 nodeManager->message()->sendResponse(command, payload);
2252 }
2253}
2254
2255///////////////////////////////////////////////////////////////////////////////////////////
2256///
2257///////////////////////////////////////////////////////////////////////////////////////////
2258void Message::sendResponse(const QString &command, const QJsonArray &payload)
2259{
2260 for (auto &nodeManager : m_NodeManagers)
2261 {
2262 nodeManager->message()->sendResponse(command, payload);
2263 }
2264}
2265
2266///////////////////////////////////////////////////////////////////////////////////////////
2267///
2268///////////////////////////////////////////////////////////////////////////////////////////
2269void Message::sendResponse(const QString &command, const QString &payload)
2270{
2271 for (auto &nodeManager : m_NodeManagers)
2272 {
2273 nodeManager->message()->sendResponse(command, payload);
2274 }
2275}
2276
2277///////////////////////////////////////////////////////////////////////////////////////////
2278///
2279///////////////////////////////////////////////////////////////////////////////////////////
2280void Message::sendResponse(const QString &command, bool payload)
2281{
2282 for (auto &nodeManager : m_NodeManagers)
2283 {
2284 nodeManager->message()->sendResponse(command, payload);
2285 }
2286}
2287
2288///////////////////////////////////////////////////////////////////////////////////////////
2289///
2290///////////////////////////////////////////////////////////////////////////////////////////
2291void Message::autofocusAborted()
2292{
2294 {
2295 {"status", "Aborted"}
2296 };
2297 sendResponse(commands[NEW_FOCUS_STATE], cStatus);
2298}
2299
2300///////////////////////////////////////////////////////////////////////////////////////////
2301///
2302///////////////////////////////////////////////////////////////////////////////////////////
2303void Message::updateMountStatus(const QJsonObject &status, bool throttle)
2304{
2305 if (throttle)
2306 {
2308 if (m_ThrottleTS.msecsTo(now) >= THROTTLE_INTERVAL)
2309 {
2310 m_ThrottleTS = now;
2311 sendResponse(commands[NEW_MOUNT_STATE], status);
2312 }
2313 }
2314 else
2315 sendResponse(commands[NEW_MOUNT_STATE], status);
2316}
2317
2318///////////////////////////////////////////////////////////////////////////////////////////
2319///
2320///////////////////////////////////////////////////////////////////////////////////////////
2321void Message::updateCaptureStatus(const QJsonObject &status)
2322{
2323 sendResponse(commands[NEW_CAPTURE_STATE], status);
2324}
2325
2326///////////////////////////////////////////////////////////////////////////////////////////
2327///
2328///////////////////////////////////////////////////////////////////////////////////////////
2329void Message::updateFocusStatus(const QJsonObject &status)
2330{
2331 sendResponse(commands[NEW_FOCUS_STATE], status);
2332}
2333
2334///////////////////////////////////////////////////////////////////////////////////////////
2335///
2336///////////////////////////////////////////////////////////////////////////////////////////
2337void Message::updateGuideStatus(const QJsonObject &status)
2338{
2339 sendResponse(commands[NEW_GUIDE_STATE], status);
2340}
2341
2342///////////////////////////////////////////////////////////////////////////////////////////
2343///
2344///////////////////////////////////////////////////////////////////////////////////////////
2345void Message::updateDomeStatus(const QJsonObject &status)
2346{
2347 sendResponse(commands[NEW_DOME_STATE], status);
2348}
2349
2350///////////////////////////////////////////////////////////////////////////////////////////
2351///
2352///////////////////////////////////////////////////////////////////////////////////////////
2353void Message::updateCapStatus(const QJsonObject &status)
2354{
2355 sendResponse(commands[NEW_CAP_STATE], status);
2356}
2357
2358///////////////////////////////////////////////////////////////////////////////////////////
2359///
2360///////////////////////////////////////////////////////////////////////////////////////////
2361void Message::updateAlignStatus(const QJsonObject &status)
2362{
2363 sendResponse(commands[NEW_ALIGN_STATE], status);
2364}
2365
2366///////////////////////////////////////////////////////////////////////////////////////////
2367///
2368///////////////////////////////////////////////////////////////////////////////////////////
2369void Message::sendConnection()
2370{
2372 {
2373 {"connected", true},
2374 {"online", m_Manager->getEkosStartingStatus() == Ekos::Success}
2375 };
2376
2377 sendResponse(commands[NEW_CONNECTION_STATE], connectionState);
2378}
2379
2380///////////////////////////////////////////////////////////////////////////////////////////
2381///
2382///////////////////////////////////////////////////////////////////////////////////////////
2383void Message::sendStates()
2384{
2385 // Send capture sequence if one exists
2386 if (m_Manager->captureModule())
2387 {
2388 QJsonObject captureState = {{ "status", getCaptureStatusString(m_Manager->captureModule()->status(), false)}};
2389 sendResponse(commands[NEW_CAPTURE_STATE], captureState);
2390 sendCaptureSequence(m_Manager->captureModule()->getSequence());
2391 }
2392
2393 if (m_Manager->mountModule())
2394 {
2395 QJsonObject mountState =
2396 {
2397 {"status", m_Manager->mountModule()->statusString(false)},
2398 {"target", m_Manager->capturePreview->mountTarget->text()},
2399 {"slewRate", m_Manager->mountModule()->slewRate()},
2400 {"pierSide", m_Manager->mountModule()->pierSide()}
2401 };
2402
2403 sendResponse(commands[NEW_MOUNT_STATE], mountState);
2404 }
2405
2406 if (m_Manager->focusModule())
2407 {
2408 QJsonObject focusState = {{ "status", getFocusStatusString(m_Manager->focusModule()->status(), false)}};
2409 sendResponse(commands[NEW_FOCUS_STATE], focusState);
2410 }
2411
2412 if (m_Manager->guideModule())
2413 {
2414 QJsonObject guideState = {{ "status", getGuideStatusString(m_Manager->guideModule()->status(), false)}};
2415 sendResponse(commands[NEW_GUIDE_STATE], guideState);
2416 }
2417
2418 if (m_Manager->alignModule())
2419 {
2420 // Align State
2421 QJsonObject alignState =
2422 {
2423 {"status", getAlignStatusString(m_Manager->alignModule()->status(), false)}
2424 };
2425 sendResponse(commands[NEW_ALIGN_STATE], alignState);
2426
2427 // Align settings
2428 sendAlignSettings(m_Manager->alignModule()->getAllSettings());
2429
2430 Ekos::PolarAlignmentAssistant *paa = m_Manager->alignModule()->polarAlignmentAssistant();
2431 if (paa)
2432 {
2433 // Polar State
2434 QTextDocument doc;
2435 doc.setHtml(paa->getPAHMessage());
2437 {
2438 {"stage", paa->getPAHStageString(false)},
2439 {"enabled", paa->isEnabled()},
2440 {"message", doc.toPlainText()},
2441 };
2442 sendResponse(commands[NEW_POLAR_STATE], polarState);
2443 }
2444 }
2445}
2446
2447///////////////////////////////////////////////////////////////////////////////////////////
2448///
2449///////////////////////////////////////////////////////////////////////////////////////////
2450void Message::sendEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event)
2451{
2452 if (Options::ekosLiveNotifications() == false)
2453 return;
2454
2456 {
2457 {"source", source},
2458 {"severity", event},
2459 {"message", message},
2460 {"uuid", QUuid::createUuid().toString()}
2461 };
2462
2463 sendResponse(commands[NEW_NOTIFICATION], newEvent);
2464}
2465
2466///////////////////////////////////////////////////////////////////////////////////////////
2467///
2468///////////////////////////////////////////////////////////////////////////////////////////
2469void Message::sendManualRotatorStatus(double currentPA, double targetPA, double threshold)
2470{
2471 QJsonObject request = {{ "currentPA", currentPA}, {"targetPA", targetPA}, {"threshold", threshold}};
2472 sendResponse(commands[ALIGN_MANUAL_ROTATOR_STATUS], request);
2473}
2474
2475///////////////////////////////////////////////////////////////////////////////////////////
2476///
2477///////////////////////////////////////////////////////////////////////////////////////////
2478void Message::setBoundingRect(QRect rect, QSize view, double currentZoom)
2479{
2480 m_BoundingRect = rect;
2481 m_ViewSize = view;
2482 m_CurrentZoom = currentZoom;
2483}
2484
2485///////////////////////////////////////////////////////////////////////////////////////////
2486///
2487///////////////////////////////////////////////////////////////////////////////////////////
2488void Message::processDialogResponse(const QJsonObject &payload)
2489{
2490 KSMessageBox::Instance()->selectResponse(payload["button"].toString());
2491}
2492
2493///////////////////////////////////////////////////////////////////////////////////////////
2494///
2495///////////////////////////////////////////////////////////////////////////////////////////
2496void Message::processNewProperty(INDI::Property prop)
2497{
2498 // Do not send new properties until all properties settle down
2499 // then send any properties that appears afterwards since the initial bunch
2500 // would cause a heavy message congestion.
2501 if (m_Manager->settleStatus() != Ekos::CommunicationStatus::Success)
2502 return;
2503
2505 ISD::propertyToJson(prop, propObject, false);
2506 sendResponse(commands[DEVICE_PROPERTY_ADD], propObject);
2507}
2508
2509///////////////////////////////////////////////////////////////////////////////////////////
2510///
2511///////////////////////////////////////////////////////////////////////////////////////////
2512void Message::processDeleteProperty(INDI::Property prop)
2513{
2514 QJsonObject payload =
2515 {
2516 {"device", prop.getDeviceName()},
2517 {"name", prop.getName()}
2518 };
2519
2520 sendResponse(commands[DEVICE_PROPERTY_REMOVE], payload);
2521}
2522
2523///////////////////////////////////////////////////////////////////////////////////////////
2524///
2525///////////////////////////////////////////////////////////////////////////////////////////
2526void Message::processMessage(const QSharedPointer<ISD::GenericDevice> &device, int id)
2527{
2528 if (Options::ekosLiveNotifications() == false)
2529 return;
2530
2531 auto message = QString::fromStdString(device->getBaseDevice().messageQueue(id));
2532 QJsonObject payload =
2533 {
2534 {"device", device->getDeviceName()},
2535 {"message", message}
2536 };
2537
2538 sendResponse(commands[DEVICE_MESSAGE], payload);
2539}
2540
2541///////////////////////////////////////////////////////////////////////////////////////////
2542///
2543///////////////////////////////////////////////////////////////////////////////////////////
2544void Message::processUpdateProperty(INDI::Property prop)
2545{
2546 if (m_PropertySubscriptions.contains(prop.getDeviceName()))
2547 {
2548 QSet<QString> subProps = m_PropertySubscriptions[prop.getDeviceName()];
2549 if (subProps.contains(prop.getName()))
2550 {
2551 m_PendingProperties.remove(prop);
2552 m_PendingProperties.insert(prop);
2553 }
2554 }
2555}
2556
2557///////////////////////////////////////////////////////////////////////////////////////////
2558///
2559///////////////////////////////////////////////////////////////////////////////////////////
2560void Message::setPendingPropertiesEnabled(bool enabled)
2561{
2562 if (enabled)
2563 m_PendingPropertiesTimer.start();
2564 else
2565 {
2566 m_PendingPropertiesTimer.stop();
2567 m_PendingProperties.clear();
2568 }
2569}
2570
2571///////////////////////////////////////////////////////////////////////////////////////////
2572///
2573///////////////////////////////////////////////////////////////////////////////////////////
2574void Message::sendPendingProperties()
2575{
2576 for (auto &prop : m_PendingProperties)
2577 {
2578 if (prop->isValid())
2579 {
2581 ISD::propertyToJson(*prop, propObject);
2582 sendResponse(commands[DEVICE_PROPERTY_GET], propObject);
2583 }
2584 }
2585
2586 m_PendingProperties.clear();
2587}
2588
2589///////////////////////////////////////////////////////////////////////////////////////////
2590///
2591///////////////////////////////////////////////////////////////////////////////////////////
2592void Message::sendModuleState(const QString &name)
2593{
2594 if (name == "Capture")
2595 {
2596 QJsonObject captureState = {{ "status", getCaptureStatusString(m_Manager->captureModule()->status(), false)}};
2597 sendResponse(commands[NEW_CAPTURE_STATE], captureState);
2598 sendCaptureSequence(m_Manager->captureModule()->getSequence());
2599 }
2600 else if (name == "Mount")
2601 {
2602 QJsonObject mountState =
2603 {
2604 {"status", m_Manager->mountStatus->getStatusText()},
2605 {"target", m_Manager->capturePreview->mountTarget->text()},
2606 {"slewRate", m_Manager->mountModule()->slewRate()},
2607 {"pierSide", m_Manager->mountModule()->pierSide()}
2608 };
2609
2610 sendResponse(commands[NEW_MOUNT_STATE], mountState);
2611 }
2612 else if (name == "Focus")
2613 {
2614 QJsonObject focusState = {{ "status", getFocusStatusString(m_Manager->focusModule()->status(), false)}};
2615 sendResponse(commands[NEW_FOCUS_STATE], focusState);
2616 }
2617 else if (name == "Guide")
2618 {
2619 QJsonObject guideState = {{ "status", getGuideStatusString(m_Manager->guideModule()->status(), false)}};
2620 sendResponse(commands[NEW_GUIDE_STATE], guideState);
2621 }
2622 else if (name == "Align")
2623 {
2624 // Align State
2625 QJsonObject alignState =
2626 {
2627 {"status", getAlignStatusString(m_Manager->alignModule()->status(), false)}
2628 };
2629 sendResponse(commands[NEW_ALIGN_STATE], alignState);
2630
2631 // Align settings
2632 sendAlignSettings(m_Manager->alignModule()->getAllSettings());
2633
2634 Ekos::PolarAlignmentAssistant *paa = m_Manager->alignModule()->polarAlignmentAssistant();
2635 if (paa)
2636 {
2637 // Polar State
2638 QTextDocument doc;
2639 doc.setHtml(paa->getPAHMessage());
2641 {
2642 {"stage", paa->getPAHStageString(false)},
2643 {"enabled", paa->isEnabled()},
2644 {"message", doc.toPlainText()},
2645 };
2646 sendResponse(commands[NEW_POLAR_STATE], polarState);
2647 }
2648 }
2649}
2650
2651///////////////////////////////////////////////////////////////////////////////////////////
2652///
2653///////////////////////////////////////////////////////////////////////////////////////////
2654QObject *Message::findObject(const QString &name)
2655{
2656 QObject *object {nullptr};
2657 // Check for manager itself
2658 if (name == "Manager")
2659 return m_Manager;
2660 // Try Manager first
2661 object = m_Manager->findChild<QObject *>(name);
2662 if (object)
2663 return object;
2664 // Then INDI Listener
2665 object = INDIListener::Instance()->findChild<QObject *>(name);
2666 if (object)
2667 return object;
2668 // FITS Viewer. Search for any matching imageData
2669 // TODO Migrate to DBus
2670 for (auto &viewer : KStars::Instance()->getFITSViewers())
2671 {
2672 for (auto &tab : viewer->tabs())
2673 {
2674 if (tab->getView()->objectName() == name)
2675 return tab->getView().get();
2676 }
2677 }
2678
2679 // Finally KStars
2680 // N.B. This does not include indepdent objects with their parent set to null (e.g. FITSViewer)
2681 object = KStars::Instance()->findChild<QObject *>(name);
2682 return object;
2683}
2684
2685///////////////////////////////////////////////////////////////////////////////////////////
2686///
2687///////////////////////////////////////////////////////////////////////////////////////////
2688bool Message::parseArgument(QVariant::Type type, const QVariant &arg, QGenericArgument &genericArg, SimpleTypes &types)
2689{
2691
2692 switch (type)
2693 {
2694 case QVariant::Type::Int:
2695 types.number_integer = arg.toInt();
2696 genericArg = Q_ARG(int, types.number_integer);
2697 return true;
2698 case QVariant::Type::UInt:
2699 types.number_unsigned_integer = arg.toUInt();
2700 genericArg = Q_ARG(uint, types.number_unsigned_integer);
2701 return true;
2702 case QVariant::Type::LongLong:
2703 types.number_integer = arg.toLongLong();
2704 genericArg = Q_ARG(int, types.number_integer);
2705 return true;
2706 case QVariant::Type::ULongLong:
2707 types.number_unsigned_integer = arg.toULongLong();
2708 genericArg = Q_ARG(uint, types.number_unsigned_integer);
2709 return true;
2710 case QVariant::Type::Double:
2711 types.number_double = arg.toDouble();
2712 genericArg = Q_ARG(double, types.number_double);
2713 return true;
2714 case QVariant::Type::Bool:
2715 types.boolean = arg.toBool();
2716 genericArg = Q_ARG(bool, types.boolean);
2717 return true;
2718 case QVariant::Type::String:
2719 types.text = arg.toString();
2720 genericArg = Q_ARG(QString, types.text);
2721 return true;
2722 case QVariant::Type::Url:
2723 types.url = arg.toUrl();
2724 genericArg = Q_ARG(QUrl, types.url);
2725 return true;
2726 default:
2727 break;
2728 }
2729
2730 return false;
2731}
2732
2733///////////////////////////////////////////////////////////////////////////////////////////
2734///
2735///////////////////////////////////////////////////////////////////////////////////////////
2736void Message::invokeMethod(QObject *context, const QJsonObject &payload)
2737{
2740
2741 auto name = payload["name"].toString().toLatin1();
2742
2743 if (payload.contains("args"))
2744 {
2745 QJsonArray args = payload["args"].toArray();
2746
2747 for (auto oneArg : args)
2748 {
2749 auto argObject = oneArg.toObject();
2751 SimpleTypes genericType;
2752 argsList.append(genericArgument);
2753 typesList.append(genericType);
2754 if (parseArgument(static_cast<QVariant::Type>(argObject["type"].toInt()), argObject["value"].toVariant(), argsList.back(),
2755 typesList.last()) == false)
2756 {
2757 argsList.pop_back();
2758 typesList.pop_back();
2759 }
2760 }
2761
2762 switch (argsList.size())
2763 {
2764 case 1:
2765 QMetaObject::invokeMethod(context, name, argsList[0]);
2766 break;
2767 case 2:
2768 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1]);
2769 break;
2770 case 3:
2771 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1], argsList[2]);
2772 break;
2773 case 4:
2774 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1], argsList[2], argsList[3]);
2775 break;
2776 default:
2777 break;
2778 }
2779 }
2780 else
2781 {
2782 QMetaObject::invokeMethod(context, name);
2783 }
2784}
2785
2786}
a dms subclass that caches its sine and cosine values every time the angle is changed.
Definition cachingdms.h:19
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
Align class handles plate-solving and polar alignment measurement and correction using astrometry....
Definition align.h:75
bool loadAndSlew(const QByteArray &image, const QString &extension)
DBUS interface function.
Definition align.cpp:3072
Captures single or sequence of images from a CCD.
Definition capture.h:92
bool setVideoLimits(uint16_t maxBufferSize, uint16_t maxPreviewFPS)
setVideoLimits sets the buffer size and max preview fps for live preview
Definition capture.cpp:309
const QJsonArray & getSequence() const
getSequence Return the JSON representation of the current sequeue queue
Definition capture.h:422
Supports manual focusing and auto focusing using relative and absolute INDI focusers.
Definition focus.h:51
void startFraming()
startFraming Begins continuous capture of the CCD and calculates HFR every frame.
Definition focus.cpp:4394
void selectFocusStarFraction(double x, double y)
selectFocusStarFraction Select the focus star based by fraction of the overall size.
Definition focus.cpp:4584
Q_SCRIPTABLE Q_NOREPLY void resetFrame()
DBUS interface function.
Definition focus.cpp:315
Performs calibration and autoguiding using an ST4 port or directly via the INDI driver.
Definition guide.h:51
Q_SCRIPTABLE bool capture()
DBUS interface function.
Definition guide.cpp:769
Q_SCRIPTABLE Q_NOREPLY void clearCalibration()
DBUS interface function.
Definition guide.cpp:1567
Q_SCRIPTABLE bool abort()
DBUS interface function.
Definition guide.cpp:889
Q_SCRIPTABLE Q_NOREPLY void loop()
DBUS interface function.
Definition guide.cpp:3023
Q_SCRIPTABLE bool guide()
DBUS interface function.
Definition guide.cpp:1329
Supports controlling INDI telescope devices including setting/retrieving mount properties,...
Definition mount.h:33
The PolarAlignmentAssistant class.
The Ekos scheduler is a simple scheduler class to orchestrate automated multi object observation jobs...
Definition scheduler.h:51
INDIListener is responsible for creating ISD::GDInterface generic devices as new devices arrive from ...
Camera class controls an INDI Camera device.
Definition indicamera.h:44
A class that implements methods to find sun rise, sun set, twilight begin / end times,...
Definition ksalmanac.h:27
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
KStarsDateTime addDays(int nd) const
Modify the Date/Time by adding a number of days.
KStarsDateTime addSecs(double s) const
static KStarsDateTime currentDateTimeUtc()
This is the main window for KStars.
Definition kstars.h:91
static KStars * Instance()
Definition kstars.h:123
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
TYPE
The type classification of the SkyObject.
Definition skyobject.h:112
The sky coordinates of a point in the sky.
Definition skypoint.h:45
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
Q_SCRIPTABLE bool captureAndSolve(bool initialCall=true)
DBUS interface function.
Definition align.cpp:1412
Q_SCRIPTABLE Q_NOREPLY void abort()
DBUS interface function.
Definition align.h:400
Q_SCRIPTABLE Q_NOREPLY void toggleVideo(bool enabled)
DBUS interface function.
Definition capture.h:521
Q_SCRIPTABLE Q_NOREPLY void stop(CaptureState targetState=CAPTURE_IDLE)
DBUS interface function.
Definition capture.h:473
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS interface function.
Definition capture.h:459
Q_SCRIPTABLE bool saveSequenceQueue(const QString &path)
DBUS interface function.
Definition capture.h:164
Q_SCRIPTABLE bool loadSequenceQueue(const QString &fileURL, QString targetName="")
DBUS interface function.
Definition capture.h:155
Q_SCRIPTABLE Q_NOREPLY void clearSequenceQueue()
DBUS interface function.
Definition capture.h:136
Q_SCRIPTABLE Q_NOREPLY void capture(double settleTime=0.0)
DBUS interface function.
Definition focus.cpp:1521
Q_SCRIPTABLE bool focusOut(int ms=-1)
DBUS interface function.
Definition focus.cpp:1715
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS interface function.
Definition focus.cpp:1012
Q_SCRIPTABLE Q_NOREPLY void abort()
DBUS interface function.
Definition focus.cpp:1438
Q_SCRIPTABLE bool focusIn(int ms=-1)
DBUS interface function.
Definition focus.cpp:1695
QString i18n(const char *text, const TYPE &arg...)
bool insert(Part *part, qint64 *insertId=nullptr)
bool remove(const QString &column, const QVariant &value)
char * toString(const EngineQuery &query)
Generic record interfaces and implementations.
Definition cloud.cpp:23
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:79
AlignState
Definition ekos.h:145
KDB_EXPORT void getProperties(const KDbLookupFieldSchema *lookup, QMap< QByteArray, QVariant > *values)
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
GeoCoordinates geo(const QVariant &location)
QVariant location(const QVariant &res)
QString path(const QString &relativePath)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString name(StandardAction id)
KGuiItem properties()
const QList< QKeySequence > & end()
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QCA_EXPORT void setProperty(const QString &name, const QVariant &value)
void trigger()
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
QDateTime currentDateTime()
QChar separator()
QString tempPath()
virtual void close() override
qint64 write(const QByteArray &data)
void append(const QJsonValue &value)
QJsonArray fromStringList(const QStringList &list)
bool isEmpty() const const
QVariantList toVariantList() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool contains(QLatin1StringView key) const const
QJsonObject fromVariantMap(const QVariantMap &map)
iterator insert(QLatin1StringView key, const QJsonValue &value)
QVariantMap toVariantMap() const const
qreal angle() const const
qreal length() const const
QPointF p1() const const
QPointF p2() const const
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
T findChild(const QString &name, Qt::FindChildOptions options) const const
QString arg(Args &&... args) const const
QString asprintf(const char *cformat,...)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
CaseInsensitive
LocalTime
QTextStream & center(QTextStream &stream)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
virtual QString fileName() const const override
void setAutoRemove(bool b)
void setHtml(const QString &html)
QString toPlainText() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
int hour() const const
int minute() const const
void timeout()
QUrl fromLocalFile(const QString &localFile)
QString url(FormattingOptions options) const const
QUuid createUuid()
QString toString(StringFormat mode) const const
bool toBool() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
qlonglong toLongLong(bool *ok) const const
QString toString() const const
uint toUInt(bool *ok) const const
qulonglong toULongLong(bool *ok) const const
QUrl toUrl() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:48:25 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.