File indexing completed on 2024-07-21 09:21:30

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #ifdef USE_QT5_INDI
0010 #include <baseclientqt.h>
0011 #else
0012 #include <baseclient.h>
0013 #endif
0014 
0015 #include "ui_manager.h"
0016 
0017 #include "ekos.h"
0018 #include "fitsviewer/summaryfitsview.h"
0019 #include "indi/indistd.h"
0020 #include "auxiliary/portselector.h"
0021 #include "ksnotification.h"
0022 #include "auxiliary/opslogs.h"
0023 #include "ekos/capture/rotatorsettings.h"
0024 
0025 #include <QDialog>
0026 #include <QHash>
0027 
0028 #include <memory>
0029 
0030 //! Generic record interfaces and implementations.
0031 namespace EkosLive
0032 {
0033 class Client;
0034 class Message;
0035 class Media;
0036 }
0037 
0038 class DriverInfo;
0039 class ProfileInfo;
0040 class KPageWidgetItem;
0041 class OpsEkos;
0042 
0043 /**
0044  * @class Manager
0045  * @short Primary class to handle all Ekos modules.
0046  * 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.
0047  * \defgroup EkosDBusInterface "Ekos DBus Interface" provides high level functions to control devices and Ekos modules for a total robotic operation:
0048  * <ul>
0049  * <li>\ref CaptureDBusInterface "Capture Module DBus Interface"</li>
0050  * <li>\ref FocusDBusInterface "Focus Module DBus Interface"</li>
0051  * <li>\ref MountDBusInterface "Mount Module DBus Interface"</li>
0052  * <li>\ref GuideDBusInterface "Guide Module DBus Interface"</li>
0053  * <li>\ref AlignDBusInterface "Align Module DBus Interface"</li>
0054  * <li>\ref WeatherDBusInterface "Weather DBus Interface"</li>
0055  * <li>\ref DustCapDBusInterface "Dust Cap DBus Interface"</li>
0056  * </ul>
0057  *  For low level access to INDI devices, the \ref INDIDBusInterface "INDI Dbus Interface" provides complete access to INDI devices and properties.
0058  *  Ekos Manager provides a summary of operations progress in the <i>Summary</i> section of the <i>Setup</i> tab.
0059  *
0060  * @author Jasem Mutlaq
0061  * @version 1.8
0062  */
0063 namespace Ekos
0064 {
0065 
0066 class Analyze;
0067 class Capture;
0068 class Scheduler;
0069 class Focus;
0070 class Align;
0071 class Guide;
0072 class Mount;
0073 class Observatory;
0074 // class RotatorSettings;
0075 
0076 class Manager : public QDialog, public Ui::Manager
0077 {
0078         Q_OBJECT
0079         Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos")
0080 
0081         Q_SCRIPTABLE Q_PROPERTY(Ekos::CommunicationStatus indiStatus READ indiStatus NOTIFY indiStatusChanged)
0082         Q_SCRIPTABLE Q_PROPERTY(Ekos::CommunicationStatus ekosStatus READ ekosStatus NOTIFY ekosStatusChanged)
0083         Q_SCRIPTABLE Q_PROPERTY(Ekos::CommunicationStatus settleStatus READ settleStatus NOTIFY settleStatusChanged)
0084         Q_SCRIPTABLE Q_PROPERTY(bool ekosLiveStatus READ ekosLiveStatus NOTIFY ekosLiveStatusChanged)
0085         Q_SCRIPTABLE Q_PROPERTY(QStringList logText READ logText NOTIFY newLog)
0086 
0087         enum class EkosModule
0088         {
0089             Setup,
0090             Scheduler,
0091             Analyze,
0092             Capture,
0093             Focus,
0094             Mount,
0095             Align,
0096             Guide,
0097             Observatory,
0098         };
0099     public:
0100         static Manager *Instance();
0101         static void release();
0102 
0103         // No OP
0104         void initialize() {}
0105 
0106         void appendLogText(const QString &);
0107         void setOptionsWidget(KPageWidgetItem *ops, OpsEkos *opsEkosPtr)
0108         {
0109             ekosOptionsWidget = ops;
0110             opsEkos = opsEkosPtr;
0111         }
0112         void addObjectToScheduler(SkyObject *object);
0113 
0114         Scheduler *schedulerModule()
0115         {
0116             return schedulerProcess.get();
0117         }
0118         Guide *guideModule()
0119         {
0120             return guideProcess.get();
0121         }
0122         Align *alignModule()
0123         {
0124             return alignProcess.get();
0125         }
0126         Mount *mountModule()
0127         {
0128             return mountProcess.get();
0129         }
0130         Focus *focusModule()
0131         {
0132             return focusProcess.get();
0133         }
0134         Capture *captureModule()
0135         {
0136             return captureProcess.get();
0137         }
0138         FITSView *getSummaryPreview()
0139         {
0140             return m_SummaryView.get();
0141         }
0142 
0143         // Filter Manager
0144         void createFilterManager(ISD::FilterWheel *device);
0145         bool getFilterManager(const QString &name, QSharedPointer<FilterManager> &fm);
0146         bool getFilterManager(QSharedPointer<FilterManager> &fm);
0147 
0148         // Rotator Control
0149         void createRotatorController(ISD::Rotator *device);
0150         bool getRotatorController(const QString &Name, QSharedPointer<RotatorSettings> &rs);
0151         bool existRotatorController();
0152 
0153         QString getCurrentJobName();
0154         void announceEvent(const QString &message, KSNotification::EventSource source, KSNotification::EventType event);
0155 
0156         /**
0157          * @brief activateModule Switch tab to specific module name (i.e. CCD) and raise Ekos screen to focus.
0158          * @param name module name CCD, Guide, Focus, Mount, Scheduler, or Observatory.
0159          * @param popup if True, show Ekos Manager window in the foreground.
0160          */
0161         void activateModule(const QString &name, bool popup = false);
0162 
0163         /**
0164          * @brief addProfile Add a new profile to the database.
0165          * @param profileInfo Collection of profile parameters to include the following:
0166          * 1. name: Profile name
0167          * 2. auto_connect: True of False for Autoconnect?
0168          * 3. Mode: "local" or "remote"
0169          * 4. remote_host: Optional. remote host (default localhost)
0170          * 5. remote_port: Optional. remote port (default 7624)
0171          * 6. guiding: 0 for "Internal", 1 for "PHD2", or 2 for "LinGuider"
0172          * 7. remote_guiding_host: Optional. remote host for guider application (default localhost)
0173          * 8. remote_guide_port: Optional. remote port for guider application.
0174          * 9. use_web_manager: True or False?
0175          * 10. web_manager_port. Optional. INDI Web Manager port (default 8624)
0176          * 12. primary_scope: ID of primary scope to use. This is the ID from OAL::Scope list in the database.
0177          * 13. guide_scope: ID of guide scope to use. This is the ID from OAL::Scope list in the database.
0178          * 14. mount: Mount driver label (default --).
0179          * 15. ccd: CCD driver label (default --).
0180          * 16. guider: Guider driver label (default --).
0181          * 17. focuser: Focuser driver label (default --).
0182          * 18. filter: Filter Wheel driver label (default --).
0183          * 19. ao: Adaptive Optics driver label (default --).
0184          * 20. dome: Dome driver label (default --).
0185          * 21. Weather: Weather station driver label (default --).
0186          * 22. aux1: aux1 driver label (default --).
0187          * 23. aux2: aux2 driver label (default --).
0188          * 24. aux3: aux3 driver label (default --).
0189          * 25. aux4: aux4 driver label (default --).
0190          */
0191         void addNamedProfile(const QJsonObject &profileInfo);
0192 
0193         /** Same as above, except it edits an existing named profile */
0194         void editNamedProfile(const QJsonObject &profileInfo);
0195 
0196         /**
0197          * @brief deleteProfile Delete existing equipment profile
0198          * @param name Name of profile
0199          * @warning Ekos must be stopped for this to work. It will fail if Ekos is online.
0200          */
0201         void deleteNamedProfile(const QString &name);
0202 
0203         /**
0204          * @brief getProfile Get a single profile information.
0205          * @param name Profile name
0206          * @return A JSon object with the detail profile info as described in addProfile function.
0207          */
0208         QJsonObject getNamedProfile(const QString &name);
0209 
0210         /**
0211          * DBus commands to manage equipment profiles.
0212          */
0213 
0214         /*@{*/
0215 
0216         /**
0217          * DBUS interface function.
0218          * set Current device profile.
0219          * @param profileName Profile name
0220          * @return True if profile is set, false if not found.
0221          */
0222         Q_SCRIPTABLE bool setProfile(const QString &profileName);
0223 
0224         /**
0225          * DBUS interface function
0226          * @brief getProfiles Return a list of all device profiles
0227          * @return List of device profiles
0228          */
0229         Q_SCRIPTABLE QStringList getProfiles();
0230 
0231         /** @}*/
0232 
0233 
0234         /**
0235          * Manager interface provides advanced scripting capabilities to establish and shutdown Ekos services.
0236          */
0237 
0238         /*@{*/
0239 
0240         /**
0241          * DBUS interface function.
0242          * @return INDI connection status (0 Idle, 1 Pending, 2 Connected, 3 Error)
0243          * @deprecated
0244          */
0245         Q_SCRIPTABLE unsigned int getINDIConnectionStatus()
0246         {
0247             return m_indiStatus;
0248         }
0249 
0250         Q_SCRIPTABLE Ekos::CommunicationStatus indiStatus()
0251         {
0252             return m_indiStatus;
0253         }
0254 
0255         /**
0256          * DBUS interface function.
0257          * @return Ekos starting status (0 Idle, 1 Pending, 2 Started, 3 Error)
0258          * @deprecated
0259          */
0260         Q_SCRIPTABLE unsigned int getEkosStartingStatus()
0261         {
0262             return m_ekosStatus;
0263         }
0264 
0265         Q_SCRIPTABLE Ekos::CommunicationStatus ekosStatus()
0266         {
0267             return m_ekosStatus;
0268         }
0269 
0270         /**
0271          * DBUS interface function.
0272          * @return Settle status (0 Idle, 1 Pending, 2 Started, 3 Error)
0273          */
0274         Q_SCRIPTABLE Ekos::CommunicationStatus settleStatus()
0275         {
0276             return m_settleStatus;
0277         }
0278 
0279         /**
0280          * DBUS interface function. Toggle Ekos logging.
0281          * @param name Name of logging to toggle. Available options are:
0282          * ** VERBOSE
0283          * ** INDI
0284          * ** FITS
0285          * ** CAPTURE
0286          * ** FOCUS
0287          * ** GUIDE
0288          * ** ALIGNMENT
0289          * ** MOUNT
0290          * ** SCHEDULER
0291          * ** OBSERVATORY
0292          * @param enabled True to enable, false otherwise.
0293          */
0294         Q_SCRIPTABLE Q_NOREPLY void setEkosLoggingEnabled(const QString &name, bool enabled);
0295 
0296         /**
0297          * DBUS interface function.
0298          * 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,
0299          * it establishes connection to the remote INDI server.
0300          * @return Returns true if server started successful (local mode) or connection to remote server is successful (remote mode).
0301          */
0302         Q_SCRIPTABLE void start();
0303 
0304         /**
0305          * DBUS interface function.
0306          * If connection mode is local, the function terminates the local INDI server and drivers. For remote, it disconnects from the remote INDI server.
0307          */
0308         Q_SCRIPTABLE void stop();
0309 
0310         Q_SCRIPTABLE QStringList logText()
0311         {
0312             return m_LogText;
0313         }
0314 
0315         Q_SCRIPTABLE bool ekosLiveStatus();
0316 
0317         /**
0318          * DBUS interface function.
0319          * @param enabled Connect to EkosLive if true, otherwise disconnect.
0320         */
0321         Q_SCRIPTABLE void setEkosLiveConnected(bool enabled);
0322 
0323         /**
0324          * @brief setEkosLiveConfig Set EkosLive settings
0325          * @param rememberCredentials Remember username and password for next session.
0326          * @param autoConnect If true, it will automatically connect to EkosLive service.
0327          */
0328         Q_SCRIPTABLE void setEkosLiveConfig(bool rememberCredentials, bool autoConnect);
0329 
0330         /**
0331          * @brief setEkosLiveUser Save EkosLive username and password
0332          * @param username User name
0333          * @param password Password
0334          */
0335         Q_SCRIPTABLE void setEkosLiveUser(const QString &username, const QString &password);
0336 
0337         /**
0338          * @brief acceptPortSelection Accept current port selection settings in the Selector Dialog
0339          */
0340         Q_SCRIPTABLE void acceptPortSelection();
0341 
0342     signals:
0343         // Have to use full Ekos::CommunicationStatus for DBus signal to work
0344         void ekosStatusChanged(Ekos::CommunicationStatus status);
0345         void indiStatusChanged(Ekos::CommunicationStatus status);
0346         void settleStatusChanged(Ekos::CommunicationStatus status);
0347         void ekosLiveStatusChanged(bool status);
0348 
0349         void newLog(const QString &text);
0350         void newModule(const QString &name);
0351         void newDevice(const QString &name, int interface);
0352 
0353     protected:
0354         void closeEvent(QCloseEvent *event) override;
0355         void hideEvent(QHideEvent *) override;
0356         void showEvent(QShowEvent *) override;
0357         void resizeEvent(QResizeEvent *) override;
0358 
0359     public slots:
0360 
0361         /**
0362          * DBUS interface function.
0363          * Connects all the INDI devices started by Ekos.
0364          */
0365         Q_SCRIPTABLE Q_NOREPLY void connectDevices();
0366 
0367         /**
0368          * DBUS interface function.
0369          * Disconnects all the INDI devices started by Ekos.
0370          */
0371         Q_SCRIPTABLE Q_NOREPLY void disconnectDevices();
0372 
0373         /** @}*/
0374 
0375         void processINDI();
0376         void cleanDevices(bool stopDrivers = true);
0377 
0378         void processNewDevice(const QSharedPointer<ISD::GenericDevice> &device);
0379 
0380         void processNewProperty(INDI::Property);
0381         void processUpdateProperty(INDI::Property);
0382         void processDeleteProperty(INDI::Property);
0383         void processMessage(int id);
0384 
0385 
0386         void setDeviceReady();
0387 
0388         void restartDriver(const QString &deviceName);
0389 
0390     private slots:
0391 
0392         void changeAlwaysOnTop(Qt::ApplicationState state);
0393 
0394         void showEkosOptions();
0395 
0396         void updateLog();
0397         void clearLog();
0398 
0399         void processTabChange();
0400 
0401         void setServerStarted(const QString &host, int port);
0402         void setServerFailed(const QString &host, int port, const QString &message);
0403         //void setServerTerminated(const QString &host, int port, const QString &message);
0404 
0405         void setClientStarted(const QString &host, int port);
0406         void setClientFailed(const QString &host, int port, const QString &message);
0407         void setClientTerminated(const QString &host, int port, const QString &message);
0408 
0409         void removeDevice(const QSharedPointer<ISD::GenericDevice> &device);
0410 
0411         void deviceConnected();
0412         void deviceDisconnected();
0413 
0414         //void processINDIModeChange();
0415         void checkINDITimeout();
0416 
0417         // Logs
0418         void updateDebugInterfaces();
0419         void watchDebugProperty(INDI::Property prop);
0420 
0421         void addMount(ISD::Mount *device);
0422         void addCamera(ISD::Camera *device);
0423         void addFilterWheel(ISD::FilterWheel *device);
0424         void addFocuser(ISD::Focuser *device);
0425         void addRotator(ISD::Rotator *device);
0426         void addDome(ISD::Dome *device);
0427         void addWeather(ISD::Weather *device);
0428         void addDustCap(ISD::DustCap *device);
0429         void addLightBox(ISD::LightBox *device);
0430         void addGuider(ISD::Guider *device);
0431         void addGPS(ISD::GPS *device);
0432 
0433         /**
0434          * @brief syncGenericDevice Check if this device needs to be added to any Ekos module.
0435          * @param device pointer to generic device.
0436          */
0437         void syncGenericDevice(const QSharedPointer<ISD::GenericDevice> &device);
0438         void createModules(const QSharedPointer<ISD::GenericDevice> &device);
0439 
0440         // Profiles
0441         void addProfile();
0442         void editProfile();
0443         void deleteProfile();
0444         void wizardProfile();
0445 
0446         // Mount Summary
0447         void updateMountCoords(const SkyPoint position, ISD::Mount::PierSide pierSide, const dms &ha);
0448         void updateMountStatus(ISD::Mount::Status status);
0449         void setTarget(const QString &name);
0450 
0451         // Capture Summary
0452         void updateCaptureStatus(CaptureState status);
0453         void updateCaptureProgress(SequenceJob *job, const QSharedPointer<FITSData> &data);
0454         void updateExposureProgress(SequenceJob *job);
0455         void updateCaptureCountDown();
0456 
0457         // Focus summary
0458         void updateFocusStatus(FocusState status);
0459         void updateCurrentHFR(double newHFR, int position, bool inAutofocus);
0460 
0461         // Guide Summary
0462         void updateGuideStatus(GuideState status);
0463         void updateSigmas(double ra, double de);
0464 
0465     private:
0466         explicit Manager(QWidget *parent);
0467         ~Manager() override;
0468 
0469         void removeTabs();
0470         void reset();
0471         void initCapture();
0472         void initFocus();
0473         void initGuide();
0474         void initAlign();
0475         void initMount();
0476         void initObservatory();
0477 
0478         void loadDrivers();
0479         void loadProfiles();
0480         int addModuleTab(EkosModule module, QWidget *tab, const QIcon &icon);
0481 
0482         /**
0483          * @brief syncActiveDevices Syncs ACTIVE_DEVICES such as ACTIVE_TELESCOPE and ACTIVE_CCD
0484          * to the currently detected devices.
0485          */
0486         void syncActiveDevices();
0487 
0488         /**
0489          * @brief isINDIReady Check whether all INDI devices are connected and ready and emit signals accordingly.
0490          * @return True if all INDI devices are connected and ready.
0491          */
0492         bool isINDIReady();
0493 
0494         // Connect Signals/Slots of Ekos modules
0495         void connectModules();
0496 
0497         // Check if INDI server is already running
0498         bool isRunning(const QString &process);
0499 
0500         bool getCurrentProfile(QSharedPointer<ProfileInfo> &profile) const;
0501         void updateProfileLocation(const QSharedPointer<ProfileInfo> &profile);
0502         void setProfileMapping(const QJsonObject &payload)
0503         {
0504             m_ProfileMapping = payload;
0505         }
0506         // Port Selector Save profile when connect all is pressed
0507         void setPortSelectionComplete();
0508         // Check if the driver binary must be one only to avoid duplicate instances
0509         // Some driver binaries support multiple devices per binary
0510         // so we only need to start a single instance to handle them all.
0511         bool checkUniqueBinaryDriver(const QSharedPointer<DriverInfo> &primaryDriver,
0512                                      const QSharedPointer<DriverInfo> &secondaryDriver);
0513 
0514         // Containers
0515 
0516         // All Drivers
0517         QHash<QString, QSharedPointer<DriverInfo>> driversList;
0518 
0519         // All managed drivers
0520         QList<QSharedPointer<DriverInfo>> managedDrivers;
0521 
0522         // Smart pointers for the various Ekos Modules
0523         std::unique_ptr<Capture> captureProcess;
0524         std::unique_ptr<Focus> focusProcess;
0525         std::unique_ptr<Guide> guideProcess;
0526         std::unique_ptr<Align> alignProcess;
0527         std::unique_ptr<Mount> mountProcess;
0528         std::unique_ptr<Analyze> analyzeProcess;
0529         std::unique_ptr<Scheduler> schedulerProcess;
0530         std::unique_ptr<Observatory> observatoryProcess;
0531         std::unique_ptr<EkosLive::Client> ekosLiveClient;
0532 
0533         bool m_LocalMode { true };
0534         bool m_isStarted { false };
0535         bool m_RemoteManagerStart { false };
0536 
0537         int m_DriverDevicesCount { 0 };
0538 
0539         QStringList m_LogText;
0540         KPageWidgetItem *ekosOptionsWidget { nullptr };
0541         OpsEkos *opsEkos { nullptr };
0542 
0543         CommunicationStatus m_ekosStatus { Ekos::Idle };
0544         CommunicationStatus m_indiStatus { Ekos::Idle };
0545         // Settle is used to know once all properties from all devices have been defined
0546         // There is no way to know this for sure so we use a debounace mechanism.
0547         CommunicationStatus m_settleStatus { Ekos::Idle };
0548 
0549         std::unique_ptr<QStandardItemModel> profileModel;
0550         QList<QSharedPointer<ProfileInfo>> profiles;
0551         QJsonObject m_ProfileMapping;
0552 
0553         // Mount Summary
0554         QPointer<QProcess> indiHubAgent;
0555         KLed *mountMotionState { nullptr };
0556 
0557 
0558         // Capture Summary
0559         QTimer m_CountdownTimer;
0560         QTimer settleTimer;
0561         // Preview Frame
0562         QSharedPointer<SummaryFITSView> m_SummaryView;
0563 
0564         QSharedPointer<ProfileInfo> m_CurrentProfile;
0565         bool profileWizardLaunched { false };
0566         QString m_PrimaryCamera, m_GuideCamera;
0567 
0568         // Port Selector
0569         std::unique_ptr<Selector::Dialog> m_PortSelector;
0570         QTimer m_PortSelectorTimer;
0571 
0572         QMap<QString, QSharedPointer<FilterManager>> m_FilterManagers;
0573         QMap<QString, QSharedPointer<RotatorSettings>> m_RotatorControllers;
0574 
0575         // Logs
0576         QPointer<OpsLogs> opsLogs;
0577 
0578         // E.g. Setup, Scheduler, and Analyze.
0579         int numPermanentTabs { 0 };
0580 
0581         friend class EkosLive::Client;
0582         friend class EkosLive::Message;
0583         friend class EkosLive::Media;
0584 
0585         static Manager *_Manager;
0586 };
0587 
0588 }