Instrument Neutral Distributed Interface INDI 2.2.5
universal_ror_client.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2024 Jasem Mutlaq. All rights reserved.
3
4 INDI Universal ROR Client. It connects to INDI server running locally at
5 localhost:7624. It checks for both input and output drivers.
6
7 Output driver is used to command Open, Close, and Stop ROR.
8 Input driver is used to query the fully closed and opened states.
9
10 The client does NOT stop the roof if the limit switches are activated. This
11 is the responsiblity of the external hardware.
12
13 This program is free software; you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by the Free
15 Software Foundation; either version 2 of the License, or (at your option)
16 any later version.
17
18 This program is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 more details.
22
23 You should have received a copy of the GNU Library General Public License
24 along with this library; see the file COPYING.LIB. If not, write to
25 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 Boston, MA 02110-1301, USA.
27
28 The full GNU General Public License is included in this distribution in the
29 file called LICENSE.
30*******************************************************************************/
31
33#include "indilogger.h"
34
35#include <cmath>
36#include <cstring>
37
41UniversalRORClient::UniversalRORClient(const std::string &input, const std::string &output) : m_Input(input),
42 m_Output(output)
43{
44 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Client initialized with input driver: %s, output driver: %s",
45 input.c_str(), output.c_str());
46}
47
54
59{
60 if (dp.isDeviceNameMatch(m_Input) && dp.isConnected())
61 {
62 m_InputReady = true;
63 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Input device '%s' is ready", m_Input.c_str());
64 }
65
66 if (dp.isDeviceNameMatch(m_Output) && dp.isConnected())
67 {
68 m_OutputReady = true;
69 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Output device '%s' is ready", m_Output.c_str());
70 }
71
72 // If we just became connected and we have a connection callback, call it
73 if (m_InputReady && m_OutputReady && m_ConnectionCallback)
74 {
75 DEBUGDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Both devices are now connected, triggering connection callback");
76 m_ConnectionCallback(true);
77 }
78}
79
87
92{
93 INDI_UNUSED(exitCode);
94 m_InputReady = m_OutputReady = false;
95 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Server disconnected with exit code %d", exitCode);
96}
97
102{
103 // If we're not fully connected yet, let's check.
104 if (!m_InputReady || !m_OutputReady)
105 {
106 if (property.isNameMatch("CONNECTION"))
107 {
108 auto toggled = property.getSwitch()->at(0)->getState() == ISS_ON;
109 if (property.isDeviceNameMatch(m_Input))
110 m_InputReady = toggled;
111 if (property.isDeviceNameMatch(m_Output))
112 m_OutputReady = toggled;
113
114 if (m_InputReady && m_OutputReady && m_ConnectionCallback)
115 {
116 DEBUGDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Both devices are now connected, triggering connection callback");
117 m_ConnectionCallback(true);
118 }
119
120 return;
121 }
122 }
123
124 auto fullyOpenedUpdated = false, fullyClosedUpdated = false;
125 for (const auto &value : m_InputFullyOpened)
126 {
127 const auto name = "DIGITAL_INPUT_" + std::to_string(value);
128 if (property.isNameMatch(name))
129 {
130 fullyOpenedUpdated = true;
131 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Fully opened input %s updated", name.c_str());
132 }
133 }
134
135 if (fullyOpenedUpdated)
137
138 for (const auto &value : m_InputFullyClosed)
139 {
140 const auto name = "DIGITAL_INPUT_" + std::to_string(value);
141 if (property.isNameMatch(name))
142 {
143 fullyClosedUpdated = true;
144 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Fully closed input %s updated", name.c_str());
145 }
146 }
147
148 if (fullyClosedUpdated)
150}
151
156{
157 auto device = getDevice(m_Output.c_str());
158 if (!device || !device.isConnected())
159 {
160 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Cannot open roof - Output device '%s' not connected",
161 m_Output.c_str());
162 return false;
163 }
164
165 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Opening roof using output device '%s'", m_Output.c_str());
166
167 for (const auto &value : m_OutputOpenRoof)
168 {
169 const auto name = "DIGITAL_OUTPUT_" + std::to_string(value);
170 auto property = device.getSwitch(name.c_str());
171 if (property)
172 {
173 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Setting output %s to ON", name.c_str());
174 property.reset();
175 property[1].setState(ISS_ON);
176 sendNewSwitch(property);
177 }
178 else
179 {
180 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Failed to get switch property for %s", name.c_str());
181 }
182 }
183 return true;
184}
185
190{
191 auto device = getDevice(m_Output.c_str());
192 if (!device || !device.isConnected())
193 {
194 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Cannot close roof - Output device '%s' not connected",
195 m_Output.c_str());
196 return false;
197 }
198
199 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Closing roof using output device '%s'", m_Output.c_str());
200
201 for (const auto &value : m_OutputCloseRoof)
202 {
203 const auto name = "DIGITAL_OUTPUT_" + std::to_string(value);
204 auto property = device.getSwitch(name.c_str());
205 if (property)
206 {
207 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Setting output %s to ON", name.c_str());
208 property.reset();
209 property[1].setState(ISS_ON);
210 sendNewSwitch(property);
211 }
212 else
213 {
214 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Failed to get switch property for %s", name.c_str());
215 }
216 }
217 return true;
218}
219
224{
225 auto device = getDevice(m_Output.c_str());
226 if (!device || !device.isConnected())
227 {
228 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Cannot stop roof - Output device '%s' not connected",
229 m_Output.c_str());
230 return false;
231 }
232
233 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Stopping roof movement using output device '%s'", m_Output.c_str());
234
235 // Find all close roof digital outputs and set them to OFF
236 // Only send OFF if required
237 for (const auto &value : m_OutputCloseRoof)
238 {
239 const auto name = "DIGITAL_OUTPUT_" + std::to_string(value);
240 auto property = device.getSwitch(name.c_str());
241 if (property && property[0].getState() != ISS_ON)
242 {
243 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Setting close output %s to OFF", name.c_str());
244 property.reset();
245 property[0].setState(ISS_ON);
246 sendNewSwitch(property);
247 }
248 }
249
250 // Same for Open roof digital outputs
251 for (const auto &value : m_OutputOpenRoof)
252 {
253 const auto name = "DIGITAL_OUTPUT_" + std::to_string(value);
254 auto property = device.getSwitch(name.c_str());
255 if (property && property[0].getState() != ISS_ON)
256 {
257 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Setting open output %s to OFF", name.c_str());
258 property.reset();
259 property[0].setState(ISS_ON);
260 sendNewSwitch(property);
261 }
262 }
263 return true;
264}
265
270{
271 auto device = getDevice(m_Input.c_str());
272 if (!device || !device.isConnected())
273 {
274 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Cannot sync open state - Input device '%s' not connected",
275 m_Input.c_str());
276 return false;
277 }
278
279 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Syncing fully opened state from input device '%s'",
280 m_Input.c_str());
281
282 std::vector<bool> fullyOpenedStates;
283 for (const auto &value : m_InputFullyOpened)
284 {
285 const auto name = "DIGITAL_INPUT_" + std::to_string(value);
286 auto property = device.getSwitch(name.c_str());
287 if (property)
288 {
289 auto toggled = property[1].getState() == ISS_ON;
290 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Input %s state: %s", name.c_str(), toggled ? "ON" : "OFF");
291 fullyOpenedStates.push_back(toggled);
292 }
293 else
294 {
295 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Failed to get switch property for %s", name.c_str());
296 return false;
297 }
298 }
299
300 auto on = std::all_of(fullyOpenedStates.begin(), fullyOpenedStates.end(), [](bool value)
301 {
302 return value;
303 });
304 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Fully opened state: %s", on ? "YES" : "NO");
305 m_FullyOpenedCallback(on);
306 return true;
307}
308
313{
314 auto device = getDevice(m_Input.c_str());
315 if (!device || !device.isConnected())
316 {
317 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Cannot sync closed state - Input device '%s' not connected",
318 m_Input.c_str());
319 return false;
320 }
321
322 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Syncing fully closed state from input device '%s'",
323 m_Input.c_str());
324
325 std::vector<bool> fullyClosedStates;
326 for (const auto &value : m_InputFullyClosed)
327 {
328 const auto name = "DIGITAL_INPUT_" + std::to_string(value);
329 auto property = device.getSwitch(name.c_str());
330 if (property)
331 {
332 auto toggled = property[1].getState() == ISS_ON;
333 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Input %s state: %s", name.c_str(), toggled ? "ON" : "OFF");
334 fullyClosedStates.push_back(toggled);
335 }
336 else
337 {
338 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_ERROR, "Failed to get switch property for %s", name.c_str());
339 return false;
340 }
341 }
342
343 auto on = std::all_of(fullyClosedStates.begin(), fullyClosedStates.end(), [](bool value)
344 {
345 return value;
346 });
347 DEBUGFDEVICE("Universal ROR", INDI::Logger::DBG_DEBUG, "Fully closed state: %s", on ? "YES" : "NO");
348 m_FullyClosedCallback(on);
349 return true;
350}
hid_device * device
INDI::BaseDevice getDevice(const char *deviceName)
void sendNewSwitch(INDI::Property pp)
Send new Switch command to server.
Class to provide basic INDI device functionality.
Definition basedevice.h:52
bool isConnected() const
bool isDeviceNameMatch(const char *otherName) const
Check that the device name matches the argument.
Provides generic container for INDI properties.
bool isDeviceNameMatch(const char *otherDeviceName) const
bool isNameMatch(const char *otherName) const
bool syncFullyOpenedState()
Checks fully opened state properties.
virtual void serverDisconnected(int exitCode) override
Emmited when the server gets disconnected.
bool stop()
Sets both Close & Open roof outputs to OFF.
virtual void newDevice(INDI::BaseDevice dp) override
Emmited when a new device is created from INDI server.
UniversalRORClient(const std::string &input, const std::string &output)
virtual void newProperty(INDI::Property property) override
Emmited when a new property is created for an INDI driver.
virtual void updateProperty(INDI::Property property) override
Check if any of the relevant digital inputs are for the fully opened or closed states.
@ ISS_ON
Definition indiapi.h:150
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define DEBUGDEVICE(device, priority, msg)
Definition indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61