Instrument Neutral Distributed Interface INDI 2.2.5
telescope_bridge_base.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2025 Jasem Mutlaq. All rights reserved.
3
4 INDI Alpaca Telescope Bridge - Base Implementation
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 The full GNU General Public License is included in this distribution in the
22 file called LICENSE.
23*******************************************************************************/
24
25#include "telescope_bridge.h"
26#include "indilogger.h"
27#include "device_manager.h"
28
29#include <httplib.h>
30#include <cmath>
31#include <cstring>
32#include <sstream>
33
34#ifdef _USE_SYSTEM_JSONLIB
35#include <nlohmann/json.hpp>
36#else
37#include <indijson.hpp>
38#endif
39
41
43 : m_Device(device), m_DeviceNumber(deviceNumber)
44{
45 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_SESSION, "Created telescope bridge for device %s with number %d",
46 device.getDeviceName(), deviceNumber);
47}
48
50{
51 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_SESSION, "Destroyed telescope bridge for device %s",
52 m_Device.getDeviceName());
53}
54
56{
57 return m_Device.getDeviceName();
58}
59
61{
62 // Generate a unique ID based on the device name
63 return "INDI_" + std::string(m_Device.getDeviceName());
64}
65
66void TelescopeBridge::handleRequest(const std::string &method, const httplib::Request &req, httplib::Response &res)
67{
68 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Handling telescope request: %s", method.c_str());
69
70 // Common methods
71 if (method == "connected")
72 handleConnected(req, res);
73 else if (method == "name")
74 handleName(req, res);
75 else if (method == "description")
76 handleDescription(req, res);
77 else if (method == "driverinfo")
78 handleDriverInfo(req, res);
79 else if (method == "driverversion")
80 handleDriverVersion(req, res);
81 else if (method == "interfaceversion")
82 handleInterfaceVersion(req, res);
83 // Telescope-specific properties
84 else if (method == "alignmentmode")
85 handleAlignmentMode(req, res);
86 else if (method == "altitude")
87 handleAltitude(req, res);
88 else if (method == "azimuth")
89 handleAzimuth(req, res);
90 else if (method == "canpark")
91 handleCanPark(req, res);
92 else if (method == "canpulseguide")
93 handleCanPulseGuide(req, res);
94 else if (method == "cansettracking")
95 handleCanSetTracking(req, res);
96 else if (method == "cansetrightascensionrate")
97 handleCanSetRightAscensionRate(req, res);
98 else if (method == "cansetdeclinationrate")
99 handleCanSetDeclinationRate(req, res);
100 else if (method == "canslew")
101 handleCanSlew(req, res);
102 else if (method == "canmoveaxis")
103 handleCanMoveAxis(req, res);
104 else if (method == "canslewasync")
105 handleCanSlewAsync(req, res);
106 else if (method == "canslewaltazasync")
107 handleCanSlewAltAzAsync(req, res);
108 else if (method == "cansync")
109 handleCanSync(req, res);
110 else if (method == "declination")
111 handleDeclination(req, res);
112 else if (method == "declinationrate")
113 handleDeclinationRate(req, res);
114 else if (method == "rightascension")
115 handleRightAscension(req, res);
116 else if (method == "rightascensionrate")
117 handleRightAscensionRate(req, res);
118 else if (method == "sideofpier")
119 handleSideOfPier(req, res);
120 else if (method == "slewing")
121 handleSlewing(req, res);
122 else if (method == "tracking")
123 handleTracking(req, res);
124 else if (method == "atpark")
125 handleAtPark(req, res);
126 // Telescope-specific actions
127 else if (method == "abortslew")
128 handleAbortSlew(req, res);
129 else if (method == "park")
130 handlePark(req, res);
131 else if (method == "unpark")
132 handleUnpark(req, res);
133 else if (method == "slewtocoordinates")
134 handleSlewToCoordinates(req, res);
135 else if (method == "slewtocoordinatesasync")
136 handleSlewToCoordinatesAsync(req, res);
137 else if (method == "synctocoordinates")
138 handleSyncToCoordinates(req, res);
139 else if (method == "pulseguide")
140 handlePulseGuide(req, res);
141 else if (method == "moveaxis")
142 handleMoveAxis(req, res);
143 else if (method == "axisrates")
144 handleAxisRates(req, res);
145 else if (method == "settracking")
146 handleSetTracking(req, res);
147 else if (method == "setrightascensionrate")
148 handleSetRightAscensionRate(req, res);
149 else if (method == "setdeclinationrate")
150 handleSetDeclinationRate(req, res);
151 else if (method == "equatorialsystem")
152 handleEquatorialSystem(req, res);
153 else if (method == "sitelatitude")
154 handleSiteLatitude(req, res);
155 else if (method == "sitelongitude")
156 handleSiteLongitude(req, res);
157 else if (method == "siteelevation")
158 handleSiteElevation(req, res);
159 else
160 {
161 // Unknown method
162 json response =
163 {
164 {"ErrorNumber", 1025},
165 {"ErrorMessage", "Method not implemented: " + method}
166 };
167 res.status = 400;
168 res.set_content(response.dump(), "application/json");
169 }
170}
171
173{
174 std::lock_guard<std::mutex> lock(m_Mutex);
175
176 const char *propName = property.getName();
177 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updating property: %s", propName);
178
179 // Update telescope state based on property updates
180 if (property.isNameMatch("EQUATORIAL_EOD_COORD"))
181 {
182 // Update RA/DEC
183 INDI::PropertyNumber numberProperty(property);
184 for (auto &num : numberProperty)
185 {
186 if (num.isNameMatch("RA"))
187 m_CurrentRA = num.getValue();
188 else if (num.isNameMatch("DEC"))
189 m_CurrentDEC = num.getValue();
190 }
191 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updated RA/DEC: %f, %f", m_CurrentRA, m_CurrentDEC);
192 }
193 else if (property.isNameMatch("HORIZONTAL_COORD"))
194 {
195 // Update AZ/ALT
196 INDI::PropertyNumber numberProperty(property);
197 for (auto &num : numberProperty)
198 {
199 if (num.isNameMatch("AZ"))
200 m_CurrentAZ = num.getValue();
201 else if (num.isNameMatch("ALT"))
202 m_CurrentALT = num.getValue();
203 }
204 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updated AZ/ALT: %f, %f", m_CurrentAZ, m_CurrentALT);
205 }
206 else if (property.isNameMatch("TELESCOPE_TRACK_STATE"))
207 {
208 // Update tracking state
209 INDI::PropertySwitch switchProperty(property);
210 m_IsTracking = switchProperty[0].getState() == ISS_ON;
211 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updated tracking state: %s", m_IsTracking ? "ON" : "OFF");
212 }
213 else if (property.isNameMatch("TELESCOPE_PARK"))
214 {
215 // Update park state
216 INDI::PropertySwitch switchProperty(property);
217 m_IsParked = switchProperty[0].getState() == ISS_ON;
218 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updated park state: %s",
219 m_IsParked ? "PARKED" : "UNPARKED");
220 }
221 else if (property.isNameMatch("TELESCOPE_MOTION_NS") || property.isNameMatch("TELESCOPE_MOTION_WE"))
222 {
223 // Update slewing state
224 INDI::PropertySwitch switchProperty(property);
225 bool isMoving = (switchProperty[0].getState() == ISS_ON || switchProperty[1].getState() == ISS_ON);
226 if (isMoving)
227 m_IsSlewing = true;
228 else
229 {
230 // Check if the other axis is still moving
231 if (property.isNameMatch("TELESCOPE_MOTION_NS"))
232 {
233 auto weSp = m_Device.getSwitch("TELESCOPE_MOTION_WE");
234 if (weSp)
235 {
236 INDI::PropertySwitch weSwitch(weSp);
237 m_IsSlewing = (weSwitch[0].getState() == ISS_ON || weSwitch[1].getState() == ISS_ON);
238 }
239 else
240 m_IsSlewing = false;
241 }
242 else
243 {
244 auto nsSp = m_Device.getSwitch("TELESCOPE_MOTION_NS");
245 if (nsSp)
246 {
247 INDI::PropertySwitch nsSwitch(nsSp);
248 m_IsSlewing = (nsSwitch[0].getState() == ISS_ON || nsSwitch[1].getState() == ISS_ON);
249 }
250 else
251 m_IsSlewing = false;
252 }
253 }
254 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updated slewing state: %s",
255 m_IsSlewing ? "SLEWING" : "NOT SLEWING");
256 }
257 else if (property.isNameMatch("TELESCOPE_PIER_SIDE"))
258 {
259 // Update pier side
260 INDI::PropertySwitch switchProperty(property);
261 if (switchProperty[0].getState() == ISS_ON)
262 m_PierSide = 0; // PIER_WEST in Alpaca
263 else if (switchProperty[1].getState() == ISS_ON)
264 m_PierSide = 1; // PIER_EAST in Alpaca
265 else
266 m_PierSide = -1; // PIER_UNKNOWN in Alpaca
267 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updated pier side: %d", m_PierSide);
268 }
269 else if (property.isNameMatch("TELESCOPE_TRACK_RATE"))
270 {
271 // Update tracking rates
272 INDI::PropertyNumber numberProperty(property);
273 for (auto &num : numberProperty)
274 {
275 if (num.isNameMatch("TRACK_RATE_RA"))
276 m_RightAscensionRate = num.getValue();
277 else if (num.isNameMatch("TRACK_RATE_DE"))
278 m_DeclinationRate = num.getValue();
279 }
280 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Updated tracking rates: RA=%f, DEC=%f",
281 m_RightAscensionRate, m_DeclinationRate);
282 }
283}
284
285// Helper methods for sending commands to INDI server
286void TelescopeBridge::requestNewNumber(const INDI::PropertyNumber &numberProperty)
287{
288 // Forward the request to the DeviceManager
289 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Request to send new number property: %s",
290 numberProperty.getName());
291
292 DeviceManager::getInstance()->sendNewNumber(numberProperty);
293}
294
295void TelescopeBridge::requestNewSwitch(const INDI::PropertySwitch &switchProperty)
296{
297 // Forward the request to the DeviceManager
298 DEBUGFDEVICE(m_Device.getDeviceName(), INDI::Logger::DBG_DEBUG, "Request to send new switch property: %s",
299 switchProperty.getName());
300
301 DeviceManager::getInstance()->sendNewSwitch(switchProperty);
302}
303
304
305// Helper methods for sending standard JSON responses
306void TelescopeBridge::sendResponse(httplib::Response &res, bool success, const std::string &errorMessage)
307{
308 // Use default transaction IDs (0) for backward compatibility
309 int clientID = 0;
310 int serverID = 0;
311
312 json response =
313 {
314 {"ClientTransactionID", clientID},
315 {"ServerTransactionID", serverID},
316 {"ErrorNumber", success ? 0 : 1},
317 {"ErrorMessage", success ? "" : errorMessage}
318 };
319 res.set_content(response.dump(), "application/json");
320}
321
322// Implementation of template methods
323template <typename T>
324void TelescopeBridge::sendResponse(httplib::Response &res, const T &value, bool success,
325 const std::string &errorMessage, int clientID, int serverID)
326{
327 json response =
328 {
329 {"Value", value},
330 {"ClientTransactionID", clientID},
331 {"ServerTransactionID", serverID},
332 {"ErrorNumber", success ? 0 : 1},
333 {"ErrorMessage", success ? "" : errorMessage}
334 };
335 res.set_content(response.dump(), "application/json");
336}
337
338template <typename T>
339void TelescopeBridge::sendResponseValue(httplib::Response &res, const T &value,
340 bool success, const std::string &errorMessage)
341{
342 sendResponse(res, value, success, errorMessage, 0, 0);
343}
344
345// Implementation of sendResponseStatus
346void TelescopeBridge::sendResponseStatus(httplib::Response &res, bool success,
347 const std::string &errorMessage)
348{
349 json response =
350 {
351 {"ErrorNumber", success ? 0 : 1},
352 {"ErrorMessage", success ? "" : errorMessage}
353 };
354 res.set_content(response.dump(), "application/json");
355}
356
357// Explicit template instantiations for common types
358template void TelescopeBridge::sendResponse<std::string>(httplib::Response &res, const std::string &value, bool success,
359 const std::string &errorMessage, int clientID, int serverID);
360template void TelescopeBridge::sendResponse<double>(httplib::Response &res, const double &value, bool success,
361 const std::string &errorMessage, int clientID, int serverID);
362template void TelescopeBridge::sendResponse<int>(httplib::Response &res, const int &value, bool success,
363 const std::string &errorMessage, int clientID, int serverID);
364template void TelescopeBridge::sendResponse<bool>(httplib::Response &res, const bool &value, bool success,
365 const std::string &errorMessage, int clientID, int serverID);
366template void TelescopeBridge::sendResponse<json>(httplib::Response &res, const json &value, bool success,
367 const std::string &errorMessage, int clientID, int serverID);
368
369template void TelescopeBridge::sendResponseValue<std::string>(httplib::Response &res,
370 const std::string &value, bool success, const std::string &errorMessage);
371template void TelescopeBridge::sendResponseValue<double>(httplib::Response &res,
372 const double &value, bool success, const std::string &errorMessage);
373template void TelescopeBridge::sendResponseValue<int>(httplib::Response &res,
374 const int &value, bool success, const std::string &errorMessage);
375template void TelescopeBridge::sendResponseValue<bool>(httplib::Response &res,
376 const bool &value, bool success, const std::string &errorMessage);
377template void TelescopeBridge::sendResponseValue<json>(httplib::Response &res,
378 const json &value, bool success, const std::string &errorMessage);
hid_device * device
void sendNewSwitch(const INDI::PropertySwitch &switchProperty)
static DeviceManager * getInstance()
void sendNewNumber(const INDI::PropertyNumber &numberProperty)
Class to provide basic INDI device functionality.
Definition basedevice.h:52
const char * getDeviceName() const
INDI::PropertySwitch getSwitch(const char *name) const
const char * getName() const
Provides generic container for INDI properties.
bool isNameMatch(const char *otherName) const
void handleConnected(const httplib::Request &req, httplib::Response &res) override
void handleInterfaceVersion(const httplib::Request &req, httplib::Response &res) override
void handleDriverVersion(const httplib::Request &req, httplib::Response &res) override
void handleName(const httplib::Request &req, httplib::Response &res) override
std::string getDeviceName() const override
void handleDescription(const httplib::Request &req, httplib::Response &res) override
TelescopeBridge(INDI::BaseDevice device, int deviceNumber)
void handleDriverInfo(const httplib::Request &req, httplib::Response &res) override
void handleRequest(const std::string &method, const httplib::Request &req, httplib::Response &res) override
void updateProperty(INDI::Property property) override
std::string getUniqueID() const override
a class to store JSON values
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
@ ISS_ON
Definition indiapi.h:150
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61
basic_json<> json
default specialization
void set_content(const char *s, size_t n, const std::string &content_type)
Definition httplib.h:9995