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

KDE's Doxygen guidelines are available online.