1 /* 2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com> 3 4 SPDX-License-Identifier: GPL-2.0-or-later 5 */ 6 7 #pragma once 8 9 #ifdef USE_QT5_INDI 10 #include <baseclientqt.h> 11 #else 12 #include <baseclient.h> 13 #endif 14 15 #include "ui_manager.h" 16 17 #include "ekos.h" 18 #include "manager/focusmanager.h" 19 #include "manager/guidemanager.h" 20 #include "fitsviewer/summaryfitsview.h" 21 #include "align/align.h" 22 #include "auxiliary/dome.h" 23 #include "auxiliary/weather.h" 24 #include "auxiliary/dustcap.h" 25 #include "capture/capture.h" 26 #include "focus/focus.h" 27 #include "guide/guide.h" 28 #include "indi/indistd.h" 29 #include "mount/mount.h" 30 #include "scheduler/scheduler.h" 31 #include "analyze/analyze.h" 32 #include "observatory/observatory.h" 33 #include "auxiliary/filtermanager.h" 34 #include "auxiliary/portselector.h" 35 #include "ksnotification.h" 36 // Can't use forward declaration with QPointer. QTBUG-29588 37 #include "auxiliary/opslogs.h" 38 39 #include <QDialog> 40 #include <QHash> 41 #include <QtDBus/QtDBus> 42 43 #include <memory> 44 45 //! Generic record interfaces and implementations. 46 namespace EkosLive 47 { 48 class Client; 49 class Message; 50 class Media; 51 } 52 53 class DriverInfo; 54 class ProfileInfo; 55 class KPageWidgetItem; 56 57 /** 58 * @class Manager 59 * @short Primary class to handle all Ekos modules. 60 * The Ekos Manager class manages startup and shutdown of INDI devices and registeration of devices within Ekos Modules. Ekos module consist of \ref Ekos::Mount, \ref Ekos::Capture, \ref Ekos::Focus, \ref Ekos::Guide, and \ref Ekos::Align modules. 61 * \defgroup EkosDBusInterface "Ekos DBus Interface" provides high level functions to control devices and Ekos modules for a total robotic operation: 62 * <ul> 63 * <li>\ref CaptureDBusInterface "Capture Module DBus Interface"</li> 64 * <li>\ref FocusDBusInterface "Focus Module DBus Interface"</li> 65 * <li>\ref MountDBusInterface "Mount Module DBus Interface"</li> 66 * <li>\ref GuideDBusInterface "Guide Module DBus Interface"</li> 67 * <li>\ref AlignDBusInterface "Align Module DBus Interface"</li> 68 * <li>\ref WeatherDBusInterface "Weather DBus Interface"</li> 69 * <li>\ref DustCapDBusInterface "Dust Cap DBus Interface"</li> 70 * </ul> 71 * For low level access to INDI devices, the \ref INDIDBusInterface "INDI Dbus Interface" provides complete access to INDI devices and properties. 72 * Ekos Manager provides a summary of operations progress in the <i>Summary</i> section of the <i>Setup</i> tab. 73 * 74 * @author Jasem Mutlaq 75 * @version 1.8 76 */ 77 namespace Ekos 78 { 79 80 class Manager : public QDialog, public Ui::Manager 81 { 82 Q_OBJECT 83 Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos") 84 85 Q_SCRIPTABLE Q_PROPERTY(Ekos::CommunicationStatus indiStatus READ indiStatus NOTIFY indiStatusChanged) 86 Q_SCRIPTABLE Q_PROPERTY(Ekos::CommunicationStatus ekosStatus READ ekosStatus NOTIFY ekosStatusChanged) 87 Q_SCRIPTABLE Q_PROPERTY(Ekos::CommunicationStatus settleStatus READ settleStatus NOTIFY settleStatusChanged) 88 Q_SCRIPTABLE Q_PROPERTY(bool ekosLiveStatus READ ekosLiveStatus NOTIFY ekosLiveStatusChanged) 89 Q_SCRIPTABLE Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) 90 91 enum class EkosModule 92 { 93 Setup, 94 Scheduler, 95 Analyze, 96 Capture, 97 Focus, 98 Mount, 99 Align, 100 Guide, 101 Observatory, 102 }; 103 public: 104 static Manager *Instance(); 105 static void release(); 106 107 // No OP initialize()108 void initialize() {} 109 110 void appendLogText(const QString &); setOptionsWidget(KPageWidgetItem * ops)111 void setOptionsWidget(KPageWidgetItem *ops) 112 { 113 ekosOptionsWidget = ops; 114 } 115 void addObjectToScheduler(SkyObject *object); 116 schedulerModule()117 Scheduler *schedulerModule() 118 { 119 return schedulerProcess.get(); 120 } guideModule()121 Guide *guideModule() 122 { 123 return guideProcess.get(); 124 } alignModule()125 Align *alignModule() 126 { 127 return alignProcess.get(); 128 } mountModule()129 Mount *mountModule() 130 { 131 return mountProcess.get(); 132 } focusModule()133 Focus *focusModule() 134 { 135 return focusProcess.get(); 136 } domeModule()137 Dome *domeModule() 138 { 139 return domeProcess.get(); 140 } capModule()141 DustCap *capModule() 142 { 143 return dustCapProcess.get(); 144 } captureModule()145 Capture *captureModule() 146 { 147 return captureProcess.get(); 148 } getSummaryPreview()149 FITSView *getSummaryPreview() 150 { 151 return summaryPreview.get(); 152 } getFilterManager()153 FilterManager *getFilterManager() 154 { 155 return filterManager.data(); 156 } 157 QString getCurrentJobName(); 158 void announceEvent(const QString &message, KSNotification::EventType event); 159 160 /** 161 * @brief addProfile Add a new profile to the database. 162 * @param profileInfo Collection of profile parameters to include the following: 163 * 1. name: Profile name 164 * 2. auto_connect: True of False for Autoconnect? 165 * 3. Mode: "local" or "remote" 166 * 4. remote_host: Optional. remote host (default localhost) 167 * 5. remote_port: Optional. remote port (default 7624) 168 * 6. guiding: 0 for "Internal", 1 for "PHD2", or 2 for "LinGuider" 169 * 7. remote_guiding_host: Optional. remote host for guider application (default localhost) 170 * 8. remote_guide_port: Optional. remote port for guider application. 171 * 9. use_web_manager: True or False? 172 * 10. web_manager_port. Optional. INDI Web Manager port (default 8624) 173 * 12. primary_scope: ID of primary scope to use. This is the ID from OAL::Scope list in the database. 174 * 13. guide_scope: ID of guide scope to use. This is the ID from OAL::Scope list in the database. 175 * 14. mount: Mount driver label (default --). 176 * 15. ccd: CCD driver label (default --). 177 * 16. guider: Guider driver label (default --). 178 * 17. focuser: Focuser driver label (default --). 179 * 18. filter: Filter Wheel driver label (default --). 180 * 19. ao: Adaptive Optics driver label (default --). 181 * 20. dome: Dome driver label (default --). 182 * 21. Weather: Weather station driver label (default --). 183 * 22. aux1: aux1 driver label (default --). 184 * 23. aux2: aux2 driver label (default --). 185 * 24. aux3: aux3 driver label (default --). 186 * 25. aux4: aux4 driver label (default --). 187 */ 188 void addNamedProfile(const QJsonObject &profileInfo); 189 190 /** Same as above, except it edits an existing named profile */ 191 void editNamedProfile(const QJsonObject &profileInfo); 192 193 /** 194 * @brief deleteProfile Delete existing equipment profile 195 * @param name Name of profile 196 * @warning Ekos must be stopped for this to work. It will fail if Ekos is online. 197 */ 198 void deleteNamedProfile(const QString &name); 199 200 /** 201 * @brief getProfile Get a single profile information. 202 * @param name Profile name 203 * @return A JSon object with the detail profile info as described in addProfile function. 204 */ 205 QJsonObject getNamedProfile(const QString &name); 206 207 /** 208 * DBus commands to manage equipment profiles. 209 */ 210 211 /*@{*/ 212 213 /** 214 * DBUS interface function. 215 * set Current device profile. 216 * @param profileName Profile name 217 * @return True if profile is set, false if not found. 218 */ 219 Q_SCRIPTABLE bool setProfile(const QString &profileName); 220 221 /** 222 * DBUS interface function 223 * @brief getProfiles Return a list of all device profiles 224 * @return List of device profiles 225 */ 226 Q_SCRIPTABLE QStringList getProfiles(); 227 228 /** @}*/ 229 230 231 /** 232 * Manager interface provides advanced scripting capabilities to establish and shutdown Ekos services. 233 */ 234 235 /*@{*/ 236 237 /** 238 * DBUS interface function. 239 * @return INDI connection status (0 Idle, 1 Pending, 2 Connected, 3 Error) 240 * @deprecated 241 */ getINDIConnectionStatus()242 Q_SCRIPTABLE unsigned int getINDIConnectionStatus() 243 { 244 return m_indiStatus; 245 } 246 indiStatus()247 Q_SCRIPTABLE Ekos::CommunicationStatus indiStatus() 248 { 249 return m_indiStatus; 250 } 251 252 /** 253 * DBUS interface function. 254 * @return Ekos starting status (0 Idle, 1 Pending, 2 Started, 3 Error) 255 * @deprecated 256 */ getEkosStartingStatus()257 Q_SCRIPTABLE unsigned int getEkosStartingStatus() 258 { 259 return m_ekosStatus; 260 } 261 ekosStatus()262 Q_SCRIPTABLE Ekos::CommunicationStatus ekosStatus() 263 { 264 return m_ekosStatus; 265 } 266 267 /** 268 * DBUS interface function. 269 * @return Settle status (0 Idle, 1 Pending, 2 Started, 3 Error) 270 */ settleStatus()271 Q_SCRIPTABLE Ekos::CommunicationStatus settleStatus() 272 { 273 return m_settleStatus; 274 } 275 276 /** 277 * DBUS interface function. Toggle Ekos logging. 278 * @param name Name of logging to toggle. Available options are: 279 * ** VERBOSE 280 * ** INDI 281 * ** FITS 282 * ** CAPTURE 283 * ** FOCUS 284 * ** GUIDE 285 * ** ALIGNMENT 286 * ** MOUNT 287 * ** SCHEDULER 288 * ** OBSERVATORY 289 * @param enabled True to enable, false otherwise. 290 */ 291 Q_SCRIPTABLE Q_NOREPLY void setEkosLoggingEnabled(const QString &name, bool enabled); 292 293 /** 294 * DBUS interface function. 295 * If connection mode is local, the function first establishes an INDI server with all the specified drivers in Ekos options or as set by the user. For remote connection, 296 * it establishes connection to the remote INDI server. 297 * @return Returns true if server started successful (local mode) or connection to remote server is successful (remote mode). 298 */ 299 Q_SCRIPTABLE void start(); 300 301 /** 302 * DBUS interface function. 303 * If connection mode is local, the function terminates the local INDI server and drivers. For remote, it disconnects from the remote INDI server. 304 */ 305 Q_SCRIPTABLE void stop(); 306 logText()307 Q_SCRIPTABLE QStringList logText() 308 { 309 return m_LogText; 310 } 311 312 Q_SCRIPTABLE bool ekosLiveStatus(); 313 314 /** 315 * DBUS interface function. 316 * @param enabled Connect to EkosLive if true, otherwise disconnect. 317 */ 318 Q_SCRIPTABLE void setEkosLiveConnected(bool enabled); 319 320 /** 321 * @brief setEkosLiveConfig Set EkosLive settings 322 * @param onlineService If true, connect to EkosLive Online Service. Otherwise, EkosLive offline service. 323 * @param rememberCredentials Remember username and password for next session. 324 * @param autoConnect If true, it will automatically connect to EkosLive service. 325 */ 326 Q_SCRIPTABLE void setEkosLiveConfig(bool onlineService, bool rememberCredentials, bool autoConnect); 327 328 /** 329 * @brief setEkosLiveUser Save EkosLive username and password 330 * @param username User name 331 * @param password Password 332 */ 333 Q_SCRIPTABLE void setEkosLiveUser(const QString &username, const QString &password); 334 335 /** 336 * @brief acceptPortSelection Accept current port selection settings in the Selector Dialog 337 */ 338 Q_SCRIPTABLE void acceptPortSelection(); 339 340 signals: 341 // Have to use full Ekos::CommunicationStatus for DBus signal to work 342 void ekosStatusChanged(Ekos::CommunicationStatus status); 343 void indiStatusChanged(Ekos::CommunicationStatus status); 344 void settleStatusChanged(Ekos::CommunicationStatus status); 345 void ekosLiveStatusChanged(bool status); 346 347 void newLog(const QString &text); 348 void newModule(const QString &name); 349 350 protected: 351 void closeEvent(QCloseEvent *event) override; 352 void hideEvent(QHideEvent *) override; 353 void showEvent(QShowEvent *) override; 354 void resizeEvent(QResizeEvent *) override; 355 356 public slots: 357 358 /** 359 * DBUS interface function. 360 * Connects all the INDI devices started by Ekos. 361 */ 362 Q_SCRIPTABLE Q_NOREPLY void connectDevices(); 363 364 /** 365 * DBUS interface function. 366 * Disconnects all the INDI devices started by Ekos. 367 */ 368 Q_SCRIPTABLE Q_NOREPLY void disconnectDevices(); 369 370 /** @}*/ 371 372 void processINDI(); 373 void cleanDevices(bool stopDrivers = true); 374 375 void processNewDevice(ISD::GDInterface *); 376 void processNewProperty(INDI::Property); 377 void processDeleteProperty(const QString &name); 378 379 void processNewNumber(INumberVectorProperty *nvp); 380 void processNewText(ITextVectorProperty *tvp); 381 void processNewSwitch(ISwitchVectorProperty *svp); 382 void processNewLight(ILightVectorProperty *lvp); 383 void processNewBLOB(IBLOB *bvp); 384 385 void restartDriver(const QString &deviceName); 386 387 private slots: 388 389 void changeAlwaysOnTop(Qt::ApplicationState state); 390 391 void showEkosOptions(); 392 393 void updateLog(); 394 void clearLog(); 395 396 void processTabChange(); 397 398 void processServerTerminated(const QString &host, const QString &port); 399 void processServerStarted(const QString &host, const QString &port); 400 401 void removeDevice(ISD::GDInterface *); 402 403 void deviceConnected(); 404 void deviceDisconnected(); 405 406 //void processINDIModeChange(); 407 void checkINDITimeout(); 408 409 // Logs 410 void updateDebugInterfaces(); 411 void watchDebugProperty(ISwitchVectorProperty *svp); 412 413 void setTelescope(ISD::GDInterface *); 414 void setCCD(ISD::GDInterface *); 415 void setFilter(ISD::GDInterface *); 416 void setFocuser(ISD::GDInterface *); 417 void setDome(ISD::GDInterface *); 418 void setWeather(ISD::GDInterface *); 419 void setDustCap(ISD::GDInterface *); 420 void setLightBox(ISD::GDInterface *); 421 void setST4(ISD::ST4 *); 422 423 // Profiles 424 void addProfile(); 425 void editProfile(); 426 void deleteProfile(); 427 void wizardProfile(); 428 429 // Mount Summary 430 void updateMountCoords(const SkyPoint position, ISD::Telescope::PierSide pierSide, const dms &ha); 431 void updateMountStatus(ISD::Telescope::Status status); 432 void setTarget(SkyObject *o); 433 434 // Capture Summary 435 void updateCaptureStatus(CaptureState status); 436 void updateCaptureProgress(SequenceJob *job, const QSharedPointer<FITSData> &data); 437 void updateExposureProgress(SequenceJob *job); 438 void updateCaptureCountDown(); 439 440 // Focus summary 441 void updateFocusStatus(FocusState status); 442 void updateCurrentHFR(double newHFR, int position); getFocusStatusText()443 const QString getFocusStatusText() 444 { 445 return focusManager->focusStatus->text(); 446 } 447 448 // Guide Summary 449 void updateGuideStatus(GuideState status); 450 void updateSigmas(double ra, double de); getGuideStatusText()451 const QString getGuideStatusText() 452 { 453 return guideManager->guideStatus->text(); 454 } 455 456 private: 457 explicit Manager(QWidget *parent); 458 ~Manager() override; 459 460 void removeTabs(); 461 void reset(); 462 void initCapture(); 463 void initFocus(); 464 void initGuide(); 465 void initAlign(); 466 void initMount(); 467 void initDome(); 468 void initWeather(); 469 void initObservatory(Weather *weather, Dome *dome); 470 void initDustCap(); 471 472 void loadDrivers(); 473 void loadProfiles(); 474 int addModuleTab(EkosModule module, QWidget *tab, const QIcon &icon); 475 476 /** 477 * @brief syncActiveDevices Syncs ACTIVE_DEVICES such as ACTIVE_TELESCOPE and ACTIVE_CCD 478 * to the currently detected devices. 479 */ 480 void syncActiveDevices(); 481 482 // Connect Signals/Slots of Ekos modules 483 void connectModules(); 484 485 // Check if INDI server is already running 486 bool isRunning(const QString &process); 487 488 // Find List of devices of specific family type 489 QList<ISD::GDInterface *> findDevices(DeviceFamily type); 490 // Find list of devices by device interface 491 QList<ISD::GDInterface *> findDevicesByInterface(uint32_t interface); 492 // Get all detected devices 493 const QList<ISD::GDInterface *> &getAllDevices() const; 494 495 ProfileInfo *getCurrentProfile(); 496 void getCurrentProfileTelescopeInfo(double &primaryFocalLength, double &primaryAperture, double &guideFocalLength, 497 double &guideAperture); 498 void updateProfileLocation(ProfileInfo *pi); setProfileMapping(const QJsonObject & payload)499 void setProfileMapping(const QJsonObject &payload) 500 { 501 m_ProfileMapping = payload; 502 } 503 // Port Selector Save profile when connect all is pressed 504 void setPortSelectionComplete(); 505 // Check if the driver binary must be one only to avoid duplicate instances 506 // Some driver binaries support multiple devices per binary 507 // so we only need to start a single instance to handle them all. 508 bool checkUniqueBinaryDriver(DriverInfo * primaryDriver, DriverInfo * secondaryDriver); 509 510 bool useGuideHead { false }; 511 bool useST4 { false }; 512 513 // Containers 514 515 // All Drivers 516 QHash<QString, DriverInfo *> driversList; 517 518 // All managed drivers 519 QList<DriverInfo *> managedDrivers; 520 521 // All generic devices (i.e. those define by INDI server) 522 QList<ISD::GDInterface *> genericDevices; 523 524 // All proxy devices (generated devices by Ekos Manager for specific interfaces) 525 QList<ISD::GDInterface *> proxyDevices; 526 527 // All Managed devices (ie. those explicitly defined in the profile) 528 QMap<DeviceFamily, ISD::GDInterface *> managedDevices; 529 530 // Smart pointers for the various Ekos Modules 531 std::unique_ptr<Capture> captureProcess; 532 std::unique_ptr<Focus> focusProcess; 533 std::unique_ptr<Guide> guideProcess; 534 std::unique_ptr<Align> alignProcess; 535 std::unique_ptr<Mount> mountProcess; 536 std::unique_ptr<Analyze> analyzeProcess; 537 std::unique_ptr<Scheduler> schedulerProcess; 538 std::unique_ptr<Observatory> observatoryProcess; 539 std::unique_ptr<Dome> domeProcess; 540 std::unique_ptr<Weather> weatherProcess; 541 std::unique_ptr<DustCap> dustCapProcess; 542 std::unique_ptr<EkosLive::Client> ekosLiveClient; 543 544 bool m_LocalMode { true }; 545 bool m_isStarted { false }; 546 bool m_RemoteManagerStart { false }; 547 548 int nDevices { 0 }; 549 550 QStringList m_LogText; 551 KPageWidgetItem *ekosOptionsWidget { nullptr }; 552 553 CommunicationStatus m_ekosStatus { Ekos::Idle }; 554 CommunicationStatus m_indiStatus { Ekos::Idle }; 555 // Settle is used to know once all properties from all devices have been defined 556 // There is no way to know this for sure so we use a debounace mechanism. 557 CommunicationStatus m_settleStatus { Ekos::Idle }; 558 559 std::unique_ptr<QStandardItemModel> profileModel; 560 QList<std::shared_ptr<ProfileInfo>> profiles; 561 QJsonObject m_ProfileMapping; 562 563 // Filter Manager 564 QSharedPointer<FilterManager> filterManager; 565 566 // Mount Summary 567 QPointer<QProcess> indiHubAgent; 568 569 // Capture Summary 570 QTimer m_CountdownTimer; 571 QTimer settleTimer; 572 // Preview Frame 573 std::unique_ptr<SummaryFITSView> summaryPreview; 574 575 ProfileInfo *currentProfile { nullptr }; 576 bool profileWizardLaunched { false }; 577 578 // Port Selector 579 std::unique_ptr<Selector::Dialog> m_PortSelector; 580 QTimer m_PortSelectorTimer; 581 582 // Logs 583 QPointer<OpsLogs> opsLogs; 584 585 // E.g. Setup, Scheduler, and Analyze. 586 int numPermanentTabs { 0 }; 587 588 friend class EkosLive::Client; 589 friend class EkosLive::Message; 590 friend class EkosLive::Media; 591 592 static Manager *_Manager; 593 }; 594 595 } 596