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
292 QJsonArray deviceList;
293
294 for(auto &gd : INDIListener::devices())
295 {
296 QJsonObject oneDevice =
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
318 QJsonArray trains;
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
354 QList<OAL::Scope *> allScopes;
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{
368 QJsonArray dslrList;
369
370 QList<OAL::DSLRLens *> allDslrLens;
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->mainCamera()->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->mainCamera()->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->mainCamera()->createJob();
436 }
437 else if (command == commands[CAPTURE_REMOVE_SEQUENCE])
438 {
439 if (capture->mainCamera()->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);
471 QJsonObject response =
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->mainCamera()->getAllSettings());
482 }
483 else if (command == commands[CAPTURE_SET_ALL_SETTINGS])
484 {
485 auto settings = payload.toVariantMap();
486 capture->mainCamera()->setAllSettings(settings);
487 KSUtils::setGlobalSettings(settings);
488 }
489 else if (command == commands[CAPTURE_GENERATE_DARK_FLATS])
490 {
491 capture->mainCamera()->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 auto ra = dms::fromString(payload["ra"].toString(), false);
714 auto de = dms::fromString(payload["de"].toString(), true);
715 mount->sync(ra.Hours(), de.Degrees());
716 }
717 else if (command == commands[MOUNT_SYNC_TARGET])
718 {
719 mount->syncTarget(payload["target"].toString());
720 }
721 else if (command == commands[MOUNT_GOTO_RADE])
722 {
723 mount->setJ2000Enabled(payload["isJ2000"].toBool());
724 auto ra = dms::fromString(payload["ra"].toString(), false);
725 auto de = dms::fromString(payload["de"].toString(), true);
726 mount->slew(ra.Hours(), de.Degrees());
727 }
728 else if (command == commands[MOUNT_GOTO_TARGET])
729 {
730 mount->gotoTarget(ki18n(payload["target"].toString().toLatin1()).toString());
731 }
732 else if (command == commands[MOUNT_SET_SLEW_RATE])
733 {
734 int rate = payload["rate"].toInt(-1);
735 if (rate >= 0)
736 mount->setSlewRate(rate);
737 }
738 else if (command == commands[MOUNT_SET_ALL_SETTINGS])
739 {
740 auto settings = payload.toVariantMap();
741 mount->setAllSettings(settings);
742 KSUtils::setGlobalSettings(settings);
743 }
744 else if (command == commands[MOUNT_GET_ALL_SETTINGS])
745 sendMountSettings(mount->getAllSettings());
746 else if (command == commands[MOUNT_SET_MOTION])
747 {
748 QString direction = payload["direction"].toString();
749 ISD::Mount::MotionCommand action = payload["action"].toBool(false) ?
750 ISD::Mount::MOTION_START : ISD::Mount::MOTION_STOP;
751
752 if (direction == "N")
753 mount->motionCommand(action, ISD::Mount::MOTION_NORTH, -1);
754 else if (direction == "S")
755 mount->motionCommand(action, ISD::Mount::MOTION_SOUTH, -1);
756 else if (direction == "E")
757 mount->motionCommand(action, -1, ISD::Mount::MOTION_EAST);
758 else if (direction == "W")
759 mount->motionCommand(action, -1, ISD::Mount::MOTION_WEST);
760 }
761 else if (command == commands[MOUNT_GOTO_PIXEL])
762 {
763 const auto name = payload["camera"].toString();
764 const auto xFactor = payload["x"].toDouble();
765 const auto yFactor = payload["y"].toDouble();
766
767 for(auto &oneDevice : INDIListener::devices())
768 {
769 auto camera = oneDevice->getCamera();
770 if (!camera || camera->getDeviceName() != name)
771 continue;
772
773 auto primaryChip = camera->getChip(ISD::CameraChip::PRIMARY_CCD);
774
775 if (!primaryChip)
776 break;
777
778 auto imageData = primaryChip->getImageData();
779 if (!imageData || imageData->hasWCS() == false)
780 break;
781
782 auto x = xFactor * imageData->width();
783 auto y = yFactor * imageData->height();
784
785 QPointF point(x, y);
786 SkyPoint coord;
787 if (imageData->pixelToWCS(point, coord))
788 {
789 // J2000 -> JNow
790 coord.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd());
791 mount->gotoTarget(coord);
792 break;
793 }
794 }
795 }
796 else if (command == commands[MOUNT_TOGGLE_AUTOPARK])
797 mount->setAutoParkEnabled(payload["toggled"].toBool());
798}
799
800///////////////////////////////////////////////////////////////////////////////////////////
801///
802///////////////////////////////////////////////////////////////////////////////////////////
803void Message::processAlignCommands(const QString &command, const QJsonObject &payload)
804{
805 Ekos::Align *align = m_Manager->alignModule();
806
807 if (align == nullptr)
808 {
809 qCWarning(KSTARS_EKOS) << "Ignoring command" << command << "as align module is not available";
810 return;
811 }
812
813 if (command == commands[ALIGN_SOLVE])
814 {
815 align->captureAndSolve();
816 }
817 else if (command == commands[ALIGN_SET_ALL_SETTINGS])
818 {
819 auto settings = payload.toVariantMap();
820 align->setAllSettings(settings);
821 KSUtils::setGlobalSettings(settings);
822 }
823 else if (command == commands[ALIGN_GET_ALL_SETTINGS])
824 sendAlignSettings(align->getAllSettings());
825 else if(command == commands[ALIGN_SET_ASTROMETRY_SETTINGS])
826 {
827 Options::setAstrometryRotatorThreshold(payload["threshold"].toInt());
828 Options::setAstrometryUseRotator(payload["rotator_control"].toBool());
829 Options::setAstrometryUseImageScale(payload["scale"].toBool());
830 Options::setAstrometryUsePosition(payload["position"].toBool());
831 }
832 else if (command == commands[ALIGN_STOP])
833 align->abort();
834 else if (command == commands[ALIGN_LOAD_AND_SLEW])
835 {
836 // Check if we have filename payload first
837 if (payload.contains("filename"))
838 {
839 align->loadAndSlew(payload["filename"].toString());
840 }
841 else
842 {
843 QString filename = QDir::tempPath() + QDir::separator() +
844 QString("XXXXXXloadslew.%1").arg(payload["ext"].toString("fits"));
845 QTemporaryFile file(filename);
846 file.setAutoRemove(false);
847 file.open();
848 file.write(QByteArray::fromBase64(payload["data"].toString().toLatin1()));
849 file.close();
850 align->loadAndSlew(file.fileName());
851 }
852 }
853 else if (command == commands[ALIGN_MANUAL_ROTATOR_TOGGLE])
854 {
855 align->toggleManualRotator(payload["toggled"].toBool());
856 }
857}
858
859///////////////////////////////////////////////////////////////////////////////////////////
860///
861///////////////////////////////////////////////////////////////////////////////////////////
862void Message::setAlignStatus(Ekos::AlignState newState)
863{
864 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
865 return;
866
867 QJsonObject alignState =
868 {
869 {"status", QString::fromLatin1(Ekos::alignStates[newState].untranslatedText())}
870 };
871
872 sendResponse(commands[NEW_ALIGN_STATE], alignState);
873}
874
875///////////////////////////////////////////////////////////////////////////////////////////
876///
877///////////////////////////////////////////////////////////////////////////////////////////
878void Message::setAlignSolution(const QVariantMap &solution)
879{
880 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
881 return;
882
883 QJsonObject alignState =
884 {
885 {"solution", QJsonObject::fromVariantMap(solution)},
886 };
887
888 sendResponse(commands[NEW_ALIGN_STATE], alignState);
889}
890
891///////////////////////////////////////////////////////////////////////////////////////////
892///
893///////////////////////////////////////////////////////////////////////////////////////////
894void Message::processSchedulerCommands(const QString &command, const QJsonObject &payload)
895{
896 Ekos::Scheduler *scheduler = m_Manager->schedulerModule();
897
898 if (command == commands[SCHEDULER_GET_JOBS])
899 {
900 sendSchedulerJobs();
901 }
902 else if (command == commands[SCHEDULER_ADD_JOBS])
903 {
904 scheduler->addJob();
905 }
906 else if(command == commands[SCHEDULER_REMOVE_JOBS])
907 {
908 int index = payload["index"].toInt();
909 scheduler->removeOneJob(index);
910 }
911 else if(command == commands[SCHEDULER_GET_ALL_SETTINGS])
912 {
913 sendSchedulerSettings(scheduler->getAllSettings());
914 }
915 else if(command == commands[SCHEDULER_SET_ALL_SETTINGS])
916 {
917 auto settings = payload.toVariantMap();
918 scheduler->setAllSettings(settings);
919 KSUtils::setGlobalSettings(settings);
920 }
921 else if (command == commands[SCHEDULER_SAVE_FILE])
922 {
923 if (scheduler->saveFile(QUrl::fromLocalFile(payload["filepath"].toString())))
924 sendResponse(commands[SCHEDULER_SAVE_FILE], QString::fromUtf8(QFile(payload["filepath"].toString()).readAll()));
925 }
926 else if (command == commands[SCHEDULER_LOAD_FILE])
927 {
929 if (payload.contains("filedata"))
930 {
931 QTemporaryFile file;
932 if (file.open())
933 {
934 file.setAutoRemove(false);
935 path = file.fileName();
936 file.write(payload["filedata"].toString().toUtf8());
937 file.close();
938 }
939 }
940 else
941 path = payload["filepath"].toString();
942
943 if (!path.isEmpty())
944 {
945 auto result = scheduler->loadFile(QUrl::fromLocalFile(path));
946 QJsonObject response =
947 {
948 {"result", result},
949 {"path", path}
950 };
951 sendResponse(commands[SCHEDULER_LOAD_FILE], response);
952 }
953 }
954 else if(command == commands[SCHEDULER_START_JOB])
955 {
956 scheduler->toggleScheduler();
957 }
958 else if(command == commands[SCHEDULER_IMPORT_MOSAIC])
959 {
960 if (scheduler->importMosaic(payload))
961 sendSchedulerJobs();
962 else
963 sendEvent(i18n("Mosaic import failed."), KSNotification::Scheduler, KSNotification::Alert);
964 }
965}
966
967///////////////////////////////////////////////////////////////////////////////////////////
968///
969///////////////////////////////////////////////////////////////////////////////////////////
970void Message::processPolarCommands(const QString &command, const QJsonObject &payload)
971{
972 Ekos::Align *align = m_Manager->alignModule();
973 Ekos::PolarAlignmentAssistant *paa = align->polarAlignmentAssistant();
974
975 if (!paa)
976 return;
977
978 if (command == commands[PAH_START])
979 {
980 paa->startPAHProcess();
981 }
982 if (command == commands[PAH_STOP])
983 {
984 paa->stopPAHProcess();
985 }
986 else if (command == commands[PAH_REFRESH])
987 {
988 paa->setPAHRefreshDuration(payload["value"].toDouble(1));
989 paa->startPAHRefreshProcess();
990 }
991 else if (command == commands[PAH_SET_ALGORITHM])
992 {
993 auto algorithmCombo = paa->findChild<QComboBox*>("PAHRefreshAlgorithmCombo");
994 if (algorithmCombo)
995 algorithmCombo->setCurrentIndex(static_cast<Ekos::PolarAlignmentAssistant::RefreshAlgorithm>(payload["value"].toInt(1)));
996 }
997 else if (command == commands[PAH_RESET_VIEW])
998 {
999 emit resetPolarView();
1000 }
1001 else if (command == commands[PAH_SET_CROSSHAIR])
1002 {
1003 double x = payload["x"].toDouble();
1004 double y = payload["y"].toDouble();
1005
1006 if (m_BoundingRect.isNull() == false)
1007 {
1008 // #1 Find actual dimension inside the bounding rectangle
1009 // since if we have bounding rectable then x,y fractions are INSIDE it
1010 double boundX = x * m_BoundingRect.width();
1011 double boundY = y * m_BoundingRect.height();
1012
1013 // #2 Find fraction of the dimensions above the full image size
1014 // Add to it the bounding rect top left offsets
1015 // factors in the change caused by zoom
1016 x = ((boundX + m_BoundingRect.x()) / (m_CurrentZoom / 100)) / m_ViewSize.width();
1017 y = ((boundY + m_BoundingRect.y()) / (m_CurrentZoom / 100)) / m_ViewSize.height();
1018
1019 }
1020
1021 paa->setPAHCorrectionOffsetPercentage(x, y);
1022 }
1023 else if (command == commands[PAH_SELECT_STAR_DONE])
1024 {
1025 // This button was removed from the desktop PAA scheme.
1026 // Nothing to do.
1027 // TODO: Make sure this works.
1028 }
1029 else if (command == commands[PAH_REFRESHING_DONE])
1030 {
1031 paa->stopPAHProcess();
1032 }
1033 else if (command == commands[PAH_SLEW_DONE])
1034 {
1035 paa->setPAHSlewDone();
1036 }
1037 else if (command == commands[PAH_PAH_SET_ZOOM])
1038 {
1039 double scale = payload["scale"].toDouble();
1040 align->setAlignZoom(scale);
1041 }
1042
1043}
1044
1045///////////////////////////////////////////////////////////////////////////////////////////
1046///
1047///////////////////////////////////////////////////////////////////////////////////////////
1048void Message::setPAHStage(Ekos::PolarAlignmentAssistant::Stage stage)
1049{
1050 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1051 return;
1052
1053 Q_UNUSED(stage)
1054 Ekos::Align *align = m_Manager->alignModule();
1055
1056 Ekos::PolarAlignmentAssistant *paa = align->polarAlignmentAssistant();
1057
1058 if (!paa)
1059 return;
1060
1061 QJsonObject polarState =
1062 {
1063 {"stage", paa->getPAHStageString(false)}
1064 };
1065
1066
1067 // Increase size when select star
1068 if (stage == Ekos::PolarAlignmentAssistant::PAH_STAR_SELECT)
1069 align->zoomAlignView();
1070
1071 sendResponse(commands[NEW_POLAR_STATE], polarState);
1072}
1073
1074///////////////////////////////////////////////////////////////////////////////////////////
1075///
1076///////////////////////////////////////////////////////////////////////////////////////////
1077void Message::setPAHMessage(const QString &message)
1078{
1079 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1080 return;
1081
1082 QTextDocument doc;
1083 doc.setHtml(message);
1084 QJsonObject polarState =
1085 {
1086 {"message", doc.toPlainText()}
1087 };
1088
1089 sendResponse(commands[NEW_POLAR_STATE], polarState);
1090}
1091
1092///////////////////////////////////////////////////////////////////////////////////////////
1093///
1094///////////////////////////////////////////////////////////////////////////////////////////
1095void Message::setPolarResults(QLineF correctionVector, double polarError, double azError, double altError)
1096{
1097 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1098 return;
1099
1100 this->correctionVector = correctionVector;
1101
1102 QPointF center = 0.5 * correctionVector.p1() + 0.5 * correctionVector.p2();
1103 QJsonObject vector =
1104 {
1105 {"center_x", center.x()},
1106 {"center_y", center.y()},
1107 {"mag", correctionVector.length()},
1108 {"pa", correctionVector.angle()},
1109 {"error", polarError},
1110 {"azError", azError},
1111 {"altError", altError}
1112 };
1113
1114 QJsonObject polarState =
1115 {
1116 {"vector", vector}
1117 };
1118
1119 sendResponse(commands[NEW_POLAR_STATE], polarState);
1120}
1121
1122///////////////////////////////////////////////////////////////////////////////////////////
1123///
1124///////////////////////////////////////////////////////////////////////////////////////////
1125void Message::setUpdatedErrors(double total, double az, double alt)
1126{
1127 if (isConnected() == false || m_Manager->getEkosStartingStatus() != Ekos::Success)
1128 return;
1129
1131 {
1132 {"updatedError", total},
1133 {"updatedAZError", az},
1134 {"updatedALTError", alt}
1135 };
1136
1137 sendResponse(commands[NEW_POLAR_STATE], error);
1138}
1139
1140///////////////////////////////////////////////////////////////////////////////////////////
1141///
1142///////////////////////////////////////////////////////////////////////////////////////////
1143void Message::setPAHEnabled(bool enabled)
1144{
1145 if (m_Manager->getEkosStartingStatus() != Ekos::Success)
1146 return;
1147
1148 QJsonObject polarState =
1149 {
1150 {"enabled", enabled}
1151 };
1152
1153 sendResponse(commands[NEW_POLAR_STATE], polarState);
1154}
1155
1156///////////////////////////////////////////////////////////////////////////////////////////
1157///
1158///////////////////////////////////////////////////////////////////////////////////////////
1159void Message::processProfileCommands(const QString &command, const QJsonObject &payload)
1160{
1161 if (command == commands[START_PROFILE])
1162 {
1163 if (m_Manager->getEkosStartingStatus() != Ekos::Idle)
1164 m_Manager->stop();
1165
1166 m_Manager->setProfile(payload["name"].toString());
1167 // Always Sync time before we start
1168 KStarsData::Instance()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
1169 m_Manager->start();
1170 }
1171 else if (command == commands[STOP_PROFILE])
1172 {
1173 m_Manager->stop();
1174
1175 // Close all FITS Viewers
1176 KStars::Instance()->clearAllViewers();
1177
1178 m_PropertySubscriptions.clear();
1179 }
1180 else if (command == commands[ADD_PROFILE])
1181 {
1182 m_Manager->addNamedProfile(payload);
1183 sendProfiles();
1184 }
1185 else if (command == commands[UPDATE_PROFILE])
1186 {
1187 m_Manager->editNamedProfile(payload);
1188 sendProfiles();
1189 }
1190 else if (command == commands[GET_PROFILE])
1191 {
1192 m_Manager->getNamedProfile(payload["name"].toString());
1193 }
1194 else if (command == commands[DELETE_PROFILE])
1195 {
1196 m_Manager->deleteNamedProfile(payload["name"].toString());
1197 sendProfiles();
1198 }
1199 else if (command == commands[SET_PROFILE_MAPPING])
1200 {
1201 m_Manager->setProfileMapping(payload);
1202 }
1203 else if (command == commands[SET_PROFILE_PORT_SELECTION])
1204 {
1205 requestPortSelection(false);
1206 m_Manager->acceptPortSelection();
1207 }
1208}
1209
1210///////////////////////////////////////////////////////////////////////////////////////////
1211///
1212///////////////////////////////////////////////////////////////////////////////////////////
1213void Message::sendProfiles()
1214{
1215 QJsonArray profileArray;
1216
1218 if (!m_Manager->getCurrentProfile(profile))
1219 return;
1220
1221 for (auto &oneProfile : m_Manager->profiles)
1222 profileArray.append(oneProfile->toJson());
1223
1224 QJsonObject profiles =
1225 {
1226 {"selectedProfile", profile->name},
1227 {"profiles", profileArray}
1228 };
1229 sendResponse(commands[GET_PROFILES], profiles);
1230}
1231
1232///////////////////////////////////////////////////////////////////////////////////////////
1233///
1234///////////////////////////////////////////////////////////////////////////////////////////
1235void Message::sendSchedulerJobs()
1236{
1237 QJsonObject jobs =
1238 {
1239 {"jobs", m_Manager->schedulerModule()->moduleState()->getJSONJobs()}
1240 };
1241 sendResponse(commands[SCHEDULER_GET_JOBS], jobs);
1242}
1243
1244///////////////////////////////////////////////////////////////////////////////////////////
1245///
1246///////////////////////////////////////////////////////////////////////////////////////////
1247void Message::sendSchedulerJobList(QJsonArray jobsList)
1248{
1249 QJsonObject jobs =
1250 {
1251 {"jobs", jobsList}
1252 };
1253 sendResponse(commands[SCHEDULER_GET_JOBS], jobs);
1254}
1255
1256///////////////////////////////////////////////////////////////////////////////////////////
1257///
1258///////////////////////////////////////////////////////////////////////////////////////////
1259void Message::sendSchedulerStatus(const QJsonObject &status)
1260{
1261 sendResponse(commands[NEW_SCHEDULER_STATE], status);
1262}
1263
1264
1265///////////////////////////////////////////////////////////////////////////////////////////
1266///
1267///////////////////////////////////////////////////////////////////////////////////////////
1268void Message::setEkosStatingStatus(Ekos::CommunicationStatus status)
1269{
1270 if (status == Ekos::Pending)
1271 return;
1272
1273 QJsonObject connectionState =
1274 {
1275 {"connected", true},
1276 {"online", status == Ekos::Success}
1277 };
1278 sendResponse(commands[NEW_CONNECTION_STATE], connectionState);
1279}
1280
1281///////////////////////////////////////////////////////////////////////////////////////////
1282///
1283///////////////////////////////////////////////////////////////////////////////////////////
1284void Message::setINDIStatus(Ekos::CommunicationStatus status)
1285{
1286 QJsonObject connectionState =
1287 {
1288 {"status", status},
1289 };
1290
1291 sendResponse(commands[NEW_INDI_STATE], connectionState);
1292}
1293
1294///////////////////////////////////////////////////////////////////////////////////////////
1295///
1296///////////////////////////////////////////////////////////////////////////////////////////
1297void Message::processOptionsCommands(const QString &command, const QJsonObject &payload)
1298{
1299 if (command == commands[OPTION_SET])
1300 {
1301 const QJsonArray options = payload["options"].toArray();
1302 for (const auto &oneOption : options)
1303 Options::self()->setProperty(oneOption["name"].toString().toLatin1(), oneOption["value"].toVariant());
1304
1305 Options::self()->save();
1306 emit optionsUpdated();
1307 }
1308 else if (command == commands[OPTION_GET])
1309 {
1310 const QJsonArray options = payload["options"].toArray();
1311 QJsonArray result;
1312 for (const auto &oneOption : options)
1313 {
1314 const auto name = oneOption["name"].toString();
1315 QVariant value = Options::self()->property(name.toLatin1());
1316 QVariantMap map;
1317 map["name"] = name;
1318 map["value"] = value;
1320 }
1321 sendResponse(commands[OPTION_GET], result);
1322 }
1323}
1324
1325///////////////////////////////////////////////////////////////////////////////////////////
1326///
1327///////////////////////////////////////////////////////////////////////////////////////////
1328void Message::processScopeCommands(const QString &command, const QJsonObject &payload)
1329{
1330 if (command == commands[ADD_SCOPE])
1331 {
1332 KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(),
1333 payload["type"].toString(), payload["aperture"].toDouble(), payload["focal_length"].toDouble());
1334 }
1335 else if (command == commands[UPDATE_SCOPE])
1336 {
1337 KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(),
1338 payload["type"].toString(), payload["aperture"].toDouble(), payload["focal_length"].toDouble(), payload["id"].toString());
1339 }
1340 else if (command == commands[DELETE_SCOPE])
1341 {
1342 KStarsData::Instance()->userdb()->DeleteEquipment("telescope", payload["id"].toString());
1343 }
1344
1345 sendScopes();
1346}
1347
1348///////////////////////////////////////////////////////////////////////////////////////////
1349///
1350///////////////////////////////////////////////////////////////////////////////////////////
1351void Message::processDSLRCommands(const QString &command, const QJsonObject &payload)
1352{
1353 if (command == commands[DSLR_SET_INFO])
1354 {
1355 if (m_Manager->captureModule())
1356 m_Manager->captureModule()->mainCamera()->addDSLRInfo(
1357 payload["model"].toString(),
1358 payload["width"].toInt(),
1359 payload["height"].toInt(),
1360 payload["pixelw"].toDouble(),
1361 payload["pixelh"].toDouble());
1362
1363 }
1364 else if(command == commands[DSLR_ADD_LENS])
1365 {
1366 KStarsData::Instance()->userdb()->AddDSLRLens(payload["model"].toString(), payload["vendor"].toString(),
1367 payload["focal_length"].toDouble(), payload["focal_ratio"].toDouble());
1368 }
1369 else if (command == commands[DSLR_DELETE_LENS])
1370 {
1371 KStarsData::Instance()->userdb()->DeleteEquipment("dslrlens", payload["id"].toString());
1372 }
1373 else if (command == commands[DSLR_UPDATE_LENS])
1374 {
1375 KStarsData::Instance()->userdb()->AddDSLRLens(payload["model"].toString(), payload["vendor"].toString(),
1376 payload["focal_length"].toDouble(), payload["focal_ratio"].toDouble(), payload["id"].toString());
1377 }
1378
1379 sendDSLRLenses();
1380}
1381
1382///////////////////////////////////////////////////////////////////////////////////////////
1383///
1384///////////////////////////////////////////////////////////////////////////////////////////
1385void Message::processTrainCommands(const QString &command, const QJsonObject &payload)
1386{
1387 if (command == commands[TRAIN_GET_ALL])
1388 sendTrains();
1389 else if (command == commands[TRAIN_GET_PROFILES])
1390 sendTrainProfiles();
1391 else if (command == commands[TRAIN_SET])
1392 {
1393 auto module = payload["module"].toString();
1394 auto name = payload["name"].toString();
1395
1396 if (module == "capture")
1397 {
1398 if (m_Manager->captureModule())
1399 m_Manager->captureModule()->setOpticalTrain(name);
1400 }
1401 else if (module == "focus")
1402 {
1403 if (m_Manager->focusModule())
1404 m_Manager->focusModule()->setOpticalTrain(name);
1405 }
1406 else if (module == "guide")
1407 {
1408 if (m_Manager->guideModule())
1409 m_Manager->guideModule()->setOpticalTrain(name);
1410 }
1411 else if (module == "align")
1412 {
1413 if (m_Manager->alignModule())
1414 m_Manager->alignModule()->setOpticalTrain(name);
1415 }
1416 else if (module == "mount")
1417 {
1418 if (m_Manager->mountModule())
1419 m_Manager->mountModule()->setOpticalTrain(name);
1420 }
1421 else if (module == "darklibrary")
1422 {
1423 Ekos::DarkLibrary::Instance()->setOpticalTrain(name);
1424 }
1425 }
1426 else if (command == commands[TRAIN_ADD])
1427 {
1428 Ekos::OpticalTrainManager::Instance()->addOpticalTrain(payload);
1429 }
1430 else if (command == commands[TRAIN_UPDATE])
1431 {
1432 Ekos::OpticalTrainManager::Instance()->setOpticalTrain(payload);
1433 }
1434 else if (command == commands[TRAIN_DELETE])
1435 {
1436 Ekos::OpticalTrainManager::Instance()->removeOpticalTrain(payload["name"].toString());
1437 }
1438 else if (command == commands[TRAIN_RESET])
1439 {
1440 Ekos::OpticalTrainManager::Instance()->reset();
1441 }
1442 else if (command == commands[TRAIN_ACCEPT])
1443 {
1444 requestOpticalTrains(false);
1445 Ekos::OpticalTrainManager::Instance()->accept();
1446 }
1447
1448}
1449
1450///////////////////////////////////////////////////////////////////////////////////////////
1451///
1452///////////////////////////////////////////////////////////////////////////////////////////
1453void Message::processFilterManagerCommands(const QString &command, const QJsonObject &payload)
1454{
1456 if (m_Manager->captureModule())
1457 manager = m_Manager->captureModule()->mainCamera()->filterManager();
1458
1459 if (manager.isNull())
1460 return;
1461
1462 if (command == commands[FM_GET_DATA])
1463 {
1464 QJsonObject data = manager->toJSON();
1465 sendResponse(commands[FM_GET_DATA], data);
1466 }
1467 else if (command == commands[FM_SET_DATA])
1468 {
1469 manager->setFilterData(payload);
1470 }
1471}
1472
1473///////////////////////////////////////////////////////////////////////////////////////////
1474///
1475///////////////////////////////////////////////////////////////////////////////////////////
1476void Message::processDarkLibraryCommands(const QString &command, const QJsonObject &payload)
1477{
1478 if (command == commands[DARK_LIBRARY_START])
1479 Ekos::DarkLibrary::Instance()->start();
1480 else if(command == commands[DARK_LIBRARY_SET_ALL_SETTINGS])
1481 {
1482 auto settings = payload.toVariantMap();
1483 Ekos::DarkLibrary::Instance()->setAllSettings(settings);
1484 KSUtils::setGlobalSettings(settings);
1485 }
1486 else if(command == commands[DARK_LIBRARY_GET_ALL_SETTINGS])
1487 sendDarkLibrarySettings(Ekos::DarkLibrary::Instance()->getAllSettings());
1488 else if(command == commands[DARK_LIBRARY_GET_DEFECT_SETTINGS])
1489 sendResponse(commands[DARK_LIBRARY_GET_DEFECT_SETTINGS], Ekos::DarkLibrary::Instance()->getDefectSettings());
1490 else if(command == commands[DARK_LIBRARY_SET_CAMERA_PRESETS])
1491 {
1492 Ekos::DarkLibrary::Instance()->setCameraPresets(payload);
1493 }
1494 else if (command == commands[DARK_LIBRARY_STOP])
1495 {
1496 Ekos::DarkLibrary::Instance()->stop();
1497 }
1498 else if (command == commands[DARK_LIBRARY_GET_MASTERS_IMAGE])
1499 {
1500 const int row = payload["row"].toInt();
1501 Ekos::DarkLibrary::Instance()->loadIndexInView(row);
1502 }
1503 else if (command == commands[DARK_LIBRARY_GET_CAMERA_PRESETS])
1504 {
1505 sendResponse(commands[DARK_LIBRARY_GET_CAMERA_PRESETS], Ekos::DarkLibrary::Instance()->getCameraPresets());
1506 }
1507 else if (command == commands[DARK_LIBRARY_SET_DEFECT_PIXELS])
1508 {
1509 Ekos::DarkLibrary::Instance()->setDefectPixels(payload);
1510 }
1511 else if (command == commands[DARK_LIBRARY_SAVE_MAP])
1512 {
1513 Ekos::DarkLibrary::Instance()->saveMapB->click();
1514 }
1515 else if (command == commands[DARK_LIBRARY_SET_DEFECT_FRAME])
1516 {
1517 Ekos::DarkLibrary::Instance()->setDefectMapEnabled(false);
1518 }
1519 else if (command == commands[DARK_LIBRARY_GET_VIEW_MASTERS])
1520 {
1521 sendResponse(commands[DARK_LIBRARY_GET_VIEW_MASTERS], Ekos::DarkLibrary::Instance()->getViewMasters());
1522 }
1523 else if (command == commands[DARK_LIBRARY_CLEAR_MASTERS_ROW])
1524 {
1525 const int rowIndex = payload["row"].toInt();
1526 Ekos::DarkLibrary::Instance()->clearRow(rowIndex);
1527 }
1528}
1529
1530///////////////////////////////////////////////////////////////////////////////////////////
1531///
1532///////////////////////////////////////////////////////////////////////////////////////////
1533void Message::processDeviceCommands(const QString &command, const QJsonObject &payload)
1534{
1535 QString device = payload["device"].toString();
1536
1537 // In case we want to UNSUBSCRIBE from all at once
1538 if (device.isEmpty() && command == commands[DEVICE_PROPERTY_UNSUBSCRIBE])
1539 {
1540 m_PropertySubscriptions.clear();
1541 return;
1542 }
1543
1545 if (!INDIListener::findDevice(device, oneDevice))
1546 return;
1547
1548 // Get specific property
1549 if (command == commands[DEVICE_PROPERTY_GET])
1550 {
1551 QJsonObject propObject;
1552 if (oneDevice->getJSONProperty(payload["property"].toString(), propObject, payload["compact"].toBool(true)))
1553 sendResponse(commands[DEVICE_PROPERTY_GET], propObject);
1554 }
1555 // Set specific property
1556 else if (command == commands[DEVICE_PROPERTY_SET])
1557 {
1558 oneDevice->setJSONProperty(payload["property"].toString(), payload["elements"].toArray());
1559 }
1560 // Return ALL properties
1561 else if (command == commands[DEVICE_GET])
1562 {
1564 for (const auto &oneProp : *oneDevice->getProperties())
1565 {
1566 QJsonObject singleProp;
1567 if (oneDevice->getJSONProperty(oneProp.getName(), singleProp, payload["compact"].toBool(false)))
1568 properties.append(singleProp);
1569 }
1570
1571 QJsonObject response =
1572 {
1573 {"device", device},
1574 {"properties", properties}
1575 };
1576
1577 sendResponse(commands[DEVICE_GET], response);
1578 }
1579 // Subscribe to one or more properties
1580 // When subscribed, the updates are immediately pushed as soon as they are received.
1581 else if (command == commands[DEVICE_PROPERTY_SUBSCRIBE])
1582 {
1583 const QJsonArray properties = payload["properties"].toArray();
1584 const QJsonArray groups = payload["groups"].toArray();
1585
1586 // Get existing subscribed props for this device
1587 QSet<QString> props;
1588 if (m_PropertySubscriptions.contains(device))
1589 props = m_PropertySubscriptions[device];
1590
1591 // If it is just a single property, let's insert it to props.
1592 if (properties.isEmpty() == false)
1593 {
1594 for (const auto &oneProp : properties)
1595 props.insert(oneProp.toString());
1596 }
1597 // If group is specified, then we need to add ALL properties belonging to this group.
1598 else if (groups.isEmpty() == false)
1599 {
1600 QVariantList indiGroups = groups.toVariantList();
1601 for (auto &oneProp : *oneDevice->getProperties())
1602 {
1603 if (indiGroups.contains(oneProp.getGroupName()))
1604 props.insert(oneProp.getName());
1605 }
1606 }
1607 // Otherwise, subscribe to ALL property in this device
1608 else
1609 {
1610 for (auto &oneProp : *oneDevice->getProperties())
1611 props.insert(oneProp.getName());
1612 }
1613
1614 m_PropertySubscriptions[device] = props;
1615 }
1616 else if (command == commands[DEVICE_PROPERTY_UNSUBSCRIBE])
1617 {
1618 const QJsonArray properties = payload["properties"].toArray();
1619 const QJsonArray groups = payload["groups"].toArray();
1620
1621 // Get existing subscribed props for this device
1622 QSet<QString> props;
1623 if (m_PropertySubscriptions.contains(device))
1624 props = m_PropertySubscriptions[device];
1625
1626 // If it is just a single property, let's insert it to props.
1627 // If it is just a single property, let's insert it to props.
1628 if (properties.isEmpty() == false)
1629 {
1630 for (const auto &oneProp : properties)
1631 props.remove(oneProp.toString());
1632 }
1633 // If group is specified, then we need to add ALL properties belonging to this group.
1634 else if (groups.isEmpty() == false)
1635 {
1636 QVariantList indiGroups = groups.toVariantList();
1637 for (auto &oneProp : *oneDevice->getProperties())
1638 {
1639 if (indiGroups.contains(oneProp.getGroupName()))
1640 props.remove(oneProp.getName());
1641 }
1642 }
1643 // Otherwise, subscribe to ALL property in this device
1644 else
1645 {
1646 for (auto &oneProp : *oneDevice->getProperties())
1647 props.remove(oneProp.getName());
1648 }
1649
1650 m_PropertySubscriptions[device] = props;
1651 }
1652}
1653
1654///////////////////////////////////////////////////////////////////////////////////////////
1655///
1656///////////////////////////////////////////////////////////////////////////////////////////
1657void Message::processAstronomyCommands(const QString &command, const QJsonObject &payload)
1658{
1659 if (command == commands[ASTRO_GET_ALMANC])
1660 {
1661 // Today's date
1662 const KStarsDateTime localTime = KStarsData::Instance()->lt();
1663 // Local Midnight
1664 const KStarsDateTime midnight = KStarsDateTime(localTime.date(), QTime(0, 0), Qt::LocalTime);
1665
1666 KSAlmanac almanac(midnight, KStarsData::Instance()->geo());
1667
1668 QJsonObject response =
1669 {
1670 {"SunRise", almanac.getSunRise()},
1671 {"SunSet", almanac.getSunSet()},
1672 {"SunMaxAlt", almanac.getSunMaxAlt()},
1673 {"SunMinAlt", almanac.getSunMinAlt()},
1674 {"MoonRise", almanac.getMoonRise()},
1675 {"MoonSet", almanac.getMoonSet()},
1676 {"MoonPhase", almanac.getMoonPhase()},
1677 {"MoonIllum", almanac.getMoonIllum()},
1678 {"Dawn", almanac.getDawnAstronomicalTwilight()},
1679 {"Dusk", almanac.getDuskAstronomicalTwilight()},
1680
1681 };
1682
1683 sendResponse(commands[ASTRO_GET_ALMANC], response);
1684 }
1685 else if (command == commands[ASTRO_GET_NAMES])
1686 {
1687 auto composite = KStarsData::Instance()->skyComposite();
1688 QStringList all;
1690 CatalogsDB::CatalogObjectList dsoObjects;
1691
1692 allObjects.append(composite->objectLists(SkyObject::STAR));
1693 allObjects.append(composite->objectLists(SkyObject::CATALOG_STAR));
1694 allObjects.append(composite->objectLists(SkyObject::PLANET));
1695 allObjects.append(composite->objectLists(SkyObject::MOON));
1696 allObjects.append(composite->objectLists(SkyObject::COMET));
1697 allObjects.append(composite->objectLists(SkyObject::ASTEROID));
1698 allObjects.append(composite->objectLists(SkyObject::SUPERNOVA));
1699 allObjects.append(composite->objectLists(SkyObject::SATELLITE));
1700 dsoObjects = m_DSOManager.get_objects_all();
1701
1702 for (auto &oneObject : allObjects)
1703 all << oneObject.second->name() << oneObject.second->longname().split(", ");
1704
1705 for (auto &oneObject : dsoObjects)
1706 all << oneObject.name() << oneObject.longname().split(", ");
1707
1708 all.removeDuplicates();
1710 sendResponse(commands[ASTRO_GET_NAMES], QJsonArray::fromStringList(all));
1711 }
1712 else if (command == commands[ASTRO_GET_DESIGNATIONS])
1713 {
1714 QJsonArray designations;
1715
1716 for (auto &oneObject : m_DSOManager.get_objects_all())
1717 {
1718 QJsonObject oneDesignation =
1719 {
1720 {"primary", oneObject.name()},
1721 {"designations", QJsonArray::fromStringList(oneObject.longname().split(", "))}
1722 };
1723
1724 designations.append(oneDesignation);
1725 }
1726
1727 sendResponse(commands[ASTRO_GET_DESIGNATIONS], designations);
1728 }
1729 else if (command == commands[ASTRO_GET_LOCATION])
1730 {
1731 auto geo = KStarsData::Instance()->geo();
1733 {
1734 {"name", geo->name()},
1735 {"longitude", geo->lng()->Degrees()},
1736 {"latitude", geo->lat()->Degrees()},
1737 {"elevation", geo->elevation()},
1738 {"tz", geo->TZ()},
1739 {"tz0", geo->TZ0()}
1740 };
1741
1742 sendResponse(commands[ASTRO_GET_LOCATION], location);
1743 }
1744 // Get a list of object based on criteria
1745 else if (command == commands[ASTRO_SEARCH_OBJECTS])
1746 {
1747 // Set time if required.
1748 if (payload.contains("jd"))
1749 {
1750 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble());
1751 KStarsData::Instance()->clock()->setUTC(jd);
1752 }
1753
1754 // Search Criteria
1755 // Object Type
1756 auto objectType = static_cast<SkyObject::TYPE>(payload["type"].toInt(SkyObject::GALAXY));
1757 // Azimuth restriction
1758 auto objectDirection = static_cast<Direction>(payload["direction"].toInt(All));
1759 // Maximum Object Magnitude
1760 auto objectMaxMagnitude = payload["maxMagnitude"].toDouble(10);
1761 // Minimum Object Altitude
1762 auto objectMinAlt = payload["minAlt"].toDouble(15);
1763 // Minimum Duration that the object must be above the altitude (if any) seconds.
1764 auto objectMinDuration = payload["minDuration"].toInt(3600);
1765 // Minimum FOV in arcmins.
1766 auto objectMinFOV = payload["minFOV"].toDouble(0);
1767 // Data instance
1768 auto *data = KStarsData::Instance();
1769 // Geo Location
1770 auto *geo = KStarsData::Instance()->geo();
1771 // If we are before dawn, we check object altitude restrictions
1772 // Otherwise, all objects are welcome
1773 auto start = KStarsData::Instance()->lt();
1774 auto end = getNextDawn();
1775 if (start > end)
1776 // Add 1 day
1777 end = end.addDays(1);
1778
1780 CatalogsDB::CatalogObjectList dsoObjects;
1781 bool isDSO = false;
1782
1783 switch (objectType)
1784 {
1785 // Stars
1786 case SkyObject::STAR:
1787 case SkyObject::CATALOG_STAR:
1788 allObjects.append(data->skyComposite()->objectLists(SkyObject::STAR));
1789 allObjects.append(data->skyComposite()->objectLists(SkyObject::CATALOG_STAR));
1790 break;
1791 // Planets & Moon
1792 case SkyObject::PLANET:
1793 case SkyObject::MOON:
1794 allObjects.append(data->skyComposite()->objectLists(SkyObject::PLANET));
1795 allObjects.append(data->skyComposite()->objectLists(SkyObject::MOON));
1796 break;
1797 // Comets & Asteroids
1798 case SkyObject::COMET:
1799 allObjects.append(data->skyComposite()->objectLists(SkyObject::COMET));
1800 break;
1801 case SkyObject::ASTEROID:
1802 allObjects.append(data->skyComposite()->objectLists(SkyObject::ASTEROID));
1803 break;
1804 // Clusters
1805 case SkyObject::OPEN_CLUSTER:
1806 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::OPEN_CLUSTER, objectMaxMagnitude));
1807 isDSO = true;
1808 break;
1809 case SkyObject::GLOBULAR_CLUSTER:
1810 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GLOBULAR_CLUSTER, objectMaxMagnitude));
1811 isDSO = true;
1812 break;
1813 // Nebuale
1814 case SkyObject::GASEOUS_NEBULA:
1815 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GASEOUS_NEBULA, objectMaxMagnitude));
1816 isDSO = true;
1817 break;
1818 case SkyObject::PLANETARY_NEBULA:
1819 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::PLANETARY_NEBULA, objectMaxMagnitude));
1820 isDSO = true;
1821 break;
1822 case SkyObject::GALAXY:
1823 dsoObjects.splice(dsoObjects.end(), m_DSOManager.get_objects(SkyObject::GALAXY, objectMaxMagnitude));
1824 isDSO = true;
1825 break;
1826 case SkyObject::SUPERNOVA:
1827 {
1828 if (!Options::showSupernovae())
1829 {
1830 Options::setShowSupernovae(true);
1831 data->setFullTimeUpdate();
1833 }
1834 allObjects.append(data->skyComposite()->objectLists(SkyObject::SUPERNOVA));
1835 }
1836 break;
1837 case SkyObject::SATELLITE:
1838 {
1839 if (!Options::showSatellites())
1840 {
1841 Options::setShowSatellites(true);
1842 data->setFullTimeUpdate();
1844 }
1845 allObjects.append(data->skyComposite()->objectLists(SkyObject::SATELLITE));
1846 }
1847 break;
1848 default:
1849 break;
1850 }
1851
1852 // Sort by magnitude
1853 std::sort(allObjects.begin(), allObjects.end(), [](const auto & a, const auto & b)
1854 {
1855 return a.second->mag() < b.second->mag();
1856 });
1857
1858 QMutableVectorIterator<QPair<QString, const SkyObject *>> objectIterator(allObjects);
1859
1860 // Filter direction, if specified.
1861 if (objectDirection != All)
1862 {
1863 QPair<int, int> Quardent1(270, 360), Quardent2(0, 90), Quardent3(90, 180), Quardent4(180, 270);
1864 QPair<int, int> minAZ, maxAZ;
1865 switch (objectDirection)
1866 {
1867 case North:
1868 minAZ = Quardent1;
1869 maxAZ = Quardent2;
1870 break;
1871 case East:
1872 minAZ = Quardent2;
1873 maxAZ = Quardent3;
1874 break;
1875 case South:
1876 minAZ = Quardent3;
1877 maxAZ = Quardent4;
1878 break;
1879 case West:
1880 minAZ = Quardent4;
1881 maxAZ = Quardent1;
1882 break;
1883 default:
1884 break;
1885 }
1886
1887 if (isDSO)
1888 {
1889 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin();
1890 while (dsoIterator != dsoObjects.end())
1891 {
1892 // If there a more efficient way to do this?
1893 const double az = (*dsoIterator).recomputeHorizontalCoords(start, geo).az().Degrees();
1894 if (! ((minAZ.first <= az && az <= minAZ.second) || (maxAZ.first <= az && az <= maxAZ.second)))
1895 dsoIterator = dsoObjects.erase(dsoIterator);
1896 else
1897 ++dsoIterator;
1898 }
1899 }
1900 else
1901 {
1902 while (objectIterator.hasNext())
1903 {
1904 const auto az = objectIterator.next().second->recomputeHorizontalCoords(start, geo).az().Degrees();
1905 if (! ((minAZ.first <= az && az <= minAZ.second) || (maxAZ.first <= az && az <= maxAZ.second)))
1906 objectIterator.remove();
1907 }
1908 }
1909 }
1910
1911 // Maximum Magnitude
1912 if (!isDSO)
1913 {
1914 objectIterator.toFront();
1915 while (objectIterator.hasNext())
1916 {
1917 auto magnitude = objectIterator.next().second->mag();
1918 // Only filter for objects that have valid magnitude, otherwise, they're automatically included.
1919 if (magnitude != NaN::f && magnitude > objectMaxMagnitude)
1920 objectIterator.remove();
1921 }
1922 }
1923
1924 // Altitude
1925 if (isDSO)
1926 {
1927 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin();
1928 while (dsoIterator != dsoObjects.end())
1929 {
1930 double duration = 0;
1931 for (KStarsDateTime t = start; t < end; t = t.addSecs(3600.0))
1932 {
1933 dms LST = geo->GSTtoLST(t.gst());
1934 (*dsoIterator).EquatorialToHorizontal(&LST, geo->lat());
1935 if ((*dsoIterator).alt().Degrees() >= objectMinAlt)
1936 duration += 3600;
1937 }
1938
1939 if (duration < objectMinDuration)
1940 dsoIterator = dsoObjects.erase(dsoIterator);
1941 else
1942 ++dsoIterator;
1943 }
1944 }
1945 else
1946 {
1947 objectIterator.toFront();
1948 while (objectIterator.hasNext())
1949 {
1950 auto oneObject = objectIterator.next().second;
1951 double duration = 0;
1952
1953 for (KStarsDateTime t = start; t < end; t = t.addSecs(3600.0))
1954 {
1955 auto LST = geo->GSTtoLST(t.gst());
1956 const_cast<SkyObject *>(oneObject)->EquatorialToHorizontal(&LST, geo->lat());
1957 if (oneObject->alt().Degrees() >= objectMinAlt)
1958 duration += 3600;
1959 }
1960
1961 if (duration < objectMinDuration)
1962 objectIterator.remove();
1963 }
1964 }
1965
1966 // For DSOs, check minimum required FOV, if any.
1967 if (isDSO && objectMinFOV > 0)
1968 {
1969 CatalogsDB::CatalogObjectList::iterator dsoIterator = dsoObjects.begin();
1970 while (dsoIterator != dsoObjects.end())
1971 {
1972 if ((*dsoIterator).a() < objectMinFOV)
1973 dsoIterator = dsoObjects.erase(dsoIterator);
1974 else
1975 ++dsoIterator;
1976 }
1977 }
1978
1979 QStringList searchObjects;
1980 for (auto &oneObject : allObjects)
1981 searchObjects.append(oneObject.second->name());
1982 for (auto &oneObject : dsoObjects)
1983 searchObjects.append(oneObject.name());
1984
1985 searchObjects.removeDuplicates();
1986 QJsonArray response = QJsonArray::fromStringList(searchObjects);
1987
1988 sendResponse(commands[ASTRO_SEARCH_OBJECTS], response);
1989 }
1990 else if(command == commands[ASTRO_GET_OBJECT_INFO])
1991 {
1992 const auto name = payload["object"].toString();
1993 bool exact = payload["exact"].toBool(false);
1994 QJsonObject info;
1995 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, exact);
1996 if(oneObject)
1997 {
1998 info =
1999 {
2000 {"name", exact ? name : oneObject->name()},
2001 {"designations", QJsonArray::fromStringList(oneObject->longname().split(", "))},
2002 {"magnitude", oneObject->mag()},
2003 {"ra0", oneObject->ra0().Hours()},
2004 {"de0", oneObject->dec0().Degrees()},
2005 {"ra", oneObject->ra().Hours()},
2006 {"de", oneObject->dec().Degrees()},
2007 {"object", true}
2008 };
2009 sendResponse(commands[ASTRO_GET_OBJECT_INFO], info);
2010 }
2011 else
2012 {
2013 info =
2014 {
2015 {"name", name},
2016 {"object", false},
2017 };
2018 sendResponse(commands[ASTRO_GET_OBJECT_INFO], info );
2019 }
2020
2021 }
2022 // Get a list of object based on criteria
2023 else if (command == commands[ASTRO_GET_OBJECTS_INFO])
2024 {
2025 // Set time if required.
2026 if (payload.contains("jd"))
2027 {
2028 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble());
2029 KStarsData::Instance()->clock()->setUTC(jd);
2030 }
2031
2032 // Object Names
2033 bool exact = payload["exact"].toBool(false);
2034 QVariantList objectNames = payload["names"].toArray().toVariantList();
2035 QJsonArray objectsArray;
2036
2037 for (auto &oneName : objectNames)
2038 {
2039 const QString name = oneName.toString();
2040 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, exact);
2041 if (oneObject)
2042 {
2043 QJsonObject info =
2044 {
2045 {"name", exact ? name : oneObject->name()},
2046 {"designations", QJsonArray::fromStringList(oneObject->longname().split(", "))},
2047 {"magnitude", oneObject->mag()},
2048 {"ra0", oneObject->ra0().Hours()},
2049 {"de0", oneObject->dec0().Degrees()},
2050 {"ra", oneObject->ra().Hours()},
2051 {"de", oneObject->dec().Degrees()},
2052 };
2053
2054 // If DSO, add angular size.
2055 CatalogObject *dsoObject = dynamic_cast<CatalogObject*>(oneObject);
2056 if (dsoObject)
2057 {
2058 info["a"] = dsoObject->a();
2059 info["b"] = dsoObject->b();
2060 info["pa"] = dsoObject->pa();
2061 }
2062
2063 objectsArray.append(info);
2064 }
2065 }
2066
2067 sendResponse(commands[ASTRO_GET_OBJECTS_INFO], objectsArray);
2068 }
2069 // Get a object observability alt/az/ha
2070 else if (command == commands[ASTRO_GET_OBJECTS_OBSERVABILITY])
2071 {
2072 // Set time if required.
2073 if (payload.contains("jd"))
2074 {
2075 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble());
2076 KStarsData::Instance()->clock()->setUTC(jd);
2077 }
2078
2079 // Object Names
2080 QVariantList objectNames = payload["names"].toArray().toVariantList();
2081 QJsonArray objectsArray;
2082
2083 bool exact = payload["exact"].toBool(false);
2084 // Data instance
2085 auto *data = KStarsData::Instance();
2086 // Geo Location
2087 auto *geo = KStarsData::Instance()->geo();
2088 // UT
2089 auto ut = data->ut();
2090
2091 for (auto &oneName : objectNames)
2092 {
2093 const QString name = oneName.toString();
2094 SkyObject *oneObject = data->skyComposite()->findByName(name, exact);
2095 if (oneObject)
2096 {
2097 oneObject->EquatorialToHorizontal(data->lst(), geo->lat());
2098 dms ha(data->lst()->Degrees() - oneObject->ra().Degrees());
2099 QJsonObject info =
2100 {
2101 {"name", exact ? name : oneObject->name()},
2102 {"az", oneObject->az().Degrees()},
2103 {"alt", oneObject->alt().Degrees()},
2104 {"ha", ha.Hours()},
2105 };
2106
2107 objectsArray.append(info);
2108 }
2109 }
2110
2111 sendResponse(commands[ASTRO_GET_OBJECTS_OBSERVABILITY], objectsArray);
2112 }
2113 else if (command == commands[ASTRO_GET_OBJECTS_RISESET])
2114 {
2115 // Set time if required.
2116 if (payload.contains("jd"))
2117 {
2118 KStarsDateTime jd = KStarsDateTime(payload["jd"].toDouble());
2119 KStarsData::Instance()->clock()->setUTC(jd);
2120 }
2121
2122 // Object Names
2123 QVariantList objectNames = payload["names"].toArray().toVariantList();
2124 QJsonArray objectsArray;
2125
2126 bool exact = payload["exact"].toBool(false);
2127 // Data instance
2128 auto *data = KStarsData::Instance();
2129 // Geo Location
2130 auto *geo = KStarsData::Instance()->geo();
2131 // UT
2132 QDateTime midnight = QDateTime(data->lt().date(), QTime());
2133 KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight));
2134
2135 int DayOffset = 0;
2136 if (data->lt().time().hour() > 12)
2137 DayOffset = 1;
2138
2139 for (auto &oneName : objectNames)
2140 {
2141 const QString name = oneName.toString();
2142 SkyObject *oneObject = data->skyComposite()->findByName(name, exact);
2143 if (oneObject)
2144 {
2145 QJsonObject info;
2146 //Prepare time/position variables
2147 //true = use rise time
2148 QTime riseTime = oneObject->riseSetTime(ut, geo, true);
2149
2150 //If transit time is before rise time, use transit time for tomorrow
2151 QTime transitTime = oneObject->transitTime(ut, geo);
2152 if (transitTime < riseTime)
2153 transitTime = oneObject->transitTime(ut.addDays(1), geo);
2154
2155 //If set time is before rise time, use set time for tomorrow
2156 //false = use set time
2157 QTime setTime = oneObject->riseSetTime(ut, geo, false);
2158 //false = use set time
2159 if (setTime < riseTime)
2160 setTime = oneObject->riseSetTime(ut.addDays(1), geo, false);
2161
2162 info["name"] = exact ? name : oneObject->name();
2163 if (riseTime.isValid())
2164 {
2165 info["rise"] = QString::asprintf("%02d:%02d", riseTime.hour(), riseTime.minute());
2166 info["set"] = QString::asprintf("%02d:%02d", setTime.hour(), setTime.minute());
2167 }
2168 else
2169 {
2170 if (oneObject->alt().Degrees() > 0.0)
2171 {
2172 info["rise"] = "Circumpolar";
2173 info["set"] = "Circumpolar";
2174 }
2175 else
2176 {
2177 info["rise"] = "Never rises";
2178 info["set"] = "Never rises";
2179 }
2180 }
2181
2182 info["transit"] = QString::asprintf("%02d:%02d", transitTime.hour(), transitTime.minute());
2183
2184 QJsonArray altitudes;
2185 for (double h = -12.0; h <= 12.0; h += 0.5)
2186 {
2187 double hour = h + (24.0 * DayOffset);
2188 KStarsDateTime offset = ut.addSecs(hour * 3600.0);
2189 CachingDms LST = geo->GSTtoLST(offset.gst());
2190 oneObject->EquatorialToHorizontal(&LST, geo->lat());
2191 altitudes.append(oneObject->alt().Degrees());
2192 }
2193
2194 info["altitudes"] = altitudes;
2195
2196 objectsArray.append(info);
2197 }
2198 }
2199
2200 sendResponse(commands[ASTRO_GET_OBJECTS_RISESET], objectsArray);
2201 }
2202}
2203
2204///////////////////////////////////////////////////////////////////////////////////////////
2205///
2206///////////////////////////////////////////////////////////////////////////////////////////
2207KStarsDateTime Message::getNextDawn()
2208{
2209 // Today's date
2210 const KStarsDateTime localTime = KStarsData::Instance()->lt();
2211 // Local Midnight
2212 const KStarsDateTime midnight = KStarsDateTime(localTime.date(), QTime(0, 0), Qt::LocalTime);
2213 // Almanac
2214 KSAlmanac almanac(midnight, KStarsData::Instance()->geo());
2215 // Next Dawn
2216 KStarsDateTime nextDawn = midnight.addSecs(almanac.getDawnAstronomicalTwilight() * 24.0 * 3600.0);
2217 // If dawn is earliar than now, add a day
2218 if (nextDawn < localTime)
2219 nextDawn.addDays(1);
2220
2221 return nextDawn;
2222}
2223
2224///////////////////////////////////////////////////////////////////////////////////////////
2225///
2226///////////////////////////////////////////////////////////////////////////////////////////
2227void Message::requestDSLRInfo(const QString &cameraName)
2228{
2229 sendResponse(commands[DSLR_GET_INFO], cameraName);
2230}
2231
2232///////////////////////////////////////////////////////////////////////////////////////////
2233///
2234///////////////////////////////////////////////////////////////////////////////////////////
2235void Message::requestPortSelection(bool show)
2236{
2237 sendResponse(commands[GET_PROFILE_PORT_SELECTION], show);
2238}
2239
2240///////////////////////////////////////////////////////////////////////////////////////////
2241///
2242///////////////////////////////////////////////////////////////////////////////////////////
2243void Message::sendDialog(const QJsonObject &message)
2244{
2245 sendResponse(commands[DIALOG_GET_INFO], message);
2246}
2247
2248///////////////////////////////////////////////////////////////////////////////////////////
2249///
2250///////////////////////////////////////////////////////////////////////////////////////////
2251void Message::sendResponse(const QString &command, const QJsonObject &payload)
2252{
2253 for (auto &nodeManager : m_NodeManagers)
2254 {
2255 nodeManager->message()->sendResponse(command, payload);
2256 }
2257}
2258
2259///////////////////////////////////////////////////////////////////////////////////////////
2260///
2261///////////////////////////////////////////////////////////////////////////////////////////
2262void Message::sendResponse(const QString &command, const QJsonArray &payload)
2263{
2264 for (auto &nodeManager : m_NodeManagers)
2265 {
2266 nodeManager->message()->sendResponse(command, payload);
2267 }
2268}
2269
2270///////////////////////////////////////////////////////////////////////////////////////////
2271///
2272///////////////////////////////////////////////////////////////////////////////////////////
2273void Message::sendResponse(const QString &command, const QString &payload)
2274{
2275 for (auto &nodeManager : m_NodeManagers)
2276 {
2277 nodeManager->message()->sendResponse(command, payload);
2278 }
2279}
2280
2281///////////////////////////////////////////////////////////////////////////////////////////
2282///
2283///////////////////////////////////////////////////////////////////////////////////////////
2284void Message::sendResponse(const QString &command, bool payload)
2285{
2286 for (auto &nodeManager : m_NodeManagers)
2287 {
2288 nodeManager->message()->sendResponse(command, payload);
2289 }
2290}
2291
2292///////////////////////////////////////////////////////////////////////////////////////////
2293///
2294///////////////////////////////////////////////////////////////////////////////////////////
2295void Message::autofocusAborted()
2296{
2297 QJsonObject cStatus =
2298 {
2299 {"status", "Aborted"}
2300 };
2301 sendResponse(commands[NEW_FOCUS_STATE], cStatus);
2302}
2303
2304///////////////////////////////////////////////////////////////////////////////////////////
2305///
2306///////////////////////////////////////////////////////////////////////////////////////////
2307void Message::updateMountStatus(const QJsonObject &status, bool throttle)
2308{
2309 if (throttle)
2310 {
2312 if (m_ThrottleTS.msecsTo(now) >= THROTTLE_INTERVAL)
2313 {
2314 m_ThrottleTS = now;
2315 sendResponse(commands[NEW_MOUNT_STATE], status);
2316 }
2317 }
2318 else
2319 sendResponse(commands[NEW_MOUNT_STATE], status);
2320}
2321
2322///////////////////////////////////////////////////////////////////////////////////////////
2323///
2324///////////////////////////////////////////////////////////////////////////////////////////
2325void Message::updateCaptureStatus(const QJsonObject &status)
2326{
2327 sendResponse(commands[NEW_CAPTURE_STATE], status);
2328}
2329
2330///////////////////////////////////////////////////////////////////////////////////////////
2331///
2332///////////////////////////////////////////////////////////////////////////////////////////
2333void Message::updateFocusStatus(const QJsonObject &status)
2334{
2335 sendResponse(commands[NEW_FOCUS_STATE], status);
2336}
2337
2338///////////////////////////////////////////////////////////////////////////////////////////
2339///
2340///////////////////////////////////////////////////////////////////////////////////////////
2341void Message::updateGuideStatus(const QJsonObject &status)
2342{
2343 sendResponse(commands[NEW_GUIDE_STATE], status);
2344}
2345
2346///////////////////////////////////////////////////////////////////////////////////////////
2347///
2348///////////////////////////////////////////////////////////////////////////////////////////
2349void Message::updateDomeStatus(const QJsonObject &status)
2350{
2351 sendResponse(commands[NEW_DOME_STATE], status);
2352}
2353
2354///////////////////////////////////////////////////////////////////////////////////////////
2355///
2356///////////////////////////////////////////////////////////////////////////////////////////
2357void Message::updateCapStatus(const QJsonObject &status)
2358{
2359 sendResponse(commands[NEW_CAP_STATE], status);
2360}
2361
2362///////////////////////////////////////////////////////////////////////////////////////////
2363///
2364///////////////////////////////////////////////////////////////////////////////////////////
2365void Message::updateAlignStatus(const QJsonObject &status)
2366{
2367 sendResponse(commands[NEW_ALIGN_STATE], status);
2368}
2369
2370///////////////////////////////////////////////////////////////////////////////////////////
2371///
2372///////////////////////////////////////////////////////////////////////////////////////////
2373void Message::sendConnection()
2374{
2375 QJsonObject connectionState =
2376 {
2377 {"connected", true},
2378 {"online", m_Manager->getEkosStartingStatus() == Ekos::Success}
2379 };
2380
2381 sendResponse(commands[NEW_CONNECTION_STATE], connectionState);
2382}
2383
2384///////////////////////////////////////////////////////////////////////////////////////////
2385///
2386///////////////////////////////////////////////////////////////////////////////////////////
2387void Message::sendStates()
2388{
2389 // Send capture sequence if one exists
2390 if (m_Manager->captureModule())
2391 {
2392 QJsonObject captureState = {{ "status", getCaptureStatusString(m_Manager->captureModule()->status(), false)}};
2393 sendResponse(commands[NEW_CAPTURE_STATE], captureState);
2394 sendCaptureSequence(m_Manager->captureModule()->getSequence());
2395 }
2396
2397 if (m_Manager->mountModule())
2398 {
2399 QJsonObject mountState =
2400 {
2401 {"status", m_Manager->mountModule()->statusString(false)},
2402 {"target", m_Manager->capturePreview->mountTarget->text()},
2403 {"slewRate", m_Manager->mountModule()->slewRate()},
2404 {"pierSide", m_Manager->mountModule()->pierSide()}
2405 };
2406
2407 sendResponse(commands[NEW_MOUNT_STATE], mountState);
2408 }
2409
2410 if (m_Manager->focusModule())
2411 {
2412 QJsonObject focusState = {{ "status", getFocusStatusString(m_Manager->focusModule()->status(), false)}};
2413 sendResponse(commands[NEW_FOCUS_STATE], focusState);
2414 }
2415
2416 if (m_Manager->guideModule())
2417 {
2418 QJsonObject guideState = {{ "status", getGuideStatusString(m_Manager->guideModule()->status(), false)}};
2419 sendResponse(commands[NEW_GUIDE_STATE], guideState);
2420 }
2421
2422 if (m_Manager->alignModule())
2423 {
2424 // Align State
2425 QJsonObject alignState =
2426 {
2427 {"status", getAlignStatusString(m_Manager->alignModule()->status(), false)}
2428 };
2429 sendResponse(commands[NEW_ALIGN_STATE], alignState);
2430
2431 // Align settings
2432 sendAlignSettings(m_Manager->alignModule()->getAllSettings());
2433
2434 Ekos::PolarAlignmentAssistant *paa = m_Manager->alignModule()->polarAlignmentAssistant();
2435 if (paa)
2436 {
2437 // Polar State
2438 QTextDocument doc;
2439 doc.setHtml(paa->getPAHMessage());
2440 QJsonObject polarState =
2441 {
2442 {"stage", paa->getPAHStageString(false)},
2443 {"enabled", paa->isEnabled()},
2444 {"message", doc.toPlainText()},
2445 };
2446 sendResponse(commands[NEW_POLAR_STATE], polarState);
2447 }
2448 }
2449}
2450
2451///////////////////////////////////////////////////////////////////////////////////////////
2452///
2453///////////////////////////////////////////////////////////////////////////////////////////
2454void Message::sendEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event)
2455{
2456 if (Options::ekosLiveNotifications() == false)
2457 return;
2458
2459 QJsonObject newEvent =
2460 {
2461 {"source", source},
2462 {"severity", event},
2463 {"message", message},
2464 {"uuid", QUuid::createUuid().toString()}
2465 };
2466
2467 sendResponse(commands[NEW_NOTIFICATION], newEvent);
2468}
2469
2470///////////////////////////////////////////////////////////////////////////////////////////
2471///
2472///////////////////////////////////////////////////////////////////////////////////////////
2473void Message::sendManualRotatorStatus(double currentPA, double targetPA, double threshold)
2474{
2475 QJsonObject request = {{ "currentPA", currentPA}, {"targetPA", targetPA}, {"threshold", threshold}};
2476 sendResponse(commands[ALIGN_MANUAL_ROTATOR_STATUS], request);
2477}
2478
2479///////////////////////////////////////////////////////////////////////////////////////////
2480///
2481///////////////////////////////////////////////////////////////////////////////////////////
2482void Message::setBoundingRect(QRect rect, QSize view, double currentZoom)
2483{
2484 m_BoundingRect = rect;
2485 m_ViewSize = view;
2486 m_CurrentZoom = currentZoom;
2487}
2488
2489///////////////////////////////////////////////////////////////////////////////////////////
2490///
2491///////////////////////////////////////////////////////////////////////////////////////////
2492void Message::processDialogResponse(const QJsonObject &payload)
2493{
2494 KSMessageBox::Instance()->selectResponse(payload["button"].toString());
2495}
2496
2497///////////////////////////////////////////////////////////////////////////////////////////
2498///
2499///////////////////////////////////////////////////////////////////////////////////////////
2500void Message::processNewProperty(INDI::Property prop)
2501{
2502 // Do not send new properties until all properties settle down
2503 // then send any properties that appears afterwards since the initial bunch
2504 // would cause a heavy message congestion.
2505 if (m_Manager->settleStatus() != Ekos::CommunicationStatus::Success)
2506 return;
2507
2508 QJsonObject propObject;
2509 ISD::propertyToJson(prop, propObject, false);
2510 sendResponse(commands[DEVICE_PROPERTY_ADD], propObject);
2511}
2512
2513///////////////////////////////////////////////////////////////////////////////////////////
2514///
2515///////////////////////////////////////////////////////////////////////////////////////////
2516void Message::processDeleteProperty(INDI::Property prop)
2517{
2518 QJsonObject payload =
2519 {
2520 {"device", prop.getDeviceName()},
2521 {"name", prop.getName()}
2522 };
2523
2524 sendResponse(commands[DEVICE_PROPERTY_REMOVE], payload);
2525}
2526
2527///////////////////////////////////////////////////////////////////////////////////////////
2528///
2529///////////////////////////////////////////////////////////////////////////////////////////
2530void Message::processMessage(const QSharedPointer<ISD::GenericDevice> &device, int id)
2531{
2532 if (Options::ekosLiveNotifications() == false)
2533 return;
2534
2535 auto message = QString::fromStdString(device->getBaseDevice().messageQueue(id));
2536 QJsonObject payload =
2537 {
2538 {"device", device->getDeviceName()},
2539 {"message", message}
2540 };
2541
2542 sendResponse(commands[DEVICE_MESSAGE], payload);
2543}
2544
2545///////////////////////////////////////////////////////////////////////////////////////////
2546///
2547///////////////////////////////////////////////////////////////////////////////////////////
2548void Message::processUpdateProperty(INDI::Property prop)
2549{
2550 if (m_PropertySubscriptions.contains(prop.getDeviceName()))
2551 {
2552 QSet<QString> subProps = m_PropertySubscriptions[prop.getDeviceName()];
2553 if (subProps.contains(prop.getName()))
2554 {
2555 m_PendingProperties.remove(prop);
2556 m_PendingProperties.insert(prop);
2557 }
2558 }
2559}
2560
2561///////////////////////////////////////////////////////////////////////////////////////////
2562///
2563///////////////////////////////////////////////////////////////////////////////////////////
2564void Message::setPendingPropertiesEnabled(bool enabled)
2565{
2566 if (enabled)
2567 m_PendingPropertiesTimer.start();
2568 else
2569 {
2570 m_PendingPropertiesTimer.stop();
2571 m_PendingProperties.clear();
2572 }
2573}
2574
2575///////////////////////////////////////////////////////////////////////////////////////////
2576///
2577///////////////////////////////////////////////////////////////////////////////////////////
2578void Message::sendPendingProperties()
2579{
2580 for (auto &prop : m_PendingProperties)
2581 {
2582 if (prop->isValid())
2583 {
2584 QJsonObject propObject;
2585 ISD::propertyToJson(*prop, propObject);
2586 sendResponse(commands[DEVICE_PROPERTY_GET], propObject);
2587 }
2588 }
2589
2590 m_PendingProperties.clear();
2591}
2592
2593///////////////////////////////////////////////////////////////////////////////////////////
2594///
2595///////////////////////////////////////////////////////////////////////////////////////////
2596void Message::sendModuleState(const QString &name)
2597{
2598 if (name == "Capture")
2599 {
2600 QJsonObject captureState = {{ "status", getCaptureStatusString(m_Manager->captureModule()->status(), false)}};
2601 sendResponse(commands[NEW_CAPTURE_STATE], captureState);
2602 sendCaptureSequence(m_Manager->captureModule()->getSequence());
2603 }
2604 else if (name == "Mount")
2605 {
2606 QJsonObject mountState =
2607 {
2608 {"status", m_Manager->mountStatus->getStatusText()},
2609 {"target", m_Manager->capturePreview->mountTarget->text()},
2610 {"slewRate", m_Manager->mountModule()->slewRate()},
2611 {"pierSide", m_Manager->mountModule()->pierSide()}
2612 };
2613
2614 sendResponse(commands[NEW_MOUNT_STATE], mountState);
2615 }
2616 else if (name == "Focus")
2617 {
2618 QJsonObject focusState = {{ "status", getFocusStatusString(m_Manager->focusModule()->status(), false)}};
2619 sendResponse(commands[NEW_FOCUS_STATE], focusState);
2620 }
2621 else if (name == "Guide")
2622 {
2623 QJsonObject guideState = {{ "status", getGuideStatusString(m_Manager->guideModule()->status(), false)}};
2624 sendResponse(commands[NEW_GUIDE_STATE], guideState);
2625 }
2626 else if (name == "Align")
2627 {
2628 // Align State
2629 QJsonObject alignState =
2630 {
2631 {"status", getAlignStatusString(m_Manager->alignModule()->status(), false)}
2632 };
2633 sendResponse(commands[NEW_ALIGN_STATE], alignState);
2634
2635 // Align settings
2636 sendAlignSettings(m_Manager->alignModule()->getAllSettings());
2637
2638 Ekos::PolarAlignmentAssistant *paa = m_Manager->alignModule()->polarAlignmentAssistant();
2639 if (paa)
2640 {
2641 // Polar State
2642 QTextDocument doc;
2643 doc.setHtml(paa->getPAHMessage());
2644 QJsonObject polarState =
2645 {
2646 {"stage", paa->getPAHStageString(false)},
2647 {"enabled", paa->isEnabled()},
2648 {"message", doc.toPlainText()},
2649 };
2650 sendResponse(commands[NEW_POLAR_STATE], polarState);
2651 }
2652 }
2653}
2654
2655///////////////////////////////////////////////////////////////////////////////////////////
2656///
2657///////////////////////////////////////////////////////////////////////////////////////////
2658QObject *Message::findObject(const QString &name)
2659{
2660 QObject *object {nullptr};
2661 // Check for manager itself
2662 if (name == "Manager")
2663 return m_Manager;
2664 // Try Manager first
2665 object = m_Manager->findChild<QObject *>(name);
2666 if (object)
2667 return object;
2668 // Then INDI Listener
2669 object = INDIListener::Instance()->findChild<QObject *>(name);
2670 if (object)
2671 return object;
2672 // FITS Viewer. Search for any matching imageData
2673 // TODO Migrate to DBus
2674 for (auto &viewer : KStars::Instance()->getFITSViewers())
2675 {
2676 for (auto &tab : viewer->tabs())
2677 {
2678 if (tab->getView()->objectName() == name)
2679 return tab->getView().get();
2680 }
2681 }
2682
2683 // Finally KStars
2684 // N.B. This does not include indepdent objects with their parent set to null (e.g. FITSViewer)
2685 object = KStars::Instance()->findChild<QObject *>(name);
2686 return object;
2687}
2688
2689///////////////////////////////////////////////////////////////////////////////////////////
2690///
2691///////////////////////////////////////////////////////////////////////////////////////////
2692bool Message::parseArgument(QVariant::Type type, const QVariant &arg, QGenericArgument &genericArg, SimpleTypes &types)
2693{
2694 QGenericArgument genericArgument;
2695
2696 switch (type)
2697 {
2698 case QVariant::Type::Int:
2699 types.number_integer = arg.toInt();
2700 genericArg = Q_ARG(int, types.number_integer);
2701 return true;
2702 case QVariant::Type::UInt:
2703 types.number_unsigned_integer = arg.toUInt();
2704 genericArg = Q_ARG(uint, types.number_unsigned_integer);
2705 return true;
2706 case QVariant::Type::LongLong:
2707 types.number_integer = arg.toLongLong();
2708 genericArg = Q_ARG(int, types.number_integer);
2709 return true;
2710 case QVariant::Type::ULongLong:
2711 types.number_unsigned_integer = arg.toULongLong();
2712 genericArg = Q_ARG(uint, types.number_unsigned_integer);
2713 return true;
2714 case QVariant::Type::Double:
2715 types.number_double = arg.toDouble();
2716 genericArg = Q_ARG(double, types.number_double);
2717 return true;
2718 case QVariant::Type::Bool:
2719 types.boolean = arg.toBool();
2720 genericArg = Q_ARG(bool, types.boolean);
2721 return true;
2722 case QVariant::Type::String:
2723 types.text = arg.toString();
2724 genericArg = Q_ARG(QString, types.text);
2725 return true;
2726 case QVariant::Type::Url:
2727 types.url = arg.toUrl();
2728 genericArg = Q_ARG(QUrl, types.url);
2729 return true;
2730 default:
2731 break;
2732 }
2733
2734 return false;
2735}
2736
2737///////////////////////////////////////////////////////////////////////////////////////////
2738///
2739///////////////////////////////////////////////////////////////////////////////////////////
2740void Message::invokeMethod(QObject *context, const QJsonObject &payload)
2741{
2742 QList<QGenericArgument> argsList;
2743 QList<SimpleTypes> typesList;
2744
2745 auto name = payload["name"].toString().toLatin1();
2746
2747 if (payload.contains("args"))
2748 {
2749 QJsonArray args = payload["args"].toArray();
2750
2751 for (auto oneArg : args)
2752 {
2753 auto argObject = oneArg.toObject();
2754 QGenericArgument genericArgument;
2755 SimpleTypes genericType;
2756 argsList.append(genericArgument);
2757 typesList.append(genericType);
2758 if (parseArgument(static_cast<QVariant::Type>(argObject["type"].toInt()), argObject["value"].toVariant(), argsList.back(),
2759 typesList.last()) == false)
2760 {
2761 argsList.pop_back();
2762 typesList.pop_back();
2763 }
2764 }
2765
2766 switch (argsList.size())
2767 {
2768 case 1:
2769 QMetaObject::invokeMethod(context, name, argsList[0]);
2770 break;
2771 case 2:
2772 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1]);
2773 break;
2774 case 3:
2775 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1], argsList[2]);
2776 break;
2777 case 4:
2778 QMetaObject::invokeMethod(context, name, argsList[0], argsList[1], argsList[2], argsList[3]);
2779 break;
2780 default:
2781 break;
2782 }
2783 }
2784 else
2785 {
2786 QMetaObject::invokeMethod(context, name);
2787 }
2788}
2789
2790}
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...
float a() const
double pa() const override
float b() const
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:90
bool setVideoLimits(uint16_t maxBufferSize, uint16_t maxPreviewFPS)
setVideoLimits sets the buffer size and max preview fps for live preview
Definition capture.cpp:276
const QJsonArray & getSequence() const
getSequence Return the JSON representation of the current sequeue queue
Definition capture.h:429
void stop()
stop Abort all dark job captures.
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:4454
void selectFocusStarFraction(double x, double y)
selectFocusStarFraction Select the focus star based by fraction of the overall size.
Definition focus.cpp:4644
Q_SCRIPTABLE Q_NOREPLY void resetFrame()
DBUS interface function.
Definition focus.cpp:313
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
void addJob(SchedulerJob *job=nullptr)
addJob Add a new job from form values
bool importMosaic(const QJsonObject &payload)
importMosaic Import mosaic into planner and generate jobs for the scheduler.
bool loadFile(const QUrl &path)
loadFile Load scheduler jobs from disk
bool saveFile(const QUrl &path)
saveFile Save scheduler jobs to disk
void removeOneJob(int index)
Remove a job by selecting a table row.
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
Q_INVOKABLE QAction * action(const QString &name) const
A class that implements methods to find sun rise, sun set, twilight begin / end times,...
Definition ksalmanac.h:27
bool selectResponse(const QString &button)
selectResponse Programatically select one the buttons in the dialog.
bool GetAllScopes(QList< OAL::Scope * > &m_scopeList)
updates the scope list with all scopes from database List is cleared and then filled with content.
bool AddDSLRLens(const QString &model, const QString &vendor, const double focalLength, const double focalRatio)
Appends the DSLR lens with given details in the database.
bool AddScope(const QString &model, const QString &vendor, const QString &type, const double &aperture, const double &focalLength)
Appends the scope with given details in the database.
bool DeleteEquipment(const QString &type, const QString &id)
Erase the equipment with given type and unique id Valid equipment types: "telescope",...
bool GetAllDSLRLenses(QList< OAL::DSLRLens * > &dslrlens_list)
updates the dslr list with all DSLR lenses from database List is cleared and then filled with content...
const KStarsDateTime & lt() const
Definition kstarsdata.h:151
KSUserDB * userdb()
Definition kstarsdata.h:215
void changeDateTime(const KStarsDateTime &newDate)
Change the current simulation date/time to the KStarsDateTime argument.
Q_INVOKABLE SimClock * clock()
Definition kstarsdata.h:218
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
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
SkyMap * map() const
Definition kstars.h:141
static KStars * Instance()
Definition kstars.h:123
virtual KActionCollection * actionCollection() const
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS function to start the SimClock.
Definition simclock.cpp:155
Q_SCRIPTABLE Q_NOREPLY void setUTC(const KStarsDateTime &newtime)
DBUS function to set the time of the SimClock.
Definition simclock.cpp:181
Q_SCRIPTABLE Q_NOREPLY void stop()
DBUS function to stop the SimClock.
Definition simclock.cpp:136
SkyObject * findByName(const QString &name, bool exact=true) override
Search the children of this SkyMapComposite for a SkyObject whose name matches the argument.
void forceUpdate(bool now=false)
Recalculates the positions of objects in the sky, and then repaints the sky map.
Definition skymap.cpp:1177
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
virtual QString name(void) const
Definition skyobject.h:145
virtual QString longname(void) const
Definition skyobject.h:164
QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
The same iteration technique described in riseSetTime() is used here.
QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact=true) const
Determine the time at which the point will rise or set.
Definition skyobject.cpp:93
float mag() const
Definition skyobject.h:206
TYPE
The type classification of the SkyObject.
Definition skyobject.h:112
The sky coordinates of a point in the sky.
Definition skypoint.h:45
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
Definition skypoint.cpp:700
const CachingDms & dec() const
Definition skypoint.h:269
const CachingDms & ra0() const
Definition skypoint.h:251
const CachingDms & ra() const
Definition skypoint.h:263
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
Definition skypoint.cpp:77
const dms & az() const
Definition skypoint.h:275
const dms & alt() const
Definition skypoint.h:281
const CachingDms & dec0() const
Definition skypoint.h:257
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition dms.cpp:429
double Hours() const
Definition dms.h:168
const double & Degrees() const
Definition dms.h:141
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:531
Q_SCRIPTABLE Q_NOREPLY void stop(CaptureState targetState=CAPTURE_IDLE)
DBUS interface function.
Definition capture.h:483
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS interface function.
Definition capture.h:469
Q_SCRIPTABLE bool saveSequenceQueue(const QString &path)
DBUS interface function.
Definition capture.h:162
Q_SCRIPTABLE bool loadSequenceQueue(const QString &fileURL, QString targetName="")
DBUS interface function.
Definition capture.h:153
Q_SCRIPTABLE Q_NOREPLY void clearSequenceQueue()
DBUS interface function.
Definition capture.h:134
Q_SCRIPTABLE Q_NOREPLY void capture(double settleTime=0.0)
DBUS interface function.
Definition focus.cpp:1510
Q_SCRIPTABLE bool focusOut(int ms=-1)
DBUS interface function.
Definition focus.cpp:1704
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS interface function.
Definition focus.cpp:1010
Q_SCRIPTABLE Q_NOREPLY void abort()
DBUS interface function.
Definition focus.cpp:1414
Q_SCRIPTABLE bool focusIn(int ms=-1)
DBUS interface function.
Definition focus.cpp:1684
KLocalizedString KI18N_EXPORT ki18n(const char *text)
QString i18n(const char *text, const TYPE &arg...)
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)
QString name(GameStandardAction id)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
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)
KGuiItem remove()
KGuiItem insert()
KGuiItem properties()
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QCA_EXPORT void setProperty(const QString &name, const QVariant &value)
void trigger()
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
void setCurrentIndex(int index)
QDateTime currentDateTime()
QDate date() const const
virtual void accept()
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
void append(QList< T > &&value)
reference back()
iterator begin()
iterator end()
T & last()
void pop_back()
qsizetype size() const const
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
T findChild(const QString &name, Qt::FindChildOptions options) const const
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
bool remove(const T &value)
bool isNull() const const
QString arg(Args &&... args) const const
QString asprintf(const char *cformat,...)
QString fromLatin1(QByteArrayView str)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
qsizetype removeDuplicates()
void sort(Qt::CaseSensitivity cs)
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
bool isValid(int h, int m, int s, int ms)
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
bool isEnabled() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:59:51 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.