34#ifdef _USE_SYSTEM_JSONLIB
35#include <nlohmann/json.hpp>
45 std::lock_guard<std::mutex> lock(m_Mutex);
51 sendResponseValue(res, req, connected);
53 else if (req.
method ==
"PUT")
56 sendResponseStatus(res, req,
true,
"");
63 std::lock_guard<std::mutex> lock(m_Mutex);
64 sendResponseValue(res, req, std::string(m_Device.
getDeviceName()));
70 std::lock_guard<std::mutex> lock(m_Mutex);
71 std::string description =
"INDI Camera Bridge for " + std::string(m_Device.
getDeviceName());
72 sendResponseValue(res, req, description);
78 std::lock_guard<std::mutex> lock(m_Mutex);
79 std::string driverInfo =
"INDI Alpaca Camera Bridge v1.0";
80 sendResponseValue(res, req, driverInfo);
86 std::lock_guard<std::mutex> lock(m_Mutex);
87 std::string version =
"1.0.0";
88 sendResponseValue(res, req, version);
94 std::lock_guard<std::mutex> lock(m_Mutex);
95 int interfaceVersion = 3;
96 sendResponseValue(res, req, interfaceVersion);
103 std::lock_guard<std::mutex> lock(m_Mutex);
104 sendResponseValue(res, req, m_CameraXSize);
110 std::lock_guard<std::mutex> lock(m_Mutex);
111 sendResponseValue(res, req, m_CameraYSize);
117 std::lock_guard<std::mutex> lock(m_Mutex);
118 sendResponseValue(res, req, m_MaxBinX);
124 std::lock_guard<std::mutex> lock(m_Mutex);
125 sendResponseValue(res, req, m_MaxBinY);
131 std::lock_guard<std::mutex> lock(m_Mutex);
132 sendResponseValue(res, req, m_CanAsymmetricBin);
138 std::lock_guard<std::mutex> lock(m_Mutex);
139 sendResponseValue(res, req, m_PixelSizeX);
145 std::lock_guard<std::mutex> lock(m_Mutex);
146 sendResponseValue(res, req, m_PixelSizeY);
151 std::lock_guard<std::mutex> lock(m_Mutex);
155 sendResponseValue(res, req, m_BinX);
157 else if (req.
method ==
"PUT")
160 httplib::detail::parse_query_text(req.
body, params);
162 if (params.count(
"BinX"))
164 int newBinX = std::stoi(params.find(
"BinX")->second);
169 auto horBinElement = numberProperty.findWidgetByName(
"HOR_BIN");
172 horBinElement->setValue(newBinX);
173 requestNewNumber(numberProperty);
174 sendResponseStatus(res, req,
true,
"");
178 sendResponseStatus(res, req,
false,
"Failed to set BinX: CCD_BINNING property not found or invalid.");
182 sendResponseStatus(res, req,
false,
"Missing 'BinX' parameter in request body");
189 std::lock_guard<std::mutex> lock(m_Mutex);
193 sendResponseValue(res, req, m_BinY);
195 else if (req.
method ==
"PUT")
198 httplib::detail::parse_query_text(req.
body, params);
200 if (params.count(
"BinY"))
202 int newBinY = std::stoi(params.find(
"BinY")->second);
207 auto verBinElement = numberProperty.findWidgetByName(
"VER_BIN");
210 verBinElement->setValue(newBinY);
211 requestNewNumber(numberProperty);
212 sendResponseStatus(res, req,
true,
"");
216 sendResponseStatus(res, req,
false,
"Failed to set BinY: CCD_BINNING property not found or invalid.");
220 sendResponseStatus(res, req,
false,
"Missing 'BinY' parameter in request body");
227 std::lock_guard<std::mutex> lock(m_Mutex);
231 sendResponseValue(res, req, m_StartX);
233 else if (req.
method ==
"PUT")
236 httplib::detail::parse_query_text(req.
body, params);
238 if (params.count(
"StartX"))
240 int newStartX = std::stoi(params.find(
"StartX")->second);
245 auto xElement = numberProperty.findWidgetByName(
"X");
248 xElement->setValue(newStartX);
249 requestNewNumber(numberProperty);
250 sendResponseStatus(res, req,
true,
"");
254 sendResponseStatus(res, req,
false,
"Failed to set StartX: CCD_FRAME property not found or invalid.");
258 sendResponseStatus(res, req,
false,
"Missing 'StartX' parameter in request body");
265 std::lock_guard<std::mutex> lock(m_Mutex);
269 sendResponseValue(res, req, m_StartY);
271 else if (req.
method ==
"PUT")
274 httplib::detail::parse_query_text(req.
body, params);
276 if (params.count(
"StartY"))
278 int newStartY = std::stoi(params.find(
"StartY")->second);
283 auto yElement = numberProperty.findWidgetByName(
"Y");
286 yElement->setValue(newStartY);
287 requestNewNumber(numberProperty);
288 sendResponseStatus(res, req,
true,
"");
292 sendResponseStatus(res, req,
false,
"Failed to set StartY: CCD_FRAME property not found or invalid.");
296 sendResponseStatus(res, req,
false,
"Missing 'StartY' parameter in request body");
303 std::lock_guard<std::mutex> lock(m_Mutex);
307 sendResponseValue(res, req, m_NumX);
309 else if (req.
method ==
"PUT")
312 httplib::detail::parse_query_text(req.
body, params);
314 if (params.count(
"NumX"))
316 int newNumX = std::stoi(params.find(
"NumX")->second);
321 auto widthElement = numberProperty.findWidgetByName(
"WIDTH");
324 widthElement->setValue(newNumX);
325 requestNewNumber(numberProperty);
326 sendResponseStatus(res, req,
true,
"");
330 sendResponseStatus(res, req,
false,
"Failed to set NumX: CCD_FRAME property not found or invalid.");
334 sendResponseStatus(res, req,
false,
"Missing 'NumX' parameter in request body");
341 std::lock_guard<std::mutex> lock(m_Mutex);
345 sendResponseValue(res, req, m_NumY);
347 else if (req.
method ==
"PUT")
350 httplib::detail::parse_query_text(req.
body, params);
352 if (params.count(
"NumY"))
354 int newNumY = std::stoi(params.find(
"NumY")->second);
359 auto heightElement = numberProperty.findWidgetByName(
"HEIGHT");
362 heightElement->setValue(newNumY);
363 requestNewNumber(numberProperty);
364 sendResponseStatus(res, req,
true,
"");
368 sendResponseStatus(res, req,
false,
"Failed to set NumY: CCD_FRAME property not found or invalid.");
372 sendResponseStatus(res, req,
false,
"Missing 'NumY' parameter in request body");
381 std::lock_guard<std::mutex> lock(m_Mutex);
382 sendResponseValue(res, req, m_CanAbortExposure);
388 std::lock_guard<std::mutex> lock(m_Mutex);
389 sendResponseValue(res, req, m_CanStopExposure);
395 std::lock_guard<std::mutex> lock(m_Mutex);
396 sendResponseValue(res, req, m_CanPulseGuide);
402 std::lock_guard<std::mutex> lock(m_Mutex);
403 sendResponseValue(res, req, m_CanSetCCDTemperature);
409 std::lock_guard<std::mutex> lock(m_Mutex);
411 sendResponseValue(res, req,
false);
417 std::lock_guard<std::mutex> lock(m_Mutex);
418 sendResponseValue(res, req, m_HasShutter);
425 std::lock_guard<std::mutex> lock(m_Mutex);
426 sendResponseValue(res, req, m_CCDTemperature);
431 std::lock_guard<std::mutex> lock(m_Mutex);
435 sendResponseValue(res, req, m_CoolerOn);
437 else if (req.
method ==
"PUT")
440 httplib::detail::parse_query_text(req.
body, params);
442 if (params.count(
"CoolerOn"))
444 bool newCoolerOn = (params.find(
"CoolerOn")->second ==
"true");
451 switchProperty.findWidgetByName(
"COOLER_ON")->setState(
ISS_ON);
452 switchProperty.findWidgetByName(
"COOLER_OFF")->setState(
ISS_OFF);
456 switchProperty.findWidgetByName(
"COOLER_ON")->setState(
ISS_OFF);
457 switchProperty.findWidgetByName(
"COOLER_OFF")->setState(
ISS_ON);
459 requestNewSwitch(switchProperty);
460 sendResponseStatus(res, req,
true,
"");
463 sendResponseStatus(res, req,
false,
"Failed to set CoolerOn: CCD_COOLER property not found or invalid.");
467 sendResponseStatus(res, req,
false,
"Missing 'CoolerOn' parameter in request body");
475 std::lock_guard<std::mutex> lock(m_Mutex);
476 sendResponseValue(res, req, m_CoolerPower);
482 std::lock_guard<std::mutex> lock(m_Mutex);
485 bool canGet = coolerPowerProperty.
isValid();
486 sendResponseValue(res, req, canGet);
491 std::lock_guard<std::mutex> lock(m_Mutex);
496 sendResponseValue(res, req, m_TargetCCDTemperature);
498 else if (req.
method ==
"PUT")
501 httplib::detail::parse_query_text(req.
body, params);
503 if (params.count(
"SetCCDTemperature"))
505 double newTemperature = std::stod(params.find(
"SetCCDTemperature")->second);
506 m_TargetCCDTemperature = newTemperature;
512 numberProperty[0].setValue(newTemperature);
513 requestNewNumber(numberProperty);
514 sendResponseStatus(res, req,
true,
"");
517 sendResponseStatus(res, req,
false,
"Failed to set CCDTemperature: CCD_TEMPERATURE property not found or invalid.");
521 sendResponseStatus(res, req,
false,
"Missing 'SetCCDTemperature' parameter in request body");
526 sendResponseStatus(res, req,
false,
"Method not supported");
533 std::lock_guard<std::mutex> lock(m_Mutex);
537 sendResponseStatus(res, req,
false,
"Gain not supported");
543 sendResponseValue(res, req,
static_cast<int>(m_Gain));
545 else if (req.
method ==
"PUT")
548 httplib::detail::parse_query_text(req.
body, params);
550 if (params.count(
"Gain"))
552 double newGain = std::stod(params.find(
"Gain")->second);
554 if (m_UsesCCDControlsForGainOffset)
560 auto gainElement = numberProperty.findWidgetByName(
"Gain");
563 gainElement->setValue(newGain);
564 requestNewNumber(numberProperty);
565 sendResponseStatus(res, req,
true,
"");
569 sendResponseStatus(res, req,
false,
"Failed to set Gain via CCD_CONTROLS");
577 numberProperty[0].setValue(newGain);
578 requestNewNumber(numberProperty);
579 sendResponseStatus(res, req,
true,
"");
582 sendResponseStatus(res, req,
false,
"Failed to set Gain via CCD_GAIN");
587 sendResponseStatus(res, req,
false,
"Missing 'Gain' parameter in request body");
595 std::lock_guard<std::mutex> lock(m_Mutex);
598 sendResponseValue(res, req,
static_cast<int>(m_GainMin));
600 sendResponseStatus(res, req,
false,
"Gain not supported");
606 std::lock_guard<std::mutex> lock(m_Mutex);
609 sendResponseValue(res, req,
static_cast<int>(m_GainMax));
611 sendResponseStatus(res, req,
false,
"Gain not supported");
617 std::lock_guard<std::mutex> lock(m_Mutex);
621 json gainsArray = json::array();
622 for (
const auto& gain : m_Gains)
623 gainsArray.push_back(gain);
624 sendResponseValue(res, req, gainsArray);
628 sendResponseStatus(res, req,
false,
"Gain not supported");
634 std::lock_guard<std::mutex> lock(m_Mutex);
638 sendResponseStatus(res, req,
false,
"Offset not supported");
644 sendResponseValue(res, req,
static_cast<int>(m_Offset));
646 else if (req.
method ==
"PUT")
649 httplib::detail::parse_query_text(req.
body, params);
651 if (params.count(
"Offset"))
653 double newOffset = std::stod(params.find(
"Offset")->second);
655 if (m_UsesCCDControlsForGainOffset)
661 auto offsetElement = numberProperty.findWidgetByName(
"Offset");
664 offsetElement->setValue(newOffset);
665 requestNewNumber(numberProperty);
666 sendResponseStatus(res, req,
true,
"");
670 sendResponseStatus(res, req,
false,
"Failed to set Offset via CCD_CONTROLS");
678 numberProperty[0].setValue(newOffset);
679 requestNewNumber(numberProperty);
680 sendResponseStatus(res, req,
true,
"");
683 sendResponseStatus(res, req,
false,
"Failed to set Offset via CCD_OFFSET");
688 sendResponseStatus(res, req,
false,
"Missing 'Offset' parameter in request body");
696 std::lock_guard<std::mutex> lock(m_Mutex);
699 sendResponseValue(res, req,
static_cast<int>(m_OffsetMin));
701 sendResponseStatus(res, req,
false,
"Offset not supported");
707 std::lock_guard<std::mutex> lock(m_Mutex);
710 sendResponseValue(res, req,
static_cast<int>(m_OffsetMax));
712 sendResponseStatus(res, req,
false,
"Offset not supported");
718 std::lock_guard<std::mutex> lock(m_Mutex);
722 json offsetsArray = json::array();
723 for (
const auto& offset : m_Offsets)
724 offsetsArray.push_back(offset);
725 sendResponseValue(res, req, offsetsArray);
729 sendResponseStatus(res, req,
false,
"Offset not supported");
736 std::lock_guard<std::mutex> lock(m_Mutex);
740 sendResponseValue(res, req, m_ReadoutMode);
742 else if (req.
method ==
"PUT")
745 httplib::detail::parse_query_text(req.
body, params);
747 if (params.count(
"ReadoutMode"))
749 int newReadoutModeIndex = std::stoi(params.find(
"ReadoutMode")->second);
751 if (newReadoutModeIndex >= 0 &&
static_cast<size_t>(newReadoutModeIndex) < m_ReadoutModes.size())
753 std::string newReadoutModeName = m_ReadoutModes[newReadoutModeIndex];
758 for (
auto &sw : switchProperty)
760 if (sw.isNameMatch(newReadoutModeName.c_str()))
765 requestNewSwitch(switchProperty);
766 sendResponseStatus(res, req,
true,
"");
769 sendResponseStatus(res, req,
false,
"Failed to set ReadoutMode: CCD_READOUT_MODE property not found or invalid.");
773 sendResponseStatus(res, req,
false,
"Invalid ReadoutMode index.");
778 sendResponseStatus(res, req,
false,
"Missing 'ReadoutMode' parameter in request body");
786 std::lock_guard<std::mutex> lock(m_Mutex);
788 json modesArray = json::array();
789 for (
const auto& mode : m_ReadoutModes)
790 modesArray.push_back(mode);
791 sendResponseValue(res, req, modesArray);
797 std::lock_guard<std::mutex> lock(m_Mutex);
798 sendResponseValue(res, req, m_SensorType);
804 std::lock_guard<std::mutex> lock(m_Mutex);
805 sendResponseValue(res, req, m_BayerOffsetX);
811 std::lock_guard<std::mutex> lock(m_Mutex);
812 sendResponseValue(res, req, m_BayerOffsetY);
818 std::lock_guard<std::mutex> lock(m_Mutex);
819 sendResponseValue(res, req, std::string());
828 httplib::detail::parse_query_text(req.
body, params);
831 double exposureDuration = 0.0;
832 if (params.count(
"Duration"))
834 exposureDuration = std::stod(params.find(
"Duration")->second);
836 else if (params.count(
"ExposureDuration"))
838 exposureDuration = std::stod(params.find(
"ExposureDuration")->second);
842 sendResponseStatus(res, req,
false,
"Missing 'Duration' parameter in request body");
847 bool isLightFrame =
true;
848 if (params.count(
"Light"))
850 std::string lightParam = params.find(
"Light")->second;
851 isLightFrame = (lightParam ==
"true" || lightParam ==
"True" || lightParam ==
"1");
859 for (
auto &sw : switchProperty)
861 if (isLightFrame && sw.isNameMatch(
"FRAME_LIGHT"))
863 else if (!isLightFrame && sw.isNameMatch(
"FRAME_DARK"))
868 requestNewSwitch(switchProperty);
876 numberProperty[0].setValue(exposureDuration);
877 requestNewNumber(numberProperty);
878 sendResponseStatus(res, req,
true,
"");
881 sendResponseStatus(res, req,
false,
"Failed to start exposure: CCD_EXPOSURE property not found or invalid.");
885 sendResponseStatus(res, req,
false,
"Method not supported");
897 auto abortElement = switchProperty.findWidgetByName(
"ABORT");
900 abortElement->setState(
ISS_ON);
901 requestNewSwitch(switchProperty);
902 sendResponseStatus(res, req,
true,
"");
906 sendResponseStatus(res, req,
false,
"Failed to stop exposure: CCD_ABORT_EXPOSURE property not found or invalid.");
910 sendResponseStatus(res, req,
false,
"Method not supported");
922 auto abortElement = switchProperty.findWidgetByName(
"ABORT");
925 abortElement->setState(
ISS_ON);
926 requestNewSwitch(switchProperty);
927 sendResponseStatus(res, req,
true,
"");
931 sendResponseStatus(res, req,
false,
"Failed to abort exposure: CCD_ABORT_EXPOSURE property not found or invalid.");
935 sendResponseStatus(res, req,
false,
"Method not supported");
942 std::lock_guard<std::mutex> lock(m_Mutex);
943 sendResponseValue(res, req, m_ImageReady);
949 std::lock_guard<std::mutex> lock(m_Mutex);
950 sendResponseValue(res, req, m_CameraState);
956 std::lock_guard<std::mutex> lock(m_Mutex);
959 if (m_IsExposing && m_LastExposureDuration > 0)
961 auto now = std::chrono::steady_clock::now();
962 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_ExposureStartTime).count() / 1000.0;
963 double percentage = std::min(100.0, (elapsed / m_LastExposureDuration) * 100.0);
964 sendResponseValue(res, req, percentage);
968 sendResponseValue(res, req, m_PercentCompleted);
975 std::lock_guard<std::mutex> lock(m_Mutex);
976 sendResponseValue(res, req, m_LastExposureDuration);
982 std::lock_guard<std::mutex> lock(m_Mutex);
983 sendResponseValue(res, req, m_LastExposureStartTime);
989 std::lock_guard<std::mutex> lock(m_Mutex);
990 sendResponseValue(res, req, m_ExposureMin);
996 std::lock_guard<std::mutex> lock(m_Mutex);
997 sendResponseValue(res, req, m_ExposureMax);
1004 std::lock_guard<std::mutex> lock(m_Mutex);
1006 if (!m_ImageReady || m_LastImageData.empty())
1008 sendResponseStatus(res, req,
false,
"No image available");
1014 formatImageAsJSON(m_LastImageData, m_LastImageWidth, m_LastImageHeight,
1015 m_LastImageBPP, m_LastImageNAxis, imageArray);
1018 int elementType = 2;
1021 uint32_t clientID, serverID;
1022 extractTransactionIDs(req, clientID, serverID);
1026 {
"Value", imageArray},
1027 {
"Type", elementType},
1028 {
"Rank", m_LastImageNAxis},
1029 {
"ClientTransactionID", clientID},
1030 {
"ServerTransactionID", serverID},
1032 {
"ErrorMessage",
""}
1041 std::lock_guard<std::mutex> lock(m_Mutex);
1043 if (!m_ImageReady || m_LastImageData.empty())
1045 sendResponseStatus(res, req,
false,
"No image available");
1051 formatImageAsJSON(m_LastImageData, m_LastImageWidth, m_LastImageHeight,
1052 m_LastImageBPP, m_LastImageNAxis, imageArray);
1055 int elementType = 2;
1058 uint32_t clientID, serverID;
1059 extractTransactionIDs(req, clientID, serverID);
1063 {
"Value", imageArray},
1064 {
"Type", elementType},
1065 {
"Rank", m_LastImageNAxis},
1066 {
"ClientTransactionID", clientID},
1067 {
"ServerTransactionID", serverID},
1069 {
"ErrorMessage",
""}
1079 std::lock_guard<std::mutex> lock(m_Mutex);
1080 sendResponseValue(res, req, m_IsPulseGuiding);
1088 httplib::detail::parse_query_text(req.
body, params);
1090 if (params.count(
"Direction") && params.count(
"Duration"))
1092 int direction = std::stoi(params.find(
"Direction")->second);
1093 double duration = std::stod(params.find(
"Duration")->second);
1100 sendResponseStatus(res, req,
false,
1101 "Pulse guiding properties (TELESCOPE_TIMED_GUIDE_NS, TELESCOPE_TIMED_GUIDE_WE) not found.");
1110 auto northElement = numberProperty.findWidgetByName(
"TIMED_GUIDE_N");
1113 northElement->setValue(duration);
1114 requestNewNumber(numberProperty);
1115 sendResponseStatus(res, req,
true,
"");
1119 sendResponseStatus(res, req,
false,
"Failed to pulse guide North.");
1121 else if (direction == 1)
1126 auto southElement = numberProperty.findWidgetByName(
"TIMED_GUIDE_S");
1129 southElement->setValue(duration);
1130 requestNewNumber(numberProperty);
1131 sendResponseStatus(res, req,
true,
"");
1135 sendResponseStatus(res, req,
false,
"Failed to pulse guide South.");
1137 else if (direction == 2)
1142 auto eastElement = numberProperty.findWidgetByName(
"TIMED_GUIDE_E");
1145 eastElement->setValue(duration);
1146 requestNewNumber(numberProperty);
1147 sendResponseStatus(res, req,
true,
"");
1151 sendResponseStatus(res, req,
false,
"Failed to pulse guide East.");
1153 else if (direction == 3)
1158 auto westElement = numberProperty.findWidgetByName(
"TIMED_GUIDE_W");
1161 westElement->setValue(duration);
1162 requestNewNumber(numberProperty);
1163 sendResponseStatus(res, req,
true,
"");
1167 sendResponseStatus(res, req,
false,
"Failed to pulse guide West.");
1171 sendResponseStatus(res, req,
false,
"Invalid 'Direction' parameter.");
1176 sendResponseStatus(res, req,
false,
"Missing 'Direction' or 'Duration' parameter in request body");
1181 sendResponseStatus(res, req,
false,
"Method not supported");
1189 std::lock_guard<std::mutex> lock(m_Mutex);
1190 sendResponseValue(res, req, m_MaxADU);
1196 std::lock_guard<std::mutex> lock(m_Mutex);
1197 sendResponseValue(res, req, m_ElectronsPerADU);
1203 std::lock_guard<std::mutex> lock(m_Mutex);
1204 sendResponseValue(res, req, m_FullWellCapacity);
void handleName(const httplib::Request &req, httplib::Response &res) override
void handleDriverInfo(const httplib::Request &req, httplib::Response &res) override
void handleDriverVersion(const httplib::Request &req, httplib::Response &res) override
void handleConnected(const httplib::Request &req, httplib::Response &res) override
void handleDescription(const httplib::Request &req, httplib::Response &res) override
void handleInterfaceVersion(const httplib::Request &req, httplib::Response &res) override
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.
Provides generic container for INDI properties.
INDI_PROPERTY_TYPE getType() const
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
std::multimap< std::string, std::string > Params
basic_json<> json
default specialization
void set_content(const char *s, size_t n, const std::string &content_type)