diff --git a/kstars/ekos/auxiliary/dome.cpp b/kstars/ekos/auxiliary/dome.cpp index 9c9ea8b0a..598251b5d 100644 --- a/kstars/ekos/auxiliary/dome.cpp +++ b/kstars/ekos/auxiliary/dome.cpp @@ -1,179 +1,210 @@ /* Ekos Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "dome.h" #include "domeadaptor.h" #include "ekos/manager.h" #include "indi/driverinfo.h" #include "indi/clientmanager.h" #include "kstars.h" #include namespace Ekos { Dome::Dome() { new DomeAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Dome", this); currentDome = nullptr; } void Dome::setDome(ISD::GDInterface *newDome) { if (newDome == currentDome) return; currentDome = static_cast(newDome); currentDome->disconnect(this); connect(currentDome, &ISD::Dome::newStatus, this, &Dome::newStatus); connect(currentDome, &ISD::Dome::newParkStatus, this, &Dome::newParkStatus); connect(currentDome, &ISD::Dome::newParkStatus, [&](ISD::ParkStatus status) {m_ParkStatus = status;}); connect(currentDome, &ISD::Dome::newShutterStatus, this, &Dome::newShutterStatus); connect(currentDome, &ISD::Dome::newShutterStatus, [&](ISD::Dome::ShutterStatus status) {m_ShutterStatus = status;}); + connect(currentDome, &ISD::Dome::newAutoSyncStatus, this, &Dome::newAutoSyncStatus); connect(currentDome, &ISD::Dome::azimuthPositionChanged, this, &Dome::azimuthPositionChanged); connect(currentDome, &ISD::Dome::ready, this, &Dome::ready); connect(currentDome, &ISD::Dome::Disconnected, this, &Dome::disconnected); } void Dome::setTelescope(ISD::GDInterface *newTelescope) { if (currentDome == nullptr) return; ITextVectorProperty *activeDevices = currentDome->getBaseDevice()->getText("ACTIVE_DEVICES"); if (activeDevices) { IText *activeTelescope = IUFindText(activeDevices, "ACTIVE_TELESCOPE"); if (activeTelescope) { IUSaveText(activeTelescope, newTelescope->getDeviceName()); currentDome->getDriverInfo()->getClientManager()->sendNewText(activeDevices); } } } bool Dome::canPark() { if (currentDome == nullptr) return false; return currentDome->canPark(); } bool Dome::park() { if (currentDome == nullptr || currentDome->canPark() == false) return false; return currentDome->Park(); } bool Dome::unpark() { if (currentDome == nullptr || currentDome->canPark() == false) return false; return currentDome->UnPark(); } bool Dome::abort() { if (currentDome == nullptr) return false; return currentDome->Abort(); } bool Dome::isMoving() { if (currentDome == nullptr) return false; return currentDome->isMoving(); } bool Dome::canAbsoluteMove() { if (currentDome) return currentDome->canAbsMove(); return false; } +bool Dome::canRelativeMove() +{ + if (currentDome) + return currentDome->canRelMove(); + + return false; +} + double Dome::azimuthPosition() { if (currentDome) return currentDome->azimuthPosition(); return -1; } void Dome::setAzimuthPosition(double position) { if (currentDome) currentDome->setAzimuthPosition(position); } +void Dome::setRelativePosition(double position) +{ + if (currentDome) + currentDome->setRelativePosition(position); +} + +bool Dome::isAutoSync() +{ + if (currentDome) + return currentDome->isAutoSync(); + // value could not be determined + return false; +} + +bool Dome::setAutoSync(bool activate) +{ + if (currentDome) + return currentDome->setAutoSync(activate); + // not succeeded + return false; +} + bool Dome::hasShutter() { if (currentDome) return currentDome->hasShutter(); // no dome, no shutter return false; } bool Dome::controlShutter(bool open) { if (currentDome) return currentDome->ControlShutter(open); // no dome, no shutter control return false; } #if 0 Dome::ParkingStatus Dome::getParkingStatus() { if (currentDome == nullptr || currentDome->canPark() == false) return PARKING_ERROR; ISwitchVectorProperty *parkSP = currentDome->getBaseDevice()->getSwitch("DOME_PARK"); if (parkSP == nullptr) return PARKING_ERROR; switch (parkSP->s) { case IPS_IDLE: return PARKING_IDLE; case IPS_OK: if (parkSP->sp[0].s == ISS_ON) return PARKING_OK; else return UNPARKING_OK; case IPS_BUSY: if (parkSP->sp[0].s == ISS_ON) return PARKING_BUSY; else return UNPARKING_BUSY; case IPS_ALERT: return PARKING_ERROR; } return PARKING_ERROR; } #endif } diff --git a/kstars/ekos/auxiliary/dome.h b/kstars/ekos/auxiliary/dome.h index a89b4de60..c20fa931c 100644 --- a/kstars/ekos/auxiliary/dome.h +++ b/kstars/ekos/auxiliary/dome.h @@ -1,131 +1,142 @@ /* Ekos Dome interface Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "indi/indistd.h" #include "indi/indidome.h" #include namespace Ekos { /** * @class Dome * @short Supports basic dome functions * * @author Jasem Mutlaq * @version 1.0 */ class Dome : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Dome") Q_PROPERTY(ISD::Dome::Status status READ status NOTIFY newStatus) Q_PROPERTY(ISD::ParkStatus parkStatus READ parkStatus NOTIFY newParkStatus) Q_PROPERTY(bool canPark READ canPark) Q_PROPERTY(bool canAbsoluteMove READ canAbsoluteMove) + Q_PROPERTY(bool canRelativeMove READ canRelativeMove) Q_PROPERTY(bool isMoving READ isMoving) Q_PROPERTY(double azimuthPosition READ azimuthPosition WRITE setAzimuthPosition NOTIFY azimuthPositionChanged) public: Dome(); virtual ~Dome() override = default; /** * @defgroup DomeDBusInterface Ekos DBus Interface - Dome Interface * Ekos::Dome interface provides advanced basic dome operations. */ /*@{*/ /** * DBUS interface function. * Abort dome */ Q_SCRIPTABLE bool abort(); /** * DBUS interface function. * Can dome park? */ Q_SCRIPTABLE bool canPark(); /** * DBUS interface function. * Can dome move to an absolute azimuth position? */ Q_SCRIPTABLE bool canAbsoluteMove(); + /** + * DBUS interface function. + * Can dome move to an relative azimuth position? + */ + Q_SCRIPTABLE bool canRelativeMove(); /** * DBUS interface function. * Park dome */ Q_SCRIPTABLE bool park(); /** * DBUS interface function. * Park dome */ Q_SCRIPTABLE bool unpark(); /** * DBUS interface function. * Get the dome park status */ //Q_SCRIPTABLE ParkingStatus getParkingStatus(); /** * DBUS interface function. * Check if the dome is in motion */ Q_SCRIPTABLE bool isMoving(); Q_SCRIPTABLE double azimuthPosition(); Q_SCRIPTABLE void setAzimuthPosition(double position); + Q_SCRIPTABLE void setRelativePosition(double position); + + Q_SCRIPTABLE bool isAutoSync(); + Q_SCRIPTABLE bool setAutoSync(bool activate); Q_SCRIPTABLE bool hasShutter(); Q_SCRIPTABLE bool controlShutter(bool open); /** @}*/ /** * @brief setDome set the dome device * @param newDome pointer to Dome device. */ void setDome(ISD::GDInterface *newDome); /** * @brief setTelescope Set the telescope device. This is only used to sync ACTIVE_TELESCOPE to the current active telescope. * @param newTelescope pointer to telescope device. */ void setTelescope(ISD::GDInterface *newTelescope); ISD::Dome::Status status() { return currentDome->status(); } ISD::Dome::ShutterStatus shutterStatus() { return currentDome->shutterStatus(); } ISD::ParkStatus parkStatus() { return m_ParkStatus; } signals: void newStatus(ISD::Dome::Status status); void newParkStatus(ISD::ParkStatus status); void newShutterStatus(ISD::Dome::ShutterStatus status); + void newAutoSyncStatus(bool enabled); void azimuthPositionChanged(double position); void ready(); // Signal when the underlying ISD::Dome signals a Disconnected() void disconnected(); private: // Devices needed for Dome operation ISD::Dome *currentDome { nullptr }; ISD::ParkStatus m_ParkStatus { ISD::PARK_UNKNOWN }; ISD::Dome::ShutterStatus m_ShutterStatus { ISD::Dome::SHUTTER_UNKNOWN }; }; } diff --git a/kstars/ekos/observatory/observatory.cpp b/kstars/ekos/observatory/observatory.cpp index 723f8f162..e4338992d 100644 --- a/kstars/ekos/observatory/observatory.cpp +++ b/kstars/ekos/observatory/observatory.cpp @@ -1,396 +1,470 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "observatory.h" #include "ekos_observatory_debug.h" namespace Ekos { Observatory::Observatory() { setupUi(this); // status control mObservatoryModel = new ObservatoryModel(); setObseratoryStatusControl(mObservatoryModel->statusControl()); // update UI for status control connect(useDomeCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(useShutterCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(useWeatherCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(mObservatoryModel, &Ekos::ObservatoryModel::newStatus, this, &Ekos::Observatory::observatoryStatusChanged); // ready button deactivated // connect(statusReadyButton, &QPushButton::clicked, mObservatoryModel, &Ekos::ObservatoryModel::makeReady); statusReadyButton->setEnabled(false); setDomeModel(new ObservatoryDomeModel()); setWeatherModel(new ObservatoryWeatherModel()); statusDefinitionBox->setVisible(true); statusDefinitionBox->setEnabled(true); // make invisible, since not implemented yet - angleLabel->setVisible(false); - domeAngleSpinBox->setVisible(false); - setDomeAngleButton->setVisible(false); weatherWarningSchedulerCB->setVisible(false); weatherAlertSchedulerCB->setVisible(false); + motionCWButton->setVisible(false); + motionCCWButton->setVisible(false); } void Observatory::setObseratoryStatusControl(ObservatoryStatusControl control) { if (mObservatoryModel != nullptr) { useDomeCB->setChecked(control.useDome); useShutterCB->setChecked(control.useShutter); useWeatherCB->setChecked(control.useWeather); } } void Observatory::setDomeModel(ObservatoryDomeModel *model) { mObservatoryModel->setDomeModel(model); if (model != nullptr) { connect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome); connect(model, &Ekos::ObservatoryDomeModel::disconnected, this, &Ekos::Observatory::shutdownDome); connect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus); connect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus); + connect(model, &Ekos::ObservatoryDomeModel::azimuthPositionChanged, this, &Ekos::Observatory::domeAzimuthChanged); + connect(model, &Ekos::ObservatoryDomeModel::newAutoSyncStatus, this, &Ekos::Observatory::showAutoSync); + // motion controls + connect(motionMoveAbsButton, &QCheckBox::clicked, [this]() + { + mObservatoryModel->getDomeModel()->setAzimuthPosition(absoluteMotionSB->value()); + }); + + connect(motionMoveRelButton, &QCheckBox::clicked, [this]() + { + mObservatoryModel->getDomeModel()->setRelativePosition(relativeMotionSB->value()); + }); + + // abort button + connect(motionAbortButton, &QPushButton::clicked, model, &ObservatoryDomeModel::abort); + + // weather controls connect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); connect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherAlertSettingsChanged(); }); } else { shutdownDome(); - // JM 2019-06-11: You cannot disconnect a nullptr here. - // disconnect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus); - // disconnect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus); - // disconnect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome); - // disconnect(model, &Ekos::ObservatoryDomeModel::disconnected, this, &Ekos::Observatory::shutdownDome); - disconnect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); disconnect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); disconnect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); disconnect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); } } void Observatory::initDome() { domeBox->setEnabled(true); if (getDomeModel() != nullptr) { connect(getDomeModel(), &Ekos::ObservatoryDomeModel::newLog, this, &Ekos::Observatory::appendLogText); if (getDomeModel()->canPark()) { connect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); connect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); domePark->setEnabled(true); domeUnpark->setEnabled(true); } else { domePark->setEnabled(false); domeUnpark->setEnabled(false); } + // initialize the dome motion controls + domeAzimuthChanged(getDomeModel()->azimuthPosition()); + if (getDomeModel()->hasShutter()) { shutterBox->setVisible(true); shutterBox->setEnabled(true); connect(shutterOpen, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::openShutter); connect(shutterClosed, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::closeShutter); shutterClosed->setEnabled(true); shutterOpen->setEnabled(true); } else { shutterBox->setVisible(false); weatherWarningShutterCB->setVisible(false); weatherAlertShutterCB->setVisible(false); } + motionAbortButton->setEnabled(true); + + // slaving + connect(slavingEnableButton, &QPushButton::clicked, this, [this]() + { + enableAutoSync(true); + }); + connect(slavingDisableButton, &QPushButton::clicked, this, [this]() + { + enableAutoSync(false); + }); + + setDomeStatus(getDomeModel()->status()); setShutterStatus(getDomeModel()->shutterStatus()); + + enableAutoSync(getDomeModel()->isAutoSync()); } } void Observatory::shutdownDome() { domeBox->setEnabled(false); shutterBox->setEnabled(false); shutterBox->setVisible(false); domePark->setEnabled(false); domeUnpark->setEnabled(false); shutterClosed->setEnabled(false); shutterOpen->setEnabled(false); - angleLabel->setEnabled(false); - domeAngleSpinBox->setEnabled(false); - setDomeAngleButton->setEnabled(false); disconnect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); disconnect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); } void Observatory::setDomeStatus(ISD::Dome::Status status) { switch (status) { case ISD::Dome::DOME_ERROR: break; case ISD::Dome::DOME_IDLE: domePark->setChecked(false); domePark->setText(i18n("Park")); domeUnpark->setChecked(true); domeUnpark->setText(i18n("UnParked")); - appendLogText(i18n("Dome is unparked.")); + enableMotionControl(true); + appendLogText(i18n("Dome is idle.")); break; case ISD::Dome::DOME_MOVING: + enableMotionControl(false); appendLogText(i18n("Dome is moving...")); break; case ISD::Dome::DOME_PARKED: domePark->setChecked(true); domePark->setText(i18n("Parked")); domeUnpark->setChecked(false); domeUnpark->setText(i18n("UnPark")); + enableMotionControl(false); appendLogText(i18n("Dome is parked.")); break; case ISD::Dome::DOME_PARKING: domePark->setText(i18n("Parking")); domeUnpark->setText(i18n("UnPark")); + enableMotionControl(false); appendLogText(i18n("Dome is parking...")); break; case ISD::Dome::DOME_UNPARKING: domePark->setText(i18n("Park")); domeUnpark->setText(i18n("UnParking")); + enableMotionControl(false); appendLogText(i18n("Dome is unparking...")); break; case ISD::Dome::DOME_TRACKING: + enableMotionControl(true); appendLogText(i18n("Dome is tracking.")); break; } } void Observatory::setShutterStatus(ISD::Dome::ShutterStatus status) { switch (status) { case ISD::Dome::SHUTTER_OPEN: shutterOpen->setChecked(true); shutterClosed->setChecked(false); shutterOpen->setText(i18n("Opened")); shutterClosed->setText(i18n("Close")); appendLogText(i18n("Shutter is open.")); break; case ISD::Dome::SHUTTER_OPENING: shutterOpen->setText(i18n("Opening")); shutterClosed->setText(i18n("Closed")); appendLogText(i18n("Shutter is opening...")); break; case ISD::Dome::SHUTTER_CLOSED: shutterOpen->setChecked(false); shutterClosed->setChecked(true); shutterOpen->setText(i18n("Open")); shutterClosed->setText(i18n("Closed")); appendLogText(i18n("Shutter is closed.")); break; case ISD::Dome::SHUTTER_CLOSING: shutterOpen->setText(i18n("Opened")); shutterClosed->setText(i18n("Closing")); appendLogText(i18n("Shutter is closing...")); break; default: break; } } void Observatory::setWeatherModel(ObservatoryWeatherModel *model) { mObservatoryModel->setWeatherModel(model); if (model != nullptr) { connect(weatherWarningBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setWarningActionsActive); connect(weatherAlertBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setAlertActionsActive); connect(model, &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather); connect(model, &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); connect(model, &Ekos::ObservatoryWeatherModel::disconnected, this, &Ekos::Observatory::shutdownWeather); connect(&weatherStatusTimer, &QTimer::timeout, [this]() { weatherWarningStatusLabel->setText(getWeatherModel()->getWarningActionsStatus()); weatherAlertStatusLabel->setText(getWeatherModel()->getAlertActionsStatus()); }); } else - { shutdownWeather(); +} + +void Observatory::enableMotionControl(bool enabled) +{ + MotionBox->setEnabled(enabled); + + // absolute motion controls + if (getDomeModel()->canAbsoluteMove()) + { + motionMoveAbsButton->setEnabled(enabled); + absoluteMotionSB->setEnabled(enabled); + } + else + { + motionMoveAbsButton->setEnabled(false); + absoluteMotionSB->setEnabled(false); + } + + // relative motion controls + if (getDomeModel()->canRelativeMove()) + { + motionMoveRelButton->setEnabled(enabled); + relativeMotionSB->setEnabled(enabled); + } + else + { + motionMoveRelButton->setEnabled(false); + relativeMotionSB->setEnabled(false); + } - // 2019-06-11 JM: Cannot disconnect a nullptr - // disconnect(model, &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); - // disconnect(model, &Ekos::ObservatoryWeatherModel::disconnected, this, &Ekos::Observatory::shutdownWeather); - // disconnect(model, &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather); - //disconnect(weatherWarningBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setWarningActionsActive); - //disconnect(weatherAlertBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setAlertActionsActive); +} + +void Observatory::enableAutoSync(bool enabled) +{ + if (getDomeModel() == nullptr) + showAutoSync(false); + else + { + getDomeModel()->setAutoSync(enabled); + showAutoSync(enabled); } } +void Observatory::showAutoSync(bool enabled) +{ + slavingEnableButton->setChecked(enabled); + slavingDisableButton->setChecked(! enabled); +} + void Observatory::initWeather() { weatherBox->setEnabled(true); weatherLabel->setEnabled(true); weatherActionsBox->setVisible(true); weatherActionsBox->setEnabled(true); weatherWarningBox->setChecked(getWeatherModel()->getWarningActionsActive()); weatherAlertBox->setChecked(getWeatherModel()->getAlertActionsActive()); setWeatherStatus(getWeatherModel()->status()); setWarningActions(getWeatherModel()->getWarningActions()); setAlertActions(getWeatherModel()->getAlertActions()); weatherStatusTimer.start(1000); } void Observatory::shutdownWeather() { weatherBox->setEnabled(false); weatherLabel->setEnabled(false); setWeatherStatus(ISD::Weather::WEATHER_IDLE); weatherStatusTimer.stop(); } void Observatory::setWeatherStatus(ISD::Weather::Status status) { std::string label; switch (status) { case ISD::Weather::WEATHER_OK: label = "security-high"; appendLogText(i18n("Weather is OK")); break; case ISD::Weather::WEATHER_WARNING: label = "security-medium"; appendLogText(i18n("Weather Warning")); break; case ISD::Weather::WEATHER_ALERT: label = "security-low"; appendLogText(i18n("Weather Alert")); break; default: label = ""; break; } weatherStatusLabel->setPixmap(QIcon::fromTheme(label.c_str()).pixmap(QSize(48, 48))); } void Observatory::weatherWarningSettingsChanged() { struct WeatherActions actions; actions.parkDome = weatherWarningDomeCB->isChecked(); actions.closeShutter = weatherWarningShutterCB->isChecked(); actions.delay = weatherWarningDelaySB->value(); getWeatherModel()->setWarningActions(actions); } void Observatory::weatherAlertSettingsChanged() { struct WeatherActions actions; actions.parkDome = weatherAlertDomeCB->isChecked(); actions.closeShutter = weatherAlertShutterCB->isChecked(); actions.delay = weatherAlertDelaySB->value(); getWeatherModel()->setAlertActions(actions); } void Observatory::observatoryStatusChanged(bool ready) { // statusReadyButton->setEnabled(!ready); statusReadyButton->setChecked(ready); emit newStatus(ready); } +void Observatory::domeAzimuthChanged(double position) +{ + domeAzimuthPosition->setText(QString::number(position, 'f', 2)); +} + void Observatory::setWarningActions(WeatherActions actions) { weatherWarningDomeCB->setChecked(actions.parkDome); weatherWarningShutterCB->setChecked(actions.closeShutter); weatherWarningDelaySB->setValue(actions.delay); } void Observatory::setAlertActions(WeatherActions actions) { weatherAlertDomeCB->setChecked(actions.parkDome); weatherAlertShutterCB->setChecked(actions.closeShutter); weatherAlertDelaySB->setValue(actions.delay); } void Observatory::statusControlSettingsChanged() { ObservatoryStatusControl control; control.useDome = useDomeCB->isChecked(); control.useShutter = useShutterCB->isChecked(); control.useWeather = useWeatherCB->isChecked(); mObservatoryModel->setStatusControl(control); } void Observatory::appendLogText(const QString &text) { m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"), text)); qCInfo(KSTARS_EKOS_OBSERVATORY) << text; emit newLog(text); } void Observatory::clearLog() { m_LogText.clear(); emit newLog(QString()); } } diff --git a/kstars/ekos/observatory/observatory.h b/kstars/ekos/observatory/observatory.h index e87e06bde..9de475800 100644 --- a/kstars/ekos/observatory/observatory.h +++ b/kstars/ekos/observatory/observatory.h @@ -1,100 +1,108 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "ui_observatory.h" #include "observatorymodel.h" #include "observatorydomemodel.h" #include "observatoryweathermodel.h" #include #include namespace Ekos { class Observatory : public QWidget, public Ui::Observatory { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Observatory") Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) public: Observatory(); ObservatoryDomeModel *getDomeModel() { return mObservatoryModel->getDomeModel(); } ObservatoryWeatherModel *getWeatherModel() { return mObservatoryModel->getWeatherModel(); } // Logging QStringList logText() { return m_LogText; } QString getLogText() { return m_LogText.join("\n"); } void clearLog(); signals: Q_SCRIPTABLE void newLog(const QString &text); /** * @brief Signal a new observatory status */ Q_SCRIPTABLE void newStatus(bool isReady); private: ObservatoryModel *mObservatoryModel = nullptr; void setDomeModel(ObservatoryDomeModel *model); void setWeatherModel(ObservatoryWeatherModel *model); + // motion control + void enableMotionControl(bool enabled); + + // slaving control + void enableAutoSync(bool enabled); + void showAutoSync(bool enabled); + // Logging QStringList m_LogText; void appendLogText(const QString &); // timer for refreshing the observatory status QTimer weatherStatusTimer; // reacting on weather changes void setWarningActions(WeatherActions actions); void setAlertActions(WeatherActions actions); private slots: // observatory status handling void setObseratoryStatusControl(ObservatoryStatusControl control); void statusControlSettingsChanged(); void initWeather(); void shutdownWeather(); void setWeatherStatus(ISD::Weather::Status status); // reacting on weather changes void weatherWarningSettingsChanged(); void weatherAlertSettingsChanged(); // reacting on observatory status changes void observatoryStatusChanged(bool ready); + void domeAzimuthChanged(double position); void initDome(); void shutdownDome(); void setDomeStatus(ISD::Dome::Status status); void setShutterStatus(ISD::Dome::ShutterStatus status); }; } diff --git a/kstars/ekos/observatory/observatory.ui b/kstars/ekos/observatory/observatory.ui index 0cf0d8293..11560d3d9 100644 --- a/kstars/ekos/observatory/observatory.ui +++ b/kstars/ekos/observatory/observatory.ui @@ -1,742 +1,1043 @@ Observatory 0 0 - 730 - 284 + 873 + 494 Observatory Status of the Observatory 3 3 3 3 3 3 3 false Dome - - - 3 - + 3 3 3 3 - - - - false - - - - 96 - 36 - - - - - 96 - 36 - - - - <html><head/><body><p>Park the dome. For advanced control of the dome please use the INDI tab.</p></body></html> - - - QPushButton:checked + + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Park the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - - - Park - - - - 32 - 16 - - - - true - - - - - - - false - - - - 96 - 36 - - - - - 96 - 36 - - - - <html><head/><body><p>Unpark the dome. For advanced control of the dome please use the INDI tab.</p></body></html> - - - QPushButton:checked + + + Park + + + + 32 + 16 + + + + true + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Unpark the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - - - UnPark - - - true - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + + UnPark + + + true + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + Abort dome motion + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + Abort + + + + + + + + + + + false + + + Relative position the dome should move. + + + -999.990000000000009 + + + 999.990000000000009 + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + Move the dome for the given degrees and direction. + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + Move (rel) + + + + + + + false + + + Absolute position the dome should move. + + + 999.990000000000009 + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + Move the dome to the given absolute position. + + + Move (abs) + + + + + + + + + + 160 + 0 + + + + + 320 + 16777215 + + + + + 24 + 75 + true + + + + 0 + + + Qt::AlignCenter + + + + + false + + + 96 + 36 + + + + + 96 + 36 + + + + Rotate counter clockwise + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + - Azimuth: + CCW - - + + false + + Motion: + + + + + + + + + + + + + + Position + - - + + false - 72 - 24 + 96 + 36 - 72 - 24 + 96 + 36 + + Rotate clockwise + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + - Set + &CW + + + + + + Slaving + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Enable slaving, dome motion <span style=" font-weight:600;">follows telescope motion</span></p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + Enable + + + true + + + + + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Disable slaving, dome <span style=" font-weight:600;">does not follow telescope motion</span>.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + Disable + + + true + + + + + false Shutter 3 3 3 3 3 + + + + Qt::Horizontal + + + + 40 + 20 + + + + false 96 36 72 36 <html><head/><body><p>Close the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Close 32 16 true false 96 36 72 36 <html><head/><body><p>Open the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Open true false - - - - Qt::Horizontal - - - - 40 - 20 - - - - false Observatory Status 3 3 3 3 3 <html><head/><body><p>If selected, the dome needs to be unparked for the observatory status being &quot;READY&quot;.</p></body></html> Dome <html><head/><body><p>If selected, the shutter needs to be open for the observatory status being &quot;READY&quot;.</p></body></html> Shutter <html><head/><body><p>If selected, the weather needs to be OK for the observatory status being &quot;READY&quot;.</p></body></html> Weather false 96 36 72 36 - <html><head/><body><p>Close the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + <html><head/><body><p>Observatory status. Select the observatory elements that are relevant for the status:</p> +<ul> +<li><b>Dome</b>: unparked &rarr; ready</li> +<li><b>Shutter</b>: open &rarr; ready</li> +<li><b>Weather</b>: OK &rarr; ready</li> +</ul> +</body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Ready 32 16 true false false Weather 3 3 3 3 3 - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - Display the weather status. The warning and alert limits are set in the INDI tab. - - - Weather Status: - - - - - - - - 0 - 0 - - - - - 48 - 48 - - - - - 48 - 48 - - - - - - - false Actions 3 3 3 3 3 War&ning true 3 0 3 0 3 Park Dome Close Shutter Stop Scheduler <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> Qt::AutoText Qt::Horizontal 40 20 Delay (sec): Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 9999 - + Ale&rt true 3 3 3 0 3 Close Shutter Park Dome Stop Scheduler <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> Qt::AutoText Qt::Horizontal 40 20 Delay (sec): Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 9999 + + + + + 0 + 0 + + + + + 48 + 48 + + + + + 48 + 48 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Display the weather status. The warning and alert limits are set in the INDI tab. + + + Weather Status: + + + Qt::Vertical 20 3 diff --git a/kstars/ekos/observatory/observatorydomemodel.cpp b/kstars/ekos/observatory/observatorydomemodel.cpp index 070dcfa96..2873fb07f 100644 --- a/kstars/ekos/observatory/observatorydomemodel.cpp +++ b/kstars/ekos/observatory/observatorydomemodel.cpp @@ -1,89 +1,110 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "observatorydomemodel.h" #include namespace Ekos { void ObservatoryDomeModel::initModel(Dome *dome) { mDome = dome; connect(mDome, &Dome::ready, this, &ObservatoryDomeModel::ready); connect(mDome, &Dome::disconnected, this, &ObservatoryDomeModel::disconnected); connect(mDome, &Dome::newStatus, this, &ObservatoryDomeModel::newStatus); connect(mDome, &Dome::newShutterStatus, this, &ObservatoryDomeModel::newShutterStatus); + connect(mDome, &Dome::azimuthPositionChanged, this, &ObservatoryDomeModel::azimuthPositionChanged); + connect(mDome, &Dome::newAutoSyncStatus, this, &ObservatoryDomeModel::newAutoSyncStatus); } ISD::Dome::Status ObservatoryDomeModel::status() { if (mDome == nullptr) return ISD::Dome::DOME_IDLE; return mDome->status(); } ISD::Dome::ShutterStatus ObservatoryDomeModel::shutterStatus() { if (mDome == nullptr) return ISD::Dome::SHUTTER_UNKNOWN; return mDome->shutterStatus(); } void ObservatoryDomeModel::park() { if (mDome == nullptr) return; emit newLog(i18n("Parking dome...")); mDome->park(); } void ObservatoryDomeModel::unpark() { if (mDome == nullptr) return; emit newLog(i18n("Unparking dome...")); mDome->unpark(); } +void ObservatoryDomeModel::setAutoSync(bool activate) +{ + if (mDome == nullptr) + return; + + if (mDome->setAutoSync(activate)) + emit newLog(i18n(activate ? "Slaving activated." : "Slaving deactivated.")); + +} + +void ObservatoryDomeModel::abort() +{ + if (mDome == nullptr) + return; + + emit newLog(i18n("Aborting...")); + mDome->abort(); +} + void ObservatoryDomeModel::openShutter() { if (mDome == nullptr) return; emit newLog(i18n("Opening shutter...")); mDome->controlShutter(true); } void ObservatoryDomeModel::closeShutter() { if (mDome == nullptr) return; emit newLog(i18n("Closing shutter...")); mDome->controlShutter(false); } void ObservatoryDomeModel::execute(WeatherActions actions) { if (hasShutter() && actions.closeShutter) closeShutter(); if (actions.parkDome) park(); } } diff --git a/kstars/ekos/observatory/observatorydomemodel.h b/kstars/ekos/observatory/observatorydomemodel.h index ab0a419a2..eb2635e0c 100644 --- a/kstars/ekos/observatory/observatorydomemodel.h +++ b/kstars/ekos/observatory/observatorydomemodel.h @@ -1,62 +1,98 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "../auxiliary/dome.h" #include "observatoryweathermodel.h" #include namespace Ekos { class ObservatoryDomeModel: public QObject { Q_OBJECT public: ObservatoryDomeModel() = default; void initModel(Dome *dome); ISD::Dome::Status status(); ISD::Dome::ShutterStatus shutterStatus(); // proxies to the underlying dome object bool canPark() { return (mDome != nullptr && mDome->canPark()); } void park(); void unpark(); + + double azimuthPosition() + { + return mDome->azimuthPosition(); + } + void setAzimuthPosition(double position) + { + mDome->setAzimuthPosition(position); + } + + bool canAbsoluteMove() + { + return (mDome != nullptr && mDome->canAbsoluteMove()); + } + + void setRelativePosition(double position) + { + mDome->setRelativePosition(position); + } + + bool canRelativeMove() + { + return (mDome != nullptr && mDome->canRelativeMove()); + } + + bool isAutoSync() + { + return (mDome != nullptr && mDome->isAutoSync()); + } + + void setAutoSync(bool activate); + + void abort(); + bool hasShutter() { return (mDome != nullptr && mDome->hasShutter()); } void openShutter(); void closeShutter(); public slots: void execute(WeatherActions actions); private: Dome *mDome; signals: void newStatus(ISD::Dome::Status state); void newShutterStatus(ISD::Dome::ShutterStatus status); + void newAutoSyncStatus(bool enabled); + void azimuthPositionChanged(double position); void ready(); void disconnected(); void newLog(const QString &text); }; } diff --git a/kstars/ekos/observatory/observatorymodel.cpp b/kstars/ekos/observatory/observatorymodel.cpp index ede19249b..424b3a2eb 100644 --- a/kstars/ekos/observatory/observatorymodel.cpp +++ b/kstars/ekos/observatory/observatorymodel.cpp @@ -1,111 +1,113 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "observatorymodel.h" #include "Options.h" namespace Ekos { ObservatoryModel::ObservatoryModel() { mStatusControl.useDome = Options::observatoryStatusUseDome(); mStatusControl.useShutter = Options::observatoryStatusUseShutter(); mStatusControl.useWeather = Options::observatoryStatusUseWeather(); setDomeModel(new ObservatoryDomeModel()); setWeatherModel(new ObservatoryWeatherModel()); } void ObservatoryModel::setDomeModel(ObservatoryDomeModel *model) { mDomeModel = model; if (model != nullptr) { connect(mDomeModel, &ObservatoryDomeModel::newStatus, [this](ISD::Dome::Status s) { Q_UNUSED(s); updateStatus(); }); connect(mDomeModel, &ObservatoryDomeModel::newShutterStatus, [this](ISD::Dome::ShutterStatus s) { Q_UNUSED(s); updateStatus(); }); if (mWeatherModel != nullptr) connect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } else { if (mWeatherModel != nullptr) disconnect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } updateStatus(); } void ObservatoryModel::setWeatherModel(ObservatoryWeatherModel *model) { mWeatherModel = model; if (model != nullptr) { connect(mWeatherModel, &ObservatoryWeatherModel::newStatus, [this](ISD::Weather::Status s) { Q_UNUSED(s); updateStatus(); }); if (mDomeModel != nullptr) { connect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } } else { if (mDomeModel != nullptr) disconnect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } updateStatus(); } void ObservatoryModel::setStatusControl(ObservatoryStatusControl control) { mStatusControl = control; Options::setObservatoryStatusUseDome(control.useDome); Options::setObservatoryStatusUseShutter(control.useShutter); Options::setObservatoryStatusUseWeather(control.useWeather); updateStatus(); } bool ObservatoryModel::isReady() { // dome relevant for the status and dome is ready - if (mStatusControl.useDome && (getDomeModel() == nullptr || getDomeModel()->status() != ISD::Dome::DOME_IDLE)) + if (mStatusControl.useDome && (getDomeModel() == nullptr || + (getDomeModel()->status() != ISD::Dome::DOME_IDLE && + getDomeModel()->status() != ISD::Dome::DOME_TRACKING))) return false; // shutter relevant for the status and shutter open if (mStatusControl.useShutter && (getDomeModel() == nullptr || (getDomeModel()->hasShutter() && getDomeModel()->shutterStatus() != ISD::Dome::SHUTTER_OPEN))) return false; // weather relevant for the status and weather is OK if (mStatusControl.useWeather && (getWeatherModel() == nullptr || getWeatherModel()->status() != ISD::Weather::WEATHER_OK)) return false; return true; } void ObservatoryModel::updateStatus() { emit newStatus(isReady()); } void ObservatoryModel::makeReady() { // dome relevant for the status and dome is ready if (mStatusControl.useDome && (getDomeModel() == nullptr || getDomeModel()->status() != ISD::Dome::DOME_IDLE)) getDomeModel()->unpark(); // shutter relevant for the status and shutter open if (mStatusControl.useShutter && (getDomeModel() == nullptr || (getDomeModel()->hasShutter() && getDomeModel()->shutterStatus() != ISD::Dome::SHUTTER_OPEN))) getDomeModel()->openShutter(); // weather relevant for the status and weather is OK // Haha, weather we can't change } } diff --git a/kstars/indi/indidome.cpp b/kstars/indi/indidome.cpp index 0beeb9cc8..57ca869b6 100644 --- a/kstars/indi/indidome.cpp +++ b/kstars/indi/indidome.cpp @@ -1,486 +1,541 @@ /* INDI Dome Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include "indidome.h" #include "kstars.h" #include "clientmanager.h" namespace ISD { Dome::Dome(GDInterface *iPtr) : DeviceDecorator(iPtr) { dType = KSTARS_DOME; qRegisterMetaType("ISD::Dome::Status"); qDBusRegisterMetaType(); readyTimer.reset(new QTimer()); readyTimer.get()->setInterval(250); readyTimer.get()->setSingleShot(true); connect(readyTimer.get(), &QTimer::timeout, this, &Dome::ready); } void Dome::registerProperty(INDI::Property *prop) { if (isConnected()) readyTimer.get()->start(); if (!strcmp(prop->getName(), "DOME_PARK")) { ISwitchVectorProperty *svp = prop->getSwitch(); m_CanPark = true; if (svp) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if ((sp->s == ISS_ON) && svp->s == IPS_OK) { m_ParkStatus = PARK_PARKED; m_Status = DOME_PARKED; emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(true); } else if ((sp->s == ISS_OFF) && svp->s == IPS_OK) { m_ParkStatus = PARK_UNPARKED; m_Status = DOME_IDLE; emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(false); } } } } else if (!strcmp(prop->getName(), "ABS_DOME_POSITION")) { m_CanAbsMove = true; } + else if (!strcmp(prop->getName(), "REL_DOME_POSITION")) + { + m_CanRelMove = true; + } else if (!strcmp(prop->getName(), "DOME_ABORT_MOTION")) { m_CanAbort = true; } else if (!strcmp(prop->getName(), "DOME_SHUTTER")) { m_HasShutter = true; } DeviceDecorator::registerProperty(prop); } void Dome::processLight(ILightVectorProperty *lvp) { DeviceDecorator::processLight(lvp); } void Dome::processNumber(INumberVectorProperty *nvp) { if (!strcmp(nvp->name, "ABS_DOME_POSITION")) { emit azimuthPositionChanged(nvp->np[0].value); } DeviceDecorator::processNumber(nvp); } void Dome::processSwitch(ISwitchVectorProperty *svp) { if (!strcmp(svp->name, "CONNECTION")) { ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP) { if (isConnected() == false && conSP->s == ISS_ON) KStars::Instance()->slotSetDomeEnabled(true); else if (isConnected() && conSP->s == ISS_OFF) { KStars::Instance()->slotSetDomeEnabled(false); m_CanAbsMove = false; m_CanPark = false; } } } else if (!strcmp(svp->name, "DOME_PARK")) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if (svp->s == IPS_ALERT) { emit newParkStatus(PARK_ERROR); // If alert, set park status to whatever it was opposite to. That is, if it was parking and failed // then we set status to unparked since it did not successfully complete parking. if (m_ParkStatus == PARK_PARKING) m_ParkStatus = PARK_UNPARKED; else if (m_ParkStatus == PARK_UNPARKING) m_ParkStatus = PARK_PARKED; emit newParkStatus(m_ParkStatus); } else if (svp->s == IPS_BUSY && sp->s == ISS_ON && m_ParkStatus != PARK_PARKING) { m_ParkStatus = PARK_PARKING; KNotification::event(QLatin1String("DomeParking"), i18n("Dome parking is in progress")); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_PARKING) { m_Status = DOME_PARKING; emit newStatus(m_Status); } } else if (svp->s == IPS_BUSY && sp->s == ISS_OFF && m_ParkStatus != PARK_UNPARKING) { m_ParkStatus = PARK_UNPARKING; KNotification::event(QLatin1String("DomeUnparking"), i18n("Dome unparking is in progress")); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_UNPARKING) { m_Status = DOME_UNPARKING; emit newStatus(m_Status); } } else if (svp->s == IPS_OK && sp->s == ISS_ON && m_ParkStatus != PARK_PARKED) { m_ParkStatus = PARK_PARKED; KNotification::event(QLatin1String("DomeParked"), i18n("Dome parked")); emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(true); if (m_Status != DOME_PARKED) { m_Status = DOME_PARKED; emit newStatus(m_Status); } } else if ( (svp->s == IPS_OK || svp->s == IPS_IDLE) && sp->s == ISS_OFF && m_ParkStatus != PARK_UNPARKED) { m_ParkStatus = PARK_UNPARKED; KNotification::event(QLatin1String("DomeUnparked"), i18n("Dome unparked")); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(false); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_IDLE) { m_Status = DOME_IDLE; emit newStatus(m_Status); } } } } else if (!strcmp(svp->name, "DOME_MOTION")) { Status lastStatus = m_Status; if (svp->s == IPS_BUSY && lastStatus != DOME_MOVING && lastStatus != DOME_PARKING && lastStatus != DOME_UNPARKING) { m_Status = DOME_MOVING; emit newStatus(m_Status); } else if (svp->s == IPS_OK && lastStatus == DOME_MOVING) { m_Status = DOME_TRACKING; emit newStatus(m_Status); } else if (svp->s == IPS_IDLE && lastStatus != DOME_IDLE) { m_Status = DOME_IDLE; emit newStatus(m_Status); } } else if (!strcmp(svp->name, "DOME_SHUTTER")) { if (svp->s == IPS_ALERT) { emit newShutterStatus(SHUTTER_ERROR); // If alert, set shutter status to whatever it was opposite to. That is, if it was opening and failed // then we set status to closed since it did not successfully complete opening. if (m_ShutterStatus == SHUTTER_CLOSING) m_ShutterStatus = SHUTTER_OPEN; else if (m_ShutterStatus == SHUTTER_CLOSING) m_ShutterStatus = SHUTTER_CLOSED; emit newShutterStatus(m_ShutterStatus); } ShutterStatus status = shutterStatus(svp); switch (status) { case SHUTTER_CLOSING: if (m_ShutterStatus != SHUTTER_CLOSING) { m_ShutterStatus = SHUTTER_CLOSING; KNotification::event(QLatin1String("ShutterClosing"), i18n("Shutter closing is in progress")); emit newShutterStatus(m_ShutterStatus); } break; case SHUTTER_OPENING: if (m_ShutterStatus != SHUTTER_OPENING) { m_ShutterStatus = SHUTTER_OPENING; KNotification::event(QLatin1String("ShutterOpening"), i18n("Shutter opening is in progress")); emit newShutterStatus(m_ShutterStatus); } break; case SHUTTER_CLOSED: if (m_ShutterStatus != SHUTTER_CLOSED) { m_ShutterStatus = SHUTTER_CLOSED; KNotification::event(QLatin1String("ShutterClosed"), i18n("Shutter closed")); emit newShutterStatus(m_ShutterStatus); } break; case SHUTTER_OPEN: if (m_ShutterStatus != SHUTTER_OPEN) { m_ShutterStatus = SHUTTER_OPEN; KNotification::event(QLatin1String("ShutterOpened"), i18n("Shutter opened")); emit newShutterStatus(m_ShutterStatus); } break; default: break; } return; } + else if (!strcmp(svp->name, "DOME_AUTOSYNC")) + { + ISwitch *sp = IUFindSwitch(svp, "DOME_AUTOSYNC_ENABLE"); + if (sp != nullptr) + emit newAutoSyncStatus(sp->s == ISS_ON); + } DeviceDecorator::processSwitch(svp); } void Dome::processText(ITextVectorProperty *tvp) { DeviceDecorator::processText(tvp); } bool Dome::Abort() { if (m_CanAbort == false) return false; ISwitchVectorProperty *motionSP = baseDevice->getSwitch("DOME_ABORT_MOTION"); if (motionSP == nullptr) return false; ISwitch *abortSW = IUFindSwitch(motionSP, "ABORT"); if (abortSW == nullptr) return false; abortSW->s = ISS_ON; clientManager->sendNewSwitch(motionSP); return true; } bool Dome::Park() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("DOME_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "PARK"); if (parkSW == nullptr) return false; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Dome::UnPark() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("DOME_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "UNPARK"); if (parkSW == nullptr) return false; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Dome::isMoving() const { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("DOME_MOTION"); if (motionSP && motionSP->s == IPS_BUSY) return true; return false; } double Dome::azimuthPosition() const { INumberVectorProperty *az = baseDevice->getNumber("ABS_DOME_POSITION"); if (az == nullptr) return -1; else return az->np[0].value; } bool Dome::setAzimuthPosition(double position) { INumberVectorProperty *az = baseDevice->getNumber("ABS_DOME_POSITION"); if (az == nullptr) return false; az->np[0].value = position; clientManager->sendNewNumber(az); return true; } +bool Dome::setRelativePosition(double position) +{ + INumberVectorProperty *azDiff = baseDevice->getNumber("REL_DOME_POSITION"); + + if (azDiff == nullptr) + return false; + + azDiff->np[0].value = position; + clientManager->sendNewNumber(azDiff); + return true; +} + +bool Dome::isAutoSync() +{ + ISwitchVectorProperty *autosync = baseDevice->getSwitch("DOME_AUTOSYNC"); + + if (autosync == nullptr) + return false; + + ISwitch *autosyncSW = IUFindSwitch(autosync, "DOME_AUTOSYNC_ENABLE"); + + if (autosync == nullptr) + return false; + else + return (autosyncSW->s == ISS_ON); +} + +bool Dome::setAutoSync(bool activate) +{ + ISwitchVectorProperty *autosync = baseDevice->getSwitch("DOME_AUTOSYNC"); + + if (autosync == nullptr) + return false; + + ISwitch *autosyncSW = IUFindSwitch(autosync, activate ? "DOME_AUTOSYNC_ENABLE" : "DOME_AUTOSYNC_DISABLE"); + if (autosyncSW == nullptr) + return false; + + IUResetSwitch(autosync); + autosyncSW->s = ISS_ON; + clientManager->sendNewSwitch(autosync); + + return true; +} + bool Dome::ControlShutter(bool open) { ISwitchVectorProperty *shutterSP = baseDevice->getSwitch("DOME_SHUTTER"); if (shutterSP == nullptr) return false; ISwitch *shutterSW = IUFindSwitch(shutterSP, open ? "SHUTTER_OPEN" : "SHUTTER_CLOSE"); if (shutterSW == nullptr) return false; IUResetSwitch(shutterSP); shutterSW->s = ISS_ON; clientManager->sendNewSwitch(shutterSP); return true; } Dome::ShutterStatus Dome::shutterStatus() { ISwitchVectorProperty *shutterSP = baseDevice->getSwitch("DOME_SHUTTER"); return shutterStatus(shutterSP); } Dome::ShutterStatus Dome::shutterStatus(ISwitchVectorProperty *svp) { if (svp == nullptr) return SHUTTER_UNKNOWN; ISwitch *sp = IUFindSwitch(svp, "SHUTTER_OPEN"); if (sp == nullptr) return SHUTTER_UNKNOWN; if (svp->s == IPS_ALERT) return SHUTTER_ERROR; else if (svp->s == IPS_BUSY) return (sp->s == ISS_ON) ? SHUTTER_OPENING : SHUTTER_CLOSING; else if (svp->s == IPS_OK) return (sp->s == ISS_ON) ? SHUTTER_OPEN : SHUTTER_CLOSED; // this should not happen return SHUTTER_UNKNOWN; } const QString Dome::getStatusString(Dome::Status status) { switch (status) { case ISD::Dome::DOME_IDLE: return i18n("Idle"); case ISD::Dome::DOME_PARKED: return i18n("Parked"); case ISD::Dome::DOME_PARKING: return i18n("Parking"); case ISD::Dome::DOME_UNPARKING: return i18n("UnParking"); case ISD::Dome::DOME_MOVING: return i18n("Moving"); case ISD::Dome::DOME_TRACKING: return i18n("Tracking"); case ISD::Dome::DOME_ERROR: return i18n("Error"); } return i18n("Error"); } } QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Dome::Status& source) { argument.beginStructure(); argument << static_cast(source); argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Dome::Status &dest) { int a; argument.beginStructure(); argument >> a; argument.endStructure(); dest = static_cast(a); return argument; } diff --git a/kstars/indi/indidome.h b/kstars/indi/indidome.h index 94f8dabb8..42de8f8b6 100644 --- a/kstars/indi/indidome.h +++ b/kstars/indi/indidome.h @@ -1,126 +1,138 @@ /* INDI Dome Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include #include #include "indistd.h" namespace ISD { /** * @class Dome * Focuser class handles control of INDI dome devices. Both open and closed loop (senor feedback) domes are supported. * * @author Jasem Mutlaq */ class Dome : public DeviceDecorator { Q_OBJECT public: explicit Dome(GDInterface *iPtr); typedef enum { DOME_IDLE, DOME_MOVING, DOME_TRACKING, DOME_PARKING, DOME_UNPARKING, DOME_PARKED, DOME_ERROR } Status; typedef enum { SHUTTER_UNKNOWN, SHUTTER_OPEN, SHUTTER_CLOSED, SHUTTER_OPENING, SHUTTER_CLOSING, SHUTTER_ERROR } ShutterStatus; void processSwitch(ISwitchVectorProperty *svp) override; void processText(ITextVectorProperty *tvp) override; void processNumber(INumberVectorProperty *nvp) override; void processLight(ILightVectorProperty *lvp) override; void registerProperty(INDI::Property *prop) override; DeviceFamily getType() override { return dType; } bool canPark() const { return m_CanPark; } bool canAbsMove() const { return m_CanAbsMove; } + bool canRelMove() const + { + return m_CanRelMove; + } bool canAbort() const { return m_CanAbort; } bool isParked() const { return m_ParkStatus == PARK_PARKED; } bool isMoving() const; double azimuthPosition() const; bool setAzimuthPosition(double position); + bool setRelativePosition(double position); bool hasShutter() const { return m_HasShutter; } + + // slaving + bool isAutoSync(); + bool setAutoSync(bool activate); + Status status() const { return m_Status; } static const QString getStatusString (Status status); ShutterStatus shutterStatus(); ShutterStatus shutterStatus(ISwitchVectorProperty *svp); public slots: bool Abort(); bool Park(); bool UnPark(); bool ControlShutter(bool open); signals: void newStatus(Status status); void newParkStatus(ParkStatus status); void newShutterStatus(ShutterStatus status); + void newAutoSyncStatus(bool enabled); void azimuthPositionChanged(double Az); void ready(); private: ParkStatus m_ParkStatus { PARK_UNKNOWN }; ShutterStatus m_ShutterStatus { SHUTTER_UNKNOWN }; Status m_Status { DOME_IDLE }; bool m_CanAbsMove { false }; + bool m_CanRelMove { false }; bool m_CanPark { false }; bool m_CanAbort { false }; bool m_HasShutter { false }; std::unique_ptr readyTimer; }; } Q_DECLARE_METATYPE(ISD::Dome::Status) QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Dome::Status &source); const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Dome::Status &dest); diff --git a/kstars/org.kde.kstars.Ekos.Dome.xml b/kstars/org.kde.kstars.Ekos.Dome.xml index 49017e50f..7a60c9b19 100644 --- a/kstars/org.kde.kstars.Ekos.Dome.xml +++ b/kstars/org.kde.kstars.Ekos.Dome.xml @@ -1,38 +1,39 @@ +