From 70722a0c37e0d6bacdafd1e3349490137c42168a Mon Sep 17 00:00:00 2001 From: Jasem Mutlaq Date: Mon, 29 Jul 2024 17:13:51 +0300 Subject: [PATCH] Universal Roll off roof driver (#2092) * Add universal ROR driver and client * Driver is operational. Need one field test * Stop relays when parked or unparked is detected. --- drivers.xml | 4 + drivers/dome/CMakeLists.txt | 10 + drivers/dome/universal_ror.cpp | 423 ++++++++++++++++++++++++++ drivers/dome/universal_ror.h | 75 +++++ drivers/dome/universal_ror_client.cpp | 241 +++++++++++++++ drivers/dome/universal_ror_client.h | 96 ++++++ libs/indibase/indidome.cpp | 22 +- libs/indibase/indidome.h | 13 +- 8 files changed, 874 insertions(+), 10 deletions(-) create mode 100644 drivers/dome/universal_ror.cpp create mode 100644 drivers/dome/universal_ror.h create mode 100644 drivers/dome/universal_ror_client.cpp create mode 100644 drivers/dome/universal_ror_client.h diff --git a/drivers.xml b/drivers.xml index 00c597e427..e07ad39f87 100644 --- a/drivers.xml +++ b/drivers.xml @@ -843,6 +843,10 @@ indi_dragonlair_dome 1.0 + + indi_universalror_dome + 0.1 + diff --git a/drivers/dome/CMakeLists.txt b/drivers/dome/CMakeLists.txt index 377af11d2f..f2cd19a906 100644 --- a/drivers/dome/CMakeLists.txt +++ b/drivers/dome/CMakeLists.txt @@ -82,3 +82,13 @@ SET(dragonlair_SRC add_executable(indi_dragonlair_dome ${dragonlair_SRC}) target_link_libraries(indi_dragonlair_dome indidriver ${HTTPLIB_LIBRARY}) install(TARGETS indi_dragonlair_dome RUNTIME DESTINATION bin) + +# ############### Universal ROR ################ +SET(universalror_SRC + universal_ror.cpp + universal_ror_client.cpp + ) + +add_executable(indi_universalror_dome ${universalror_SRC}) +target_link_libraries(indi_universalror_dome indidriver indiclient) +install(TARGETS indi_universalror_dome RUNTIME DESTINATION bin) diff --git a/drivers/dome/universal_ror.cpp b/drivers/dome/universal_ror.cpp new file mode 100644 index 0000000000..9b58171c6e --- /dev/null +++ b/drivers/dome/universal_ror.cpp @@ -0,0 +1,423 @@ +/******************************************************************************* + Universal Roll Off Roof driver. + Copyright(c) 2014 Jasem Mutlaq. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + . + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*******************************************************************************/ + +#include "universal_ror.h" +#include +#include +#include + +// We declare an auto pointer to UniversalROR. +std::unique_ptr ror(new UniversalROR()); + +UniversalROR::UniversalROR() +{ + SetDomeCapability(DOME_CAN_ABORT | DOME_CAN_PARK); +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::initProperties() +{ + INDI::Dome::initProperties(); + SetParkDataType(PARK_NONE); + addAuxControls(); + + // Input Indexes + InputTP[FullyOpened].fill("FULLY_OPENED", "Fully Opened", "Comma separated indexes"); + InputTP[FullyClosed].fill("FULLY_CLOSED", "Fully Closed", "Comma separated indexes"); + InputTP.fill(getDeviceName(), "INPUT_INDEX", "Input Indexes", OPTIONS_TAB, IP_RW, 60, IPS_IDLE); + InputTP.load(); + + // Output Indexes + OutputTP[OpenRoof].fill("OPEN_ROOF", "Open Roof", "Comma separated indexes"); + OutputTP[CloseRoof].fill("CLOSE_ROOF", "Close Roof", "Comma separated indexes"); + OutputTP.fill(getDeviceName(), "OUTPUT_INDEX", "Output Indexes", OPTIONS_TAB, IP_RW, 60, IPS_IDLE); + OutputTP.load(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::ISSnoopDevice(XMLEle *root) +{ + return INDI::Dome::ISSnoopDevice(root); +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::setupParms() +{ + // If we have parking data + InitPark(); + + // If limit switches are not identical then we have a known + // parking state that we should set. + if (m_FullClosedLimitSwitch != m_FullOpenLimitSwitch) + { + if (m_FullClosedLimitSwitch && !isParked()) + SetParked(true); + else if (m_FullOpenLimitSwitch && isParked()) + SetParked(false); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::Connect() +{ + if (!m_Client || !m_Client->isConnected()) + { + LOG_ERROR("ROR Client is not connected. Specify the input and output drivers in Options tab."); + return false; + + } + + syncIndexes(); + SetTimer(getPollingPeriod()); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::Disconnect() +{ + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +const char *UniversalROR::getDefaultName() +{ + return "Universal ROR"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) +{ + return INDI::Dome::ISNewSwitch(dev, name, states, names, n); +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n) +{ + if (dev && !strcmp(dev, getDeviceName())) + { + // Input Indexes + if (InputTP.isNameMatch(name)) + { + InputTP.update(texts, names, n); + InputTP.setState(IPS_OK); + InputTP.apply(); + saveConfig(InputTP); + syncIndexes(); + return true; + } + + // Output Indexes + if (OutputTP.isNameMatch(name)) + { + OutputTP.update(texts, names, n); + OutputTP.setState(IPS_OK); + OutputTP.apply(); + saveConfig(OutputTP); + syncIndexes(); + return true; + } + } + + return INDI::Dome::ISNewText(dev, name, texts, names, n); +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::updateProperties() +{ + INDI::Dome::updateProperties(); + + if (isConnected()) + { + setupParms(); + + defineProperty(InputTP); + defineProperty(OutputTP); + } + else + { + deleteProperty(InputTP); + deleteProperty(OutputTP); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void UniversalROR::TimerHit() +{ + if (!isConnected()) + return; + + if (DomeMotionSP.getState() == IPS_BUSY) + { + // Roll off is opening + if (DomeMotionSP[DOME_CW].getState() == ISS_ON) + { + if (m_FullOpenLimitSwitch) + { + LOG_INFO("Roof is open."); + SetParked(false); + // Make sure the relays are off + if (m_Client) + m_Client->stop(); + return; + } + } + // Roll Off is closing + else if (DomeMotionSP[DOME_CCW].getState() == ISS_ON) + { + if (m_FullClosedLimitSwitch) + { + LOG_INFO("Roof is closed."); + SetParked(true); + // Make sure the relays are off + if (m_Client) + m_Client->stop(); + return; + } + } + } + + SetTimer(getPollingPeriod()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::saveConfigItems(FILE *fp) +{ + INDI::Dome::saveConfigItems(fp); + InputTP.save(fp); + OutputTP.save(fp); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +IPState UniversalROR::Move(DomeDirection dir, DomeMotionCommand operation) +{ + if (operation == MOTION_START) + { + // DOME_CW --> OPEN. If can we are ask to "open" while we are fully opened as the limit switch indicates, then we simply return false. + if (dir == DOME_CW && m_FullOpenLimitSwitch) + { + LOG_WARN("Roof is already fully opened."); + return IPS_ALERT; + } + else if (dir == DOME_CCW && m_FullClosedLimitSwitch) + { + LOG_WARN("Roof is already fully closed."); + return IPS_ALERT; + } + else if (dir == DOME_CCW && INDI::Dome::isLocked()) + { + DEBUG(INDI::Logger::DBG_WARNING, "Cannot close dome when mount is locking. See: Telescope parking policy, in options tab"); + return IPS_ALERT; + } + + if (dir == DOME_CW) + { + if (m_Client) + { + m_Client->openRoof(); + } + else + { + LOG_ERROR("Failed to open roof. ROR Client is not connected!"); + return IPS_ALERT; + } + } + else + { + if (m_Client) + { + m_Client->closeRoof(); + } + else + { + LOG_ERROR("Failed to close roof. ROR Client is not connected!"); + return IPS_ALERT; + } + } + + return IPS_BUSY; + } + + return (Dome::Abort() ? IPS_OK : IPS_ALERT); +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +IPState UniversalROR::Park() +{ + IPState rc = INDI::Dome::Move(DOME_CCW, MOTION_START); + if (rc == IPS_BUSY) + { + LOG_INFO("Roll off is parking..."); + return IPS_BUSY; + } + else + return IPS_ALERT; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +IPState UniversalROR::UnPark() +{ + IPState rc = INDI::Dome::Move(DOME_CW, MOTION_START); + if (rc == IPS_BUSY) + { + LOG_INFO("Roll off is unparking..."); + return IPS_BUSY; + } + else + return IPS_ALERT; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::Abort() +{ + if (m_Client) + return m_Client->stop(); + + return false; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// Drivers are assumed to be on localhost running at port 7624 +/// Clients connects to localhost:7624 server. +//////////////////////////////////////////////////////////////////////////////// +void UniversalROR::ActiveDevicesUpdated() +{ + auto input = std::string(ActiveDeviceTP[ACTIVE_INPUT].getText()); + auto output = std::string(ActiveDeviceTP[ACTIVE_OUTPUT].getText()); + + // If input OR output drivers are missing, do not initialize client. + if (input.empty() || output.empty()) + return; + + m_Client.reset(new UniversalRORClient(input, output)); + m_Client->setFullyClosedCallback([&](bool on) + { + m_FullClosedLimitSwitch = on; + }); + m_Client->setFullyOpenedCallback([&](bool on) + { + m_FullOpenLimitSwitch = on; + }); + m_Client->watchDevice(input.c_str()); + m_Client->watchDevice(output.c_str()); + m_Client->connectServer(); + +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool UniversalROR::syncIndexes() +{ + if (!m_Client) + return false; + + // Input --> Fully Opened Indexes + auto fullyOpenedList = extract(InputTP[FullyOpened].getText()); + if (!fullyOpenedList.empty() && fullyOpenedList != m_InputFullyOpened) + { + m_InputFullyOpened = fullyOpenedList; + m_Client->setInputFullyOpened(m_InputFullyOpened); + } + + // Input --> Fully Closed Indexes + auto fullyClosedList = extract(InputTP[FullyClosed].getText()); + if (!fullyClosedList.empty() && fullyClosedList != m_InputFullyClosed) + { + m_InputFullyClosed = fullyClosedList; + m_Client->setInputFullyClosed(m_InputFullyClosed); + } + + // Output --> Open Roof Indexes + auto openRoofList = extract(OutputTP[OpenRoof].getText()); + if (!openRoofList.empty() && openRoofList != m_OutputOpenRoof) + { + m_OutputOpenRoof = openRoofList; + m_Client->setOutputOpenRoof(m_OutputOpenRoof); + } + + // Output --> Close Roof Indexes + auto closeRoofList = extract(OutputTP[CloseRoof].getText()); + if (!closeRoofList.empty() && closeRoofList != m_OutputCloseRoof) + { + m_OutputCloseRoof = closeRoofList; + m_Client->setOutputCloseRoof(m_OutputCloseRoof); + } + + m_Client->syncFullyOpenedState(); + m_Client->syncFullyClosedState(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +std::vector UniversalROR::extract(const std::string &text) +{ + std::vector result; + std::regex pattern(R"(\d+)"); + std::smatch matches; + std::string remaining = text; + + while (std::regex_search(remaining, matches, pattern)) + { + result.push_back(std::stoi(matches[0])); + remaining = matches.suffix().str(); + } + + return result; +} \ No newline at end of file diff --git a/drivers/dome/universal_ror.h b/drivers/dome/universal_ror.h new file mode 100644 index 0000000000..974b25ca41 --- /dev/null +++ b/drivers/dome/universal_ror.h @@ -0,0 +1,75 @@ +/******************************************************************************* + Universal Roll Off Roof driver. + Copyright(c) 2024 Jasem Mutlaq. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + . + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*******************************************************************************/ + +#pragma once + +#include "indidome.h" +#include "universal_ror_client.h" + +class UniversalROR : public INDI::Dome +{ + public: + UniversalROR(); + virtual ~UniversalROR() = default; + + virtual bool initProperties() override; + const char *getDefaultName() override; + bool updateProperties() override; + + virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override; + virtual bool ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n) override; + virtual bool saveConfigItems(FILE *fp) override; + virtual bool ISSnoopDevice(XMLEle *root) override; + + protected: + bool Connect() override; + bool Disconnect() override; + + void TimerHit() override; + + virtual IPState Move(DomeDirection dir, DomeMotionCommand operation) override; + virtual IPState Park() override; + virtual IPState UnPark() override; + virtual bool Abort() override; + virtual void ActiveDevicesUpdated() override; + + private: + bool setupParms(); + bool syncIndexes(); + std::vector extract(const std::string &text); + bool m_FullOpenLimitSwitch {false}, m_FullClosedLimitSwitch {false}; + + INDI::PropertyText InputTP {2}; + enum + { + FullyOpened, + FullyClosed + }; + + INDI::PropertyText OutputTP {2}; + enum + { + OpenRoof, + CloseRoof + }; + + std::vector m_OutputOpenRoof, m_OutputCloseRoof; + std::vector m_InputFullyOpened, m_InputFullyClosed; + std::unique_ptr m_Client; +}; diff --git a/drivers/dome/universal_ror_client.cpp b/drivers/dome/universal_ror_client.cpp new file mode 100644 index 0000000000..1f0ed16320 --- /dev/null +++ b/drivers/dome/universal_ror_client.cpp @@ -0,0 +1,241 @@ +/******************************************************************************* + Copyright(c) 2024 Jasem Mutlaq. All rights reserved. + + INDI Universal ROR Client. It connects to INDI server running locally at + localhost:7624. It checks for both input and output drivers. + + Output driver is used to command Open, Close, and Stop ROR. + Input driver is used to query the fully closed and opened states. + + The client does NOT stop the roof if the limit switches are activated. This + is the responsiblity of the external hardware. + + This program 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. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. +*******************************************************************************/ + +#include "universal_ror_client.h" + +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////// +/// +/////////////////////////////////////////////////////////////////////////////////////////// +UniversalRORClient::UniversalRORClient(const std::string &input, const std::string &output) : m_Input(input), + m_Output(output) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// +/////////////////////////////////////////////////////////////////////////////////////////// +UniversalRORClient::~UniversalRORClient() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// +/////////////////////////////////////////////////////////////////////////////////////////// +void UniversalRORClient::newDevice(INDI::BaseDevice dp) +{ + if (dp.isDeviceNameMatch(m_Input)) + m_InputReady = true; + if (dp.isDeviceNameMatch(m_Output)) + m_OutputReady = true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// +/////////////////////////////////////////////////////////////////////////////////////////// +void UniversalRORClient::newProperty(INDI::Property property) +{ + updateProperty(property); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// Check if any of the relevant digital inputs are for the fully opened or closed states +/////////////////////////////////////////////////////////////////////////////////////////// +void UniversalRORClient::updateProperty(INDI::Property property) +{ + auto fullyOpenedUpdated = false, fullyClosedUpdated = false; + for (const auto &value : m_InputFullyOpened) + { + const auto name = "DIGITAL_INPUT_" + std::to_string(value); + if (property.isNameMatch(name)) + { + fullyOpenedUpdated = true; + } + } + + if (fullyOpenedUpdated) + syncFullyOpenedState(); + + for (const auto &value : m_InputFullyClosed) + { + const auto name = "DIGITAL_INPUT_" + std::to_string(value); + if (property.isNameMatch(name)) + { + fullyClosedUpdated = true; + } + } + + if (fullyClosedUpdated) + syncFullyClosedState(); + +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// +/////////////////////////////////////////////////////////////////////////////////////////// +bool UniversalRORClient::openRoof() +{ + auto device = getDevice(m_Output.c_str()); + if (!device || !device.isConnected()) + return false; + + for (const auto &value : m_OutputOpenRoof) + { + const auto name = "DIGITAL_OUTPUT_" + std::to_string(value); + auto property = device.getSwitch(name.c_str()); + if (property) + { + property.reset(); + property[1].setState(ISS_ON); + sendNewSwitch(property); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// +/////////////////////////////////////////////////////////////////////////////////////////// +bool UniversalRORClient::closeRoof() +{ + auto device = getDevice(m_Output.c_str()); + if (!device || !device.isConnected()) + return false; + + for (const auto &value : m_OutputCloseRoof) + { + const auto name = "DIGITAL_OUTPUT_" + std::to_string(value); + auto property = device.getSwitch(name.c_str()); + if (property) + { + property.reset(); + property[1].setState(ISS_ON); + sendNewSwitch(property); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// Sets both Close & Open roof outputs to OFF +/////////////////////////////////////////////////////////////////////////////////////////// +bool UniversalRORClient::stop() +{ + auto device = getDevice(m_Output.c_str()); + if (!device || !device.isConnected()) + return false; + + // Find all close roof digital outputs and set them to OFF + // Only send OFF if required + for (const auto &value : m_OutputCloseRoof) + { + const auto name = "DIGITAL_OUTPUT_" + std::to_string(value); + auto property = device.getSwitch(name.c_str()); + if (property && property[0].getState() != ISS_ON) + { + property.reset(); + property[0].setState(ISS_ON); + sendNewSwitch(property); + } + } + + // Same for Open roof digital outputs + for (const auto &value : m_OutputOpenRoof) + { + const auto name = "DIGITAL_OUTPUT_" + std::to_string(value); + auto property = device.getSwitch(name.c_str()); + if (property && property[0].getState() != ISS_ON) + { + property.reset(); + property[0].setState(ISS_ON); + sendNewSwitch(property); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// Checks fully opened state properties. +/////////////////////////////////////////////////////////////////////////////////////////// +void UniversalRORClient::syncFullyOpenedState() +{ + auto device = getDevice(m_Input.c_str()); + if (!device || !device.isConnected()) + return; + + std::vector fullyOpenedStates; + for (const auto &value : m_InputFullyOpened) + { + const auto name = "DIGITAL_INPUT_" + std::to_string(value); + auto property = device.getSwitch(name.c_str()); + if (property) + { + auto toggled = property[1].getState() == ISS_ON; + fullyOpenedStates.push_back(toggled); + } + } + + auto on = std::all_of(fullyOpenedStates.begin(), fullyOpenedStates.end(), [](bool value) + { + return value; + }); + m_FullyOpenedCallback(on); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/// +/////////////////////////////////////////////////////////////////////////////////////////// +void UniversalRORClient::syncFullyClosedState() +{ + auto device = getDevice(m_Input.c_str()); + if (!device || !device.isConnected()) + return; + + std::vector fullyClosedStates; + for (const auto &value : m_InputFullyClosed) + { + const auto name = "DIGITAL_INPUT_" + std::to_string(value); + auto property = device.getSwitch(name.c_str()); + if (property) + { + auto toggled = property[1].getState() == ISS_ON; + fullyClosedStates.push_back(toggled); + } + } + + auto on = std::all_of(fullyClosedStates.begin(), fullyClosedStates.end(), [](bool value) + { + return value; + }); + m_FullyClosedCallback(on); +} \ No newline at end of file diff --git a/drivers/dome/universal_ror_client.h b/drivers/dome/universal_ror_client.h new file mode 100644 index 0000000000..210c94d5b2 --- /dev/null +++ b/drivers/dome/universal_ror_client.h @@ -0,0 +1,96 @@ +/******************************************************************************* + Copyright(c) 2024 Jasem Mutlaq. All rights reserved. + + INDI Universal ROR Client. It connects to INDI server running locally at + localhost:7624. It checks for both input and output drivers. + + Output driver is used to command Open, Close, and Stop ROR. + Input driver is used to query the fully closed and opened states. + + The client does NOT stop the roof if the limit switches are activated. This + is the responsiblity of the external hardware. + + This program 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. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. +*******************************************************************************/ + +#pragma once + +#include "baseclient.h" +#include "basedevice.h" + +class UniversalRORClient : public INDI::BaseClient +{ + public: + UniversalRORClient(const std::string &input, const std::string &output); + ~UniversalRORClient(); + + bool isConnected() + { + return m_InputReady && m_OutputReady; + } + + bool openRoof(); + bool closeRoof(); + bool stop(); + + void setOutputOpenRoof(const std::vector &value) + { + m_OutputOpenRoof = value; + } + + void setOutputCloseRoof(const std::vector &value) + { + m_OutputCloseRoof = value; + } + + void setInputFullyOpened(const std::vector &value) + { + m_InputFullyOpened = value; + } + + void setInputFullyClosed(const std::vector &value) + { + m_InputFullyClosed = value; + } + + void setFullyOpenedCallback(std::function callback) + { + m_FullyOpenedCallback = callback; + } + + void setFullyClosedCallback(std::function callback) + { + m_FullyClosedCallback = callback; + } + + void syncFullyOpenedState(); + void syncFullyClosedState(); + + protected: + virtual void newDevice(INDI::BaseDevice dp) override; + virtual void newProperty(INDI::Property property) override; + virtual void updateProperty(INDI::Property property) override; + + private: + std::string m_Input, m_Output; + bool m_InputReady {false}, m_OutputReady {false}; + std::vector m_OutputOpenRoof, m_OutputCloseRoof; + std::vector m_InputFullyOpened, m_InputFullyClosed; + std::function m_FullyOpenedCallback, m_FullyClosedCallback; +}; diff --git a/libs/indibase/indidome.cpp b/libs/indibase/indidome.cpp index 7be9b156c1..938aad597e 100644 --- a/libs/indibase/indidome.cpp +++ b/libs/indibase/indidome.cpp @@ -119,10 +119,12 @@ bool Dome::initProperties() PresetGotoSP.fill(getDeviceName(), "Goto", "", "Presets", IP_RW, ISR_1OFMANY, 0, IPS_IDLE); // Active Devices - ActiveDeviceTP[0].fill("ACTIVE_TELESCOPE", "Telescope", "Telescope Simulator"); - //IUFillText(&ActiveDeviceT[1], "ACTIVE_WEATHER", "Weather", "WunderGround"); + ActiveDeviceTP[ACTIVE_MOUNT].fill("ACTIVE_TELESCOPE", "Telescope", "Telescope Simulator"); + ActiveDeviceTP[ACTIVE_INPUT].fill("ACTIVE_INPUT", "Input", ""); + ActiveDeviceTP[ACTIVE_OUTPUT].fill("ACTIVE_OUTPUT", "Output", ""); ActiveDeviceTP.fill(getDeviceName(), "ACTIVE_DEVICES", "Snoop devices", OPTIONS_TAB, IP_RW, 60, IPS_IDLE); - ActiveDeviceTP.load(); + if (ActiveDeviceTP.load()) + ActiveDevicesUpdated(); // Use locking if telescope is unparked MountPolicySP[MOUNT_IGNORED].fill("MOUNT_IGNORED", "Mount ignored", ISS_ON); @@ -793,14 +795,16 @@ bool Dome::ISNewText(const char * dev, const char * name, char * texts[], char * ActiveDeviceTP.update(texts, names, n); ActiveDeviceTP.apply(); - const auto scope = ActiveDeviceTP[0].getText(); - IDSnoopDevice(scope, "EQUATORIAL_EOD_COORD"); - IDSnoopDevice(scope, "TARGET_EOD_COORD"); - IDSnoopDevice(scope, "GEOGRAPHIC_COORD"); - IDSnoopDevice(scope, "TELESCOPE_PARK"); + auto mount = ActiveDeviceTP[ACTIVE_MOUNT].getText(); + IDSnoopDevice(mount, "EQUATORIAL_EOD_COORD"); + IDSnoopDevice(mount, "TARGET_EOD_COORD"); + IDSnoopDevice(mount, "GEOGRAPHIC_COORD"); + IDSnoopDevice(mount, "TELESCOPE_PARK"); if (CanAbsMove()) - IDSnoopDevice(scope, "TELESCOPE_PIER_SIDE"); + IDSnoopDevice(mount, "TELESCOPE_PIER_SIDE"); + saveConfig(ActiveDeviceTP); + ActiveDevicesUpdated(); return true; } } diff --git a/libs/indibase/indidome.h b/libs/indibase/indidome.h index e3f94c43e0..9c55d3d60c 100644 --- a/libs/indibase/indidome.h +++ b/libs/indibase/indidome.h @@ -521,6 +521,11 @@ class Dome : public DefaultDevice /** \brief perform handshake with device to check communication */ virtual bool Handshake(); + /** + * Signal to concrete driver when Active Devices are updated. + */ + virtual void ActiveDevicesUpdated() {}; + double Csc(double x); double Sec(double x); @@ -546,7 +551,13 @@ class Dome : public DefaultDevice INDI::PropertySwitch ParkOptionSP {3}; - INDI::PropertyText ActiveDeviceTP {1}; + INDI::PropertyText ActiveDeviceTP {3}; + enum + { + ACTIVE_MOUNT, + ACTIVE_INPUT, + ACTIVE_OUTPUT + }; // Switch to lock id mount is unparked INDI::PropertySwitch MountPolicySP {2};