diff --git a/kstars/CMakeLists.txt b/kstars/CMakeLists.txt --- a/kstars/CMakeLists.txt +++ b/kstars/CMakeLists.txt @@ -127,6 +127,7 @@ ekos/guide/opscalibration.ui ekos/guide/opsguide.ui ekos/guide/manualdither.ui + ekos/observatory/observatory.ui #TODO remove from GIT #ekos/guide/guider.ui #ekos/guide/rcalibration.ui @@ -203,6 +204,11 @@ ekos/guide/externalguide/phd2.cpp ekos/guide/externalguide/linguider.cpp + #Observatory + ekos/observatory/observatory.cpp + ekos/observatory/observatorydomemodel.cpp + ekos/observatory/observatoryweathermodel.cpp + # Ekos Live ekos/ekoslive/ekosliveclient.cpp ekos/ekoslive/message.cpp @@ -939,6 +945,7 @@ ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_guide_debug.h IDENTIFIER KSTARS_EKOS_GUIDE CATEGORY_NAME org.kde.kstars.ekos.guide) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_mount_debug.h IDENTIFIER KSTARS_EKOS_MOUNT CATEGORY_NAME org.kde.kstars.ekos.mount) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_scheduler_debug.h IDENTIFIER KSTARS_EKOS_SCHEDULER CATEGORY_NAME org.kde.kstars.ekos.scheduler) +ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_observatory_debug.h IDENTIFIER KSTARS_EKOS_OBSERVATORY CATEGORY_NAME org.kde.kstars.ekos.observatory) kconfig_add_kcfg_files(kstars_SRCS ${kstars_KCFG_SRCS}) @@ -972,6 +979,7 @@ qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Weather.xml ekos/auxiliary/weather.h Ekos::Weather) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.DustCap.xml ekos/auxiliary/dustcap.h Ekos::DustCap) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Scheduler.xml ekos/scheduler/scheduler.h Ekos::Scheduler) + qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Observatory.xml ekos/observatory/observatory.h Ekos::Observatory) ENDIF () ki18n_wrap_ui(kstars_SRCS diff --git a/kstars/data/icons/ekos_observatory.png b/kstars/data/icons/ekos_observatory.png new file mode 100644 index 0000000000000000000000000000000000000000..55904497b7ece081dc9c6735bff9493f7cd4649f GIT binary patch literal 2006 zc$^(n2~^Tq7pJhrt!c9b%cgJ2sZ%-Gn3gM`_-7Lk#05by98}anKy$?bH&Q43qSV|< zG}j3wO>J6HY_f6}%{8~I9L>zF+y^`7zTbVnd+v75yZ0p$aDc8BL<7tN!M72J< zakhiiNblFIQY#G~m;($1x|gE8;JJbv!BI|k3JM)4?BU_z<>lq?U9s))w7mR6B*{C!pcv~GUC)p`Cp*GBS}whA>u?m7U}1Nxx_bZEx>@0mSL)X{a5t ztLqH{NBj8kBNQM`PEPjq^?m*N_3BmemBgf!|4YP@bQH-u?@kdM?TjbW&^XeQz(69| zwWp`Imca-K4R2^@Xl!gk<6Z3u^w4mAWOQr|pMUqR?0I&QrP$~BBD>)A8Kn>XE# zE}ps4yf&fG27#;Na*K*fU(nUfz#92{xm-RqH5D2j3AM*Rc<`X|eig>me_&w1+7>f7 zIC#O_Ix{PKbaXU=FLa>%9vU8nbLZsb+=3yAxw+DB-@dJ_t))m(Eo>0utV`HQDZ_MK2;=n+**#(QV^qYPG z8)JjQ;Hs)?FfRUgWin21cyn`eSVUBEa`OB4@6Vf>rKY714Gl@9QY#oLkR1d=phZdI zkdVthK7Kwdc4uej+}vDjLXw3QR3MB&VjL+Rmu6>YQ8-f4)oXTGN`HU9BsC*mbR{+} z;YNDq!$*(g_bS@j+G^{RrKNw>LQay^JMeQb5k~?=%s(c@77S$vb3(pg(4k;r-JjWT>LgTgy#D3Omxj*; z`{kM(3sjcVW3k9T&8o#V?pXcveh;l>MI3Sdq<({>MuN4Btrgj zNd83ZiyivZ;5n77Vm;FU|Am{_)*4m-e)D4;2+yIIw?XHPv&?=m8Q8nEE9k{sSpBsD z83fX1E)OEM2t_3i)1Dd=luEnwGtpUfTJt)RfpZS)J?5Q!RqNRJ~m}CJtiPyD5hkqX^0aUSJDJLc=%&vcH@iMPxqoTAYg0Xi6&NM^?-a*r@#L zflF<41D2J8^4x00>dYCpjB!09BfatR${jsL-A^i2H<^c7VrlJ<4=m85LmArXuUt%z zSl)*0;rVb!-=>b?J)d4E-qm?HKp=(Ti%8(j0RbkhZaLFI2;bIr&31)m%+OQ&>Z2Mp z2|G`l9?$RGOsxpB({GjXy(g*$#HL~EO5-DQZLd{!00^&zOlV* zv0v3(b)ur71AP3Z&K|0C8MmE?6mHK?;Ov61Ut;j zjc63->ia75y$8aUiXrs}yoVMyT>=x7)N;3nuhYhxXEL?50_9t+>}JxQ&&5_V9~kY7 z^Jg)ZU~w?eKXWC0gzgKzm@r>=1m++tF6@&^^xATYChCDf3k&EP2sb}}mR)?nZ z3m7OQt-HKU6#y#e>dZv6UfF}#`ztfP*QT8wsAA%*}1MGnFD0CC2N9GGg7xi*YukEPJn&rh|Ux lkL%%Z`v|k5`0IO;Ss>Tbt?bf$Bxm*i141Eic4csS%zp!e!wUca literal 0 Hc$@icons/kstars_satellites_invisible.svg icons/kstars_satellites_visible.svg icons/cloud-online.svg + icons/ekos_observatory.png kstars.knsrc diff --git a/kstars/ekos/auxiliary/dome.h b/kstars/ekos/auxiliary/dome.h --- a/kstars/ekos/auxiliary/dome.h +++ b/kstars/ekos/auxiliary/dome.h @@ -91,6 +91,9 @@ Q_SCRIPTABLE double azimuthPosition(); Q_SCRIPTABLE void setAzimuthPosition(double position); + Q_SCRIPTABLE bool hasShutter(); + Q_SCRIPTABLE bool controlShutter(bool open); + /** @}*/ /** @@ -106,18 +109,21 @@ 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: +signals: void newStatus(ISD::Dome::Status status); void newParkStatus(ISD::ParkStatus status); + void newShutterStatus(ISD::Dome::ShutterStatus status); void azimuthPositionChanged(double position); void ready(); 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/auxiliary/dome.cpp b/kstars/ekos/auxiliary/dome.cpp --- a/kstars/ekos/auxiliary/dome.cpp +++ b/kstars/ekos/auxiliary/dome.cpp @@ -39,6 +39,8 @@ 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::azimuthPositionChanged, this, &Dome::azimuthPositionChanged); connect(currentDome, &ISD::Dome::ready, this, &Dome::ready); } @@ -121,6 +123,23 @@ currentDome->setAzimuthPosition(position); } +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() { diff --git a/kstars/ekos/manager.h b/kstars/ekos/manager.h --- a/kstars/ekos/manager.h +++ b/kstars/ekos/manager.h @@ -28,6 +28,7 @@ #include "indi/indistd.h" #include "mount/mount.h" #include "scheduler/scheduler.h" +#include "observatory/observatory.h" #include "auxiliary/filtermanager.h" #include "auxiliary/serialportassistant.h" #include "ksnotification.h" @@ -395,6 +396,7 @@ void initMount(); void initDome(); void initWeather(); + void initObservatory(Weather *weather, Dome *dome); void initDustCap(); void loadDrivers(); @@ -443,6 +445,7 @@ std::unique_ptr alignProcess; std::unique_ptr mountProcess; std::unique_ptr schedulerProcess; + std::unique_ptr observatoryProcess; std::unique_ptr domeProcess; std::unique_ptr weatherProcess; std::unique_ptr dustCapProcess; diff --git a/kstars/ekos/manager.cpp b/kstars/ekos/manager.cpp --- a/kstars/ekos/manager.cpp +++ b/kstars/ekos/manager.cpp @@ -431,6 +431,7 @@ alignProcess.reset(); mountProcess.reset(); weatherProcess.reset(); + observatoryProcess.reset(); dustCapProcess.reset(); Ekos::CommunicationStatus previousStatus = m_ekosStatus; @@ -1870,8 +1871,10 @@ ekosLogOut->setPlainText(guideProcess->getLogText()); else if (currentWidget == mountProcess.get()) ekosLogOut->setPlainText(mountProcess->getLogText()); - if (currentWidget == schedulerProcess.get()) + else if (currentWidget == schedulerProcess.get()) ekosLogOut->setPlainText(schedulerProcess->getLogText()); + else if (currentWidget == observatoryProcess.get()) + ekosLogOut->setPlainText(observatoryProcess->getLogText()); #ifdef Q_OS_OSX repaint(); //This is a band-aid for a bug in QT 5.10.0 @@ -2222,6 +2225,7 @@ ekosLiveClient.get()->message()->updateDomeStatus(status); }); + initObservatory(nullptr, domeProcess.get()); emit newModule("Dome"); ekosLiveClient->message()->sendDomes(); @@ -2233,10 +2237,32 @@ return; weatherProcess.reset(new Ekos::Weather()); + initObservatory(weatherProcess.get(), nullptr); emit newModule("Weather"); } +void Manager::initObservatory(Weather *weather, Dome *dome) +{ + if (observatoryProcess.get() == nullptr) + { + // Initialize the Observatory Module + observatoryProcess.reset(new Ekos::Observatory()); + int index = toolsWidget->addTab(observatoryProcess.get(), QIcon(":/icons/ekos_observatory.png"), ""); + toolsWidget->tabBar()->setTabToolTip(index, i18n("Observatory")); + connect(observatoryProcess.get(), &Ekos::Observatory::newLog, this, &Ekos::Manager::updateLog); + } + + Observatory *obs = observatoryProcess.get(); + if (weather != nullptr) + obs->getWeatherModel()->initModel(weather); + if (dome != nullptr) + obs->getDomeModel()->initModel(dome); + + emit newModule("Observatory"); + +} + void Manager::initDustCap() { if (dustCapProcess.get() != nullptr) @@ -2293,6 +2319,7 @@ mountProcess.reset(); domeProcess.reset(); weatherProcess.reset(); + observatoryProcess.reset(); dustCapProcess.reset(); managedDevices.clear(); diff --git a/kstars/ekos/observatory/observatory.h b/kstars/ekos/observatory/observatory.h new file mode 100644 --- /dev/null +++ b/kstars/ekos/observatory/observatory.h @@ -0,0 +1,66 @@ +/* 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 "observatorydomemodel.h" +#include "observatoryweathermodel.h" +#include "indiweather.h" + +#include +#include +#include "klocalizedstring.h" + + +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 mDomeModel; } + ObservatoryWeatherModel *getWeatherModel() { return mWeatherModel; } + + // Logging + QStringList logText() { return m_LogText; } + QString getLogText() { return m_LogText.join("\n"); } + +signals: + Q_SCRIPTABLE void newLog(const QString &text); + +private: + ObservatoryDomeModel *mDomeModel; + void setDomeModel(ObservatoryDomeModel *model); + + ObservatoryWeatherModel *mWeatherModel; + void setWeatherModel(ObservatoryWeatherModel *model); + + // Logging + QStringList m_LogText; + void appendLogText(const QString &); + void clearLog(); + +private slots: + void initWeather(); + void shutdownWeather(); + void setWeatherStatus(ISD::Weather::Status status); + + void initDome(); + void shutdownDome(); + + void setDomeStatus(ISD::Dome::Status status); + void setShutterStatus(ISD::Dome::ShutterStatus status); +}; +} diff --git a/kstars/ekos/observatory/observatory.cpp b/kstars/ekos/observatory/observatory.cpp new file mode 100644 --- /dev/null +++ b/kstars/ekos/observatory/observatory.cpp @@ -0,0 +1,250 @@ +/* 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); + setDomeModel(new ObservatoryDomeModel()); + setWeatherModel(new ObservatoryWeatherModel()); +} + +void Observatory::setDomeModel(ObservatoryDomeModel *model) +{ + mDomeModel = model; + if (model != nullptr) + { + connect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome); + connect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus); + connect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus); + } + else + { + shutdownDome(); + 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); + } +} + +void Observatory::initDome() +{ + domeBox->setEnabled(true); + + if (mDomeModel != nullptr) + { + connect(mDomeModel, &Ekos::ObservatoryDomeModel::newLog, this, &Ekos::Observatory::appendLogText); + + if (mDomeModel->canPark()) + { + connect(domePark, &QPushButton::clicked, mDomeModel, &Ekos::ObservatoryDomeModel::park); + connect(domeUnpark, &QPushButton::clicked, mDomeModel, &Ekos::ObservatoryDomeModel::unpark); + domePark->setEnabled(true); + domeUnpark->setEnabled(true); + } + else + { + domePark->setEnabled(false); + domeUnpark->setEnabled(false); + } + + if (mDomeModel->hasShutter()) + { + shutterBox->setVisible(true); + connect(shutterOpen, &QPushButton::clicked, mDomeModel, &Ekos::ObservatoryDomeModel::openShutter); + connect(shutterClosed, &QPushButton::clicked, mDomeModel, &Ekos::ObservatoryDomeModel::closeShutter); + shutterClosed->setEnabled(true); + shutterOpen->setEnabled(true); + } + else + { + shutterBox->setVisible(false); + } + + setDomeStatus(mDomeModel->status()); + setShutterStatus(mDomeModel->shutterStatus()); + } + + // make invisible, since not implemented yet + angleLabel->setVisible(false); + domeAngleSpinBox->setVisible(false); + setDomeAngleButton->setVisible(false); +} + +void Observatory::shutdownDome() +{ + domeBox->setEnabled(false); + shutterBox->setEnabled(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, mDomeModel, &Ekos::ObservatoryDomeModel::park); + disconnect(domeUnpark, &QPushButton::clicked, mDomeModel, &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("PARK"); + domeUnpark->setChecked(true); + domeUnpark->setText("UNPARKED"); + appendLogText("Dome is unparked."); + break; + case ISD::Dome::DOME_MOVING: + appendLogText("Dome is moving..."); + break; + case ISD::Dome::DOME_PARKED: + domePark->setChecked(true); + domePark->setText("PARKED"); + domeUnpark->setChecked(false); + domeUnpark->setText("UNPARK"); + appendLogText("Dome is parked."); + break; + case ISD::Dome::DOME_PARKING: + domePark->setText("PARKING"); + domeUnpark->setText("UNPARK"); + appendLogText("Dome is parking..."); + break; + case ISD::Dome::DOME_UNPARKING: + domePark->setText("PARK"); + domeUnpark->setText("UNPARKING"); + appendLogText("Dome is unparking..."); + break; + case ISD::Dome::DOME_TRACKING: + appendLogText("Dome is tracking."); + break; + default: + break; + } +} + + +void Observatory::setShutterStatus(ISD::Dome::ShutterStatus status) +{ + switch (status) { + case ISD::Dome::SHUTTER_OPEN: + shutterOpen->setChecked(true); + shutterClosed->setChecked(false); + shutterOpen->setText("OPEN"); + shutterClosed->setText("CLOSE"); + appendLogText("Shutter is open."); + break; + case ISD::Dome::SHUTTER_OPENING: + shutterOpen->setText("OPENING"); + shutterClosed->setText("CLOSED"); + appendLogText("Shutter is opening..."); + break; + case ISD::Dome::SHUTTER_CLOSED: + shutterOpen->setChecked(false); + shutterClosed->setChecked(true); + shutterOpen->setText("OPEN"); + shutterClosed->setText("CLOSED"); + appendLogText("Shutter is closed."); + break; + case ISD::Dome::SHUTTER_CLOSING: + shutterOpen->setText("OPEN"); + shutterClosed->setText("CLOSING"); + appendLogText("Shutter is closing..."); + break; + default: + break; + } +} + + + + +void Observatory::setWeatherModel(ObservatoryWeatherModel *model) +{ + mWeatherModel = model; + + if (model != nullptr) + { + connect(model, &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); + initWeather(); + } + else + { + shutdownWeather(); + disconnect(model, &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); + } +} + +void Observatory::initWeather() +{ + weatherBox->setEnabled(true); + weatherLabel->setEnabled(true); + setWeatherStatus(mWeatherModel->status()); +} + +void Observatory::shutdownWeather() +{ + weatherBox->setEnabled(false); + weatherLabel->setEnabled(false); + setWeatherStatus(ISD::Weather::WEATHER_IDLE); +} + + +void Observatory::setWeatherStatus(ISD::Weather::Status status) +{ + std::string label; + switch (status) { + case ISD::Weather::WEATHER_OK: + label = "security-high"; + appendLogText("Weather is OK."); + break; + case ISD::Weather::WEATHER_WARNING: + label = "security-medium"; + appendLogText("Weather WARNING!"); + break; + case ISD::Weather::WEATHER_ALERT: + label = "security-low"; + appendLogText("!! WEATHER ALERT !!"); + break; + default: + label = ""; + break; + } + + weatherStatusLabel->setPixmap(QIcon::fromTheme(label.c_str()) + .pixmap(QSize(48, 48))); +} + + +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.ui b/kstars/ekos/observatory/observatory.ui new file mode 100644 --- /dev/null +++ b/kstars/ekos/observatory/observatory.ui @@ -0,0 +1,354 @@ + + + Observatory + + + + 0 + 0 + 855 + 421 + + + + Form + + + + + + Dome + + + + + + false + + + + 96 + 48 + + + + + 72 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + PARK + + + + 32 + 16 + + + + true + + + + + + + false + + + + 96 + 48 + + + + + 72 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + UNPARKED + + + true + + + false + + + + + + + + + false + + + Angle + + + + + + + false + + + + + + + false + + + + 72 + 24 + + + + + 72 + 24 + + + + Set + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Shutter + + + + + + false + + + + 96 + 48 + + + + + 72 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + CLOSED + + + + 32 + 16 + + + + true + + + + + + + false + + + + 96 + 48 + + + + + 72 + 48 + + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + OPEN + + + true + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Weather + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Weather Status: + + + + + + + + 0 + 0 + + + + + 48 + 48 + + + + + 48 + 48 + + + + + + + + + + + + + + + diff --git a/kstars/ekos/observatory/observatorydomemodel.h b/kstars/ekos/observatory/observatorydomemodel.h new file mode 100644 --- /dev/null +++ b/kstars/ekos/observatory/observatorydomemodel.h @@ -0,0 +1,51 @@ +/* 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 + + +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(); + bool hasShutter() { return (mDome != nullptr && mDome->hasShutter()); } + void openShutter(); + void closeShutter(); + +private: + Dome *mDome; + +signals: + void newStatus(ISD::Dome::Status state); + void newShutterStatus(ISD::Dome::ShutterStatus status); + void ready(); + void newLog(const QString &text); +}; + +} diff --git a/kstars/ekos/observatory/observatorydomemodel.cpp b/kstars/ekos/observatory/observatorydomemodel.cpp new file mode 100644 --- /dev/null +++ b/kstars/ekos/observatory/observatorydomemodel.cpp @@ -0,0 +1,79 @@ +/* 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" + +namespace Ekos +{ + +void ObservatoryDomeModel::initModel(Dome *dome) +{ + mDome = dome; + + connect(mDome, &Dome::ready, this, &ObservatoryDomeModel::ready); + connect(mDome, &Dome::newStatus, this, &ObservatoryDomeModel::newStatus); + connect(mDome, &Dome::newShutterStatus, this, &ObservatoryDomeModel::newShutterStatus); + +} + + +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("Parking dome..."); + mDome->park(); +} + + +void ObservatoryDomeModel::unpark() +{ + if (mDome == nullptr) + return; + + emit newLog("Unparking dome..."); + mDome->unpark(); +} + +void ObservatoryDomeModel::openShutter() +{ + if (mDome == nullptr) + return; + + emit newLog("Opening shutter..."); + mDome->controlShutter(true); +} + +void ObservatoryDomeModel::closeShutter() +{ + if (mDome == nullptr) + return; + + emit newLog("Closing shutter..."); + mDome->controlShutter(false); +} + +} diff --git a/kstars/ekos/observatory/observatoryweathermodel.h b/kstars/ekos/observatory/observatoryweathermodel.h new file mode 100644 --- /dev/null +++ b/kstars/ekos/observatory/observatoryweathermodel.h @@ -0,0 +1,40 @@ +/* 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/weather.h" +#include "indiweather.h" + +#include + +namespace Ekos +{ + +class ObservatoryWeatherModel : public QObject +{ + + Q_OBJECT + +public: + ObservatoryWeatherModel() = default; + + void initModel(Weather *weather); + ISD::Weather::Status status(); + +private: + Weather *mWeather; + +signals: + void newStatus(ISD::Weather::Status state); + void ready(); + +}; + +} diff --git a/kstars/ekos/observatory/observatoryweathermodel.cpp b/kstars/ekos/observatory/observatoryweathermodel.cpp new file mode 100644 --- /dev/null +++ b/kstars/ekos/observatory/observatoryweathermodel.cpp @@ -0,0 +1,34 @@ +/* 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 "observatoryweathermodel.h" + +namespace Ekos +{ + +void ObservatoryWeatherModel::initModel(Weather *weather) +{ + mWeather = weather; + + connect(mWeather, &Weather::ready, this, &ObservatoryWeatherModel::ready); + connect(mWeather, &Weather::newStatus, this, &ObservatoryWeatherModel::newStatus); + + if (mWeather->status() != ISD::Weather::WEATHER_IDLE) + emit ready(); +} + +ISD::Weather::Status ObservatoryWeatherModel::status() +{ + if (mWeather == nullptr) + return ISD::Weather::WEATHER_IDLE; + + return mWeather->status(); +} + +} // Ekos diff --git a/kstars/indi/indidome.h b/kstars/indi/indidome.h --- a/kstars/indi/indidome.h +++ b/kstars/indi/indidome.h @@ -26,18 +26,28 @@ { Q_OBJECT - public: - explicit Dome(GDInterface *iPtr); - typedef enum - { - DOME_IDLE, - DOME_MOVING, - DOME_TRACKING, - DOME_PARKING, - DOME_UNPARKING, - DOME_PARKED, - DOME_ERROR - } Status; +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; @@ -71,29 +81,41 @@ double azimuthPosition() const; bool setAzimuthPosition(double position); + + bool hasShutter() const + { + return m_HasShutter; + } Status status() const { return m_Status; } static const QString getStatusString (Status status); - public slots: + 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 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_CanPark { false }; bool m_CanAbort { false }; + bool m_HasShutter { false }; std::unique_ptr readyTimer; }; } diff --git a/kstars/indi/indidome.cpp b/kstars/indi/indidome.cpp --- a/kstars/indi/indidome.cpp +++ b/kstars/indi/indidome.cpp @@ -51,6 +51,7 @@ 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"); @@ -63,6 +64,7 @@ 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"); @@ -83,6 +85,10 @@ { m_CanAbort = true; } + else if (!strcmp(prop->getName(), "DOME_SHUTTER")) + { + m_HasShutter = true; + } DeviceDecorator::registerProperty(prop); } @@ -225,7 +231,64 @@ 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; + } DeviceDecorator::processSwitch(svp); } @@ -325,6 +388,53 @@ 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) diff --git a/kstars/indi/indilistener.cpp b/kstars/indi/indilistener.cpp --- a/kstars/indi/indilistener.cpp +++ b/kstars/indi/indilistener.cpp @@ -288,7 +288,8 @@ emit newFocuser(gd); } - else if (!strcmp(prop->getName(), "DOME_MOTION")) + else if (!strcmp(prop->getName(), "DOME_SHUTTER") || + !strcmp(prop->getName(), "DOME_MOTION")) { if (gd->getType() == KSTARS_UNKNOWN) { diff --git a/kstars/org.kde.kstars.Ekos.Observatory.xml b/kstars/org.kde.kstars.Ekos.Observatory.xml new file mode 100644 --- /dev/null +++ b/kstars/org.kde.kstars.Ekos.Observatory.xml @@ -0,0 +1,6 @@ + + + + + +