34#ifdef _USE_SYSTEM_JSONLIB
35#include <nlohmann/json.hpp>
54 std::map<std::string, std::string> formData;
58 if (formData.find(
"Connected") == formData.end())
60 sendResponseValue(res, isConnected,
false,
"Missing Connected parameter");
65 bool connected = (formData[
"Connected"] ==
"True" || formData[
"Connected"] ==
"true" ||
66 formData[
"Connected"] ==
"1");
68 if (connected && !isConnected)
71 auto connectSw = m_Device.
getSwitch(
"CONNECTION");
75 switchProperty.
reset();
78 requestNewSwitch(switchProperty);
79 sendResponseValue(res,
true);
83 sendResponseValue(res,
false,
"Failed to connect device");
86 else if (!connected && isConnected)
89 auto connectSw = m_Device.
getSwitch(
"CONNECTION");
93 switchProperty.
reset();
96 requestNewSwitch(switchProperty);
97 sendResponseValue(res,
false);
101 sendResponseValue(res,
true,
"Failed to disconnect device");
107 sendResponseValue(res, isConnected);
110 catch (
const std::exception &e)
112 sendResponseValue(res, isConnected,
false, std::string(
"Invalid request: ") + e.what());
118 sendResponseValue(res, isConnected);
126 sendResponseValue(res, deviceName);
132 std::string description = std::string(
"INDI Telescope: ") + m_Device.
getDeviceName();
133 sendResponseValue(res, description);
139 std::string driverInfo =
"INDI Alpaca Bridge";
140 sendResponseValue(res, driverInfo);
146 std::string version =
"1.0";
147 sendResponseValue(res, version);
153 sendResponseValue(res, 1);
162 auto mountTypeSw = m_Device.
getSwitch(
"TELESCOPE_MOUNT_TYPE");
173 sendResponseValue(res, switchProperty.findOnSwitchIndex());
181 if (hasHorizontalCoord && !hasEquatorialCoord)
183 sendResponseValue(res, 0);
188 sendResponseValue(res, 2);
196 std::lock_guard<std::mutex> lock(m_Mutex);
197 sendResponseValue(res, m_CurrentALT);
203 std::lock_guard<std::mutex> lock(m_Mutex);
204 sendResponseValue(res, m_CurrentAZ);
218 sendResponseValue(res, canPulseGuide);
272 std::lock_guard<std::mutex> lock(m_Mutex);
273 sendResponseValue(res, m_CurrentDEC);
279 std::lock_guard<std::mutex> lock(m_Mutex);
280 sendResponseValue(res, m_DeclinationRate);
286 std::lock_guard<std::mutex> lock(m_Mutex);
287 sendResponseValue(res, m_CurrentRA);
293 std::lock_guard<std::mutex> lock(m_Mutex);
294 sendResponseValue(res, m_RightAscensionRate);
300 std::lock_guard<std::mutex> lock(m_Mutex);
301 sendResponseValue(res, m_PierSide);
307 std::lock_guard<std::mutex> lock(m_Mutex);
308 sendResponseValue(res, m_IsSlewing);
314 std::lock_guard<std::mutex> lock(m_Mutex);
315 sendResponseValue(res, m_IsTracking);
321 std::lock_guard<std::mutex> lock(m_Mutex);
322 sendResponseValue(res, m_IsParked);
329 bool success =
false;
330 auto abortSw = m_Device.
getSwitch(
"TELESCOPE_ABORT_MOTION");
334 switchProperty.reset();
335 switchProperty[0].setState(
ISS_ON);
337 requestNewSwitch(switchProperty);
341 sendResponseStatus(res, success, success ?
"" :
"Failed to abort slew");
347 bool success =
false;
348 auto parkSw = m_Device.
getSwitch(
"TELESCOPE_PARK");
352 switchProperty.reset();
353 switchProperty[0].setState(
ISS_ON);
355 requestNewSwitch(switchProperty);
359 sendResponseStatus(res, success, success ?
"" :
"Failed to park telescope");
365 bool success =
false;
366 auto parkSw = m_Device.
getSwitch(
"TELESCOPE_PARK");
370 switchProperty.reset();
371 switchProperty[1].setState(
ISS_ON);
373 requestNewSwitch(switchProperty);
377 sendResponseStatus(res, success, success ?
"" :
"Failed to unpark telescope");
382 bool success =
false;
386 std::map<std::string, std::string> formData;
390 if (formData.find(
"RightAscension") == formData.end() || formData.find(
"Declination") == formData.end())
392 sendResponseStatus(res,
false,
"Missing RightAscension or Declination parameter");
397 double ra = std::stod(formData[
"RightAscension"]);
398 double dec = std::stod(formData[
"Declination"]);
400 auto coordSet = m_Device.
getSwitch(
"ON_COORD_SET");
401 auto eqCoord = m_Device.
getNumber(
"EQUATORIAL_EOD_COORD");
402 if (coordSet && eqCoord)
406 switchProperty.reset();
407 switchProperty[0].setState(
ISS_ON);
409 requestNewSwitch(switchProperty);
413 for (
auto &num : numberProperty)
415 if (num.isNameMatch(
"RA"))
417 else if (num.isNameMatch(
"DEC"))
421 requestNewNumber(numberProperty);
429 catch (
const std::exception &e)
434 sendResponseStatus(res, success, success ?
"" :
"Failed to slew to coordinates");
440 handleSlewToCoordinates(req, res);
445 bool success =
false;
449 std::map<std::string, std::string> formData;
453 if (formData.find(
"RightAscension") == formData.end() || formData.find(
"Declination") == formData.end())
455 sendResponseStatus(res,
false,
"Missing RightAscension or Declination parameter");
460 double ra = std::stod(formData[
"RightAscension"]);
461 double dec = std::stod(formData[
"Declination"]);
463 auto coordSet = m_Device.
getSwitch(
"ON_COORD_SET");
464 auto eqCoord = m_Device.
getNumber(
"EQUATORIAL_EOD_COORD");
465 if (coordSet && eqCoord)
469 switchProperty.reset();
470 switchProperty[1].setState(
ISS_ON);
472 requestNewSwitch(switchProperty);
476 for (
auto &num : numberProperty)
478 if (num.isNameMatch(
"RA"))
480 else if (num.isNameMatch(
"DEC"))
484 requestNewNumber(numberProperty);
488 catch (
const std::exception &e)
493 sendResponseStatus(res, success, success ?
"" :
"Failed to sync to coordinates");
498 bool success =
false;
502 std::map<std::string, std::string> formData;
506 if (formData.find(
"Direction") == formData.end() || formData.find(
"Duration") == formData.end())
508 sendResponseStatus(res,
false,
"Missing Direction or Duration parameter");
513 int direction = std::stoi(formData[
"Direction"]);
514 int duration = std::stoi(formData[
"Duration"]);
517 if (direction == 0 || direction == 1)
519 auto guideNS = m_Device.
getNumber(
"TELESCOPE_TIMED_GUIDE_NS");
523 numberProperty[direction].setValue(duration);
525 requestNewNumber(numberProperty);
529 else if (direction == 2 || direction == 3)
531 auto guideWE = m_Device.
getNumber(
"TELESCOPE_TIMED_GUIDE_WE");
535 numberProperty[direction - 2].setValue(duration);
537 requestNewNumber(numberProperty);
542 catch (
const std::exception &e)
547 sendResponseStatus(res, success, success ?
"" :
"Failed to pulse guide");
552 bool success =
false;
556 std::map<std::string, std::string> formData;
560 if (formData.find(
"Axis") == formData.end() || formData.find(
"Rate") == formData.end())
562 sendResponseStatus(res,
false,
"Missing Axis or Rate parameter");
567 int axis = std::stoi(formData[
"Axis"]);
568 double rate = std::stod(formData[
"Rate"]);
571 auto slewRateSP = m_Device.
getSwitch(
"TELESCOPE_SLEW_RATE");
578 int numRates = slewRateProperty.count();
581 if (numRates > 0 && std::fabs(rate) > 0)
584 const double SIDEREAL_RATE_DEG_SEC = 0.004178;
587 double rateMultiple = std::fabs(rate) / SIDEREAL_RATE_DEG_SEC;
590 int bestRateIndex = -1;
591 double bestRateDiff = std::numeric_limits<double>::max();
594 std::vector<double> rateMultiples;
595 rateMultiples.resize(numRates, 0);
596 bool hasExplicitMultiples =
false;
598 for (
int i = 0; i < numRates; i++)
600 std::string label = slewRateProperty[i].getLabel();
602 std::size_t xPos = label.find(
'x');
603 if (xPos != std::string::npos && xPos > 0)
606 std::string numStr = label.substr(0, xPos);
608 numStr.erase(std::remove_if(numStr.begin(), numStr.end(),
611 return !std::isdigit(c);
619 double multiple = std::stod(numStr);
620 rateMultiples[i] = multiple;
621 hasExplicitMultiples =
true;
624 double diff = std::fabs(multiple - rateMultiple);
625 if (diff < bestRateDiff)
632 "Found slew rate multiple in label: %s = %.0fx",
633 label.c_str(), multiple);
635 catch (
const std::exception &e)
644 if (!hasExplicitMultiples)
651 if (rateMultiple <= 2)
653 else if (rateMultiple <= 10)
654 bestRateIndex = std::min(1, numRates - 1);
655 else if (rateMultiple <= 30)
656 bestRateIndex = std::min(2, numRates - 1);
658 bestRateIndex = numRates - 1;
661 "Using default slew rate mapping for %.1fx sidereal", rateMultiple);
665 if (bestRateIndex >= 0)
667 slewRateProperty.reset();
668 slewRateProperty[bestRateIndex].setState(
ISS_ON);
669 requestNewSwitch(slewRateProperty);
672 "Setting slew rate to index %d (%s) for rate %.4f deg/sec (%.1fx sidereal)",
673 bestRateIndex, slewRateProperty[bestRateIndex].getLabel(),
674 std::fabs(rate), rateMultiple);
682 auto motionWE = m_Device.
getSwitch(
"TELESCOPE_MOTION_WE");
686 switchProperty.reset();
688 switchProperty[0].setState(
ISS_ON);
690 switchProperty[1].setState(
ISS_ON);
694 switchProperty[0].setState(
ISS_OFF);
695 switchProperty[1].setState(
ISS_OFF);
698 requestNewSwitch(switchProperty);
704 auto motionNS = m_Device.
getSwitch(
"TELESCOPE_MOTION_NS");
708 switchProperty.reset();
710 switchProperty[0].setState(
ISS_ON);
712 switchProperty[1].setState(
ISS_ON);
716 switchProperty[0].setState(
ISS_OFF);
717 switchProperty[1].setState(
ISS_OFF);
720 requestNewSwitch(switchProperty);
725 catch (
const std::exception &e)
730 sendResponseStatus(res, success, success ?
"" :
"Failed to move axis");
738 auto axisIt = req.
params.find(
"Axis");
739 if (axisIt == req.
params.end())
741 sendResponseValue(res, json::array(),
false,
"Missing Axis parameter");
746 int axis = std::stoi(axisIt->second);
749 if (axis < 0 || axis > 1)
751 sendResponseValue(res, json::array(),
false,
"Invalid Axis value. Must be 0 (Primary/RA/AZ) or 1 (Secondary/DEC/ALT)");
756 json ratesArray = json::array();
762 {
"Minimum", 0.00418},
767 sendResponseValue(res, ratesArray);
769 catch (
const std::exception &e)
772 sendResponseValue(res, json::array(),
false, std::string(
"Error processing request: ") + e.what());
778 bool success =
false;
782 std::map<std::string, std::string> formData;
786 if (formData.find(
"Tracking") == formData.end())
788 sendResponseStatus(res,
false,
"Missing Tracking parameter");
793 bool tracking = (formData[
"Tracking"] ==
"True" || formData[
"Tracking"] ==
"true" ||
794 formData[
"Tracking"] ==
"1");
796 auto trackState = m_Device.
getSwitch(
"TELESCOPE_TRACK_STATE");
800 switchProperty.reset();
801 switchProperty[tracking ? 0 : 1].setState(
ISS_ON);
803 requestNewSwitch(switchProperty);
807 catch (
const std::exception &e)
812 sendResponseStatus(res, success, success ?
"" :
"Failed to set tracking state");
817 bool success =
false;
821 std::map<std::string, std::string> formData;
825 if (formData.find(
"RightAscensionRate") == formData.end())
827 sendResponseStatus(res,
false,
"Missing RightAscensionRate parameter");
832 double raRate = std::stod(formData[
"RightAscensionRate"]);
838 for (
auto &num : numberProperty)
840 if (num.isNameMatch(
"TRACK_RATE_RA"))
841 num.setValue(raRate);
844 requestNewNumber(numberProperty);
847 m_RightAscensionRate = raRate;
851 catch (
const std::exception &e)
857 sendResponseStatus(res, success, success ?
"" :
"Failed to set right ascension rate");
862 bool success =
false;
866 std::map<std::string, std::string> formData;
870 if (formData.find(
"DeclinationRate") == formData.end())
872 sendResponseStatus(res,
false,
"Missing DeclinationRate parameter");
877 double decRate = std::stod(formData[
"DeclinationRate"]);
883 for (
auto &num : numberProperty)
885 if (num.isNameMatch(
"TRACK_RATE_DE"))
886 num.setValue(decRate);
889 requestNewNumber(numberProperty);
892 m_DeclinationRate = decRate;
896 catch (
const std::exception &e)
902 sendResponseStatus(res, success, success ?
"" :
"Failed to set declination rate");
909 std::lock_guard<std::mutex> lock(m_Mutex);
910 sendResponseValue(res, 1);
916 auto geoCoord = m_Device.
getNumber(
"GEOGRAPHIC_COORD");
917 if (!geoCoord.isValid())
919 sendResponseValue(res, 0.0,
false,
"GEOGRAPHIC_COORD property not found");
929 std::map<std::string, std::string> formData;
933 if (formData.find(
"SiteLatitude") == formData.end())
935 sendResponseValue(res, 0.0,
false,
"Missing SiteLatitude parameter");
940 double latitude = std::stod(formData[
"SiteLatitude"]);
944 for (
auto &num : numberProperty)
946 if (num.isNameMatch(
"LAT"))
947 num.setValue(latitude);
951 requestNewNumber(numberProperty);
952 sendResponseValue(res, latitude);
954 catch (
const std::exception &e)
956 sendResponseValue(res, 0.0,
false, std::string(
"Invalid request: ") + e.what());
962 double latitude = 0.0;
964 for (
auto &num : numberProperty)
966 if (num.isNameMatch(
"LAT"))
968 latitude = num.getValue();
972 sendResponseValue(res, latitude);
978 auto geoCoord = m_Device.
getNumber(
"GEOGRAPHIC_COORD");
979 if (!geoCoord.isValid())
981 sendResponseValue(res, 0.0,
false,
"GEOGRAPHIC_COORD property not found");
991 std::map<std::string, std::string> formData;
995 if (formData.find(
"SiteLongitude") == formData.end())
997 sendResponseValue(res, 0.0,
false,
"Missing SiteLongitude parameter");
1002 double longitude = std::stod(formData[
"SiteLongitude"]);
1006 for (
auto &num : numberProperty)
1008 if (num.isNameMatch(
"LONG"))
1009 num.setValue(longitude);
1013 requestNewNumber(numberProperty);
1014 sendResponseValue(res, longitude);
1016 catch (
const std::exception &e)
1018 sendResponseValue(res, 0.0,
false, std::string(
"Invalid request: ") + e.what());
1024 double longitude = 0.0;
1026 for (
auto &num : numberProperty)
1028 if (num.isNameMatch(
"LONG"))
1030 longitude = num.getValue();
1034 sendResponseValue(res, longitude);
1040 auto geoCoord = m_Device.
getNumber(
"GEOGRAPHIC_COORD");
1041 if (!geoCoord.isValid())
1043 sendResponseValue(res, 0.0,
false,
"GEOGRAPHIC_COORD property not found");
1053 std::map<std::string, std::string> formData;
1057 if (formData.find(
"SiteElevation") == formData.end())
1059 sendResponseValue(res, 0.0,
false,
"Missing SiteElevation parameter");
1064 double elevation = std::stod(formData[
"SiteElevation"]);
1068 for (
auto &num : numberProperty)
1070 if (num.isNameMatch(
"ELEV"))
1071 num.setValue(elevation);
1075 requestNewNumber(numberProperty);
1076 sendResponseValue(res, elevation);
1078 catch (
const std::exception &e)
1080 sendResponseValue(res, 0.0,
false, std::string(
"Invalid request: ") + e.what());
1086 double elevation = 0.0;
1088 for (
auto &num : numberProperty)
1090 if (num.isNameMatch(
"ELEV"))
1092 elevation = num.getValue();
1096 sendResponseValue(res, elevation);
static void parseFormUrlEncodedBody(const std::string &body, std::map< std::string, std::string > ¶ms)
INDI::PropertyNumber getNumber(const char *name) const
const char * getDeviceName() const
Property getProperty(const char *name, INDI_PROPERTY_TYPE type=INDI_UNKNOWN) const
Return a property and its type given its name.
INDI::PropertySwitch getSwitch(const char *name) const
void setState(IPState state)
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
void handleDescription(const httplib::Request &req, httplib::Response &res) override
void handleDriverInfo(const httplib::Request &req, httplib::Response &res) override
a class to store JSON values
void push_back(basic_json &&val)
add an object to an array
#define DEBUGFDEVICE(device, priority, msg,...)
basic_json<> json
default specialization