35#ifdef _USE_SYSTEM_JSONLIB
36#include <nlohmann/json.hpp>
44 : m_Device(
device), m_DeviceNumber(deviceNumber)
47 device.getDeviceName(), deviceNumber);
52 m_CanPulseGuide =
true;
80 if (method ==
"connected")
82 else if (method ==
"name")
84 else if (method ==
"description")
86 else if (method ==
"driverinfo")
88 else if (method ==
"driverversion")
90 else if (method ==
"interfaceversion")
93 else if (method ==
"cameraxsize")
94 handleCameraXSize(req, res);
95 else if (method ==
"cameraysize")
96 handleCameraYSize(req, res);
97 else if (method ==
"maxbinx")
98 handleMaxBinX(req, res);
99 else if (method ==
"maxbiny")
100 handleMaxBinY(req, res);
101 else if (method ==
"canasymmetricbin")
102 handleCanAsymmetricBin(req, res);
103 else if (method ==
"pixelsizex")
104 handlePixelSizeX(req, res);
105 else if (method ==
"pixelsizey")
106 handlePixelSizeY(req, res);
107 else if (method ==
"binx")
108 handleBinX(req, res);
109 else if (method ==
"biny")
110 handleBinY(req, res);
111 else if (method ==
"startx")
112 handleStartX(req, res);
113 else if (method ==
"starty")
114 handleStartY(req, res);
115 else if (method ==
"numx")
116 handleNumX(req, res);
117 else if (method ==
"numy")
118 handleNumY(req, res);
120 else if (method ==
"canabortexposure")
121 handleCanAbortExposure(req, res);
122 else if (method ==
"canstopexposure")
123 handleCanStopExposure(req, res);
124 else if (method ==
"canpulseguide")
125 handleCanPulseGuide(req, res);
126 else if (method ==
"cansetccdtemperature")
127 handleCanSetCCDTemperature(req, res);
128 else if (method ==
"canfastreadout")
129 handleCanFastReadout(req, res);
130 else if (method ==
"hasshutter")
131 handleHasShutter(req, res);
133 else if (method ==
"ccdtemperature")
134 handleCCDTemperature(req, res);
135 else if (method ==
"cooleron")
136 handleCoolerOn(req, res);
137 else if (method ==
"coolerpower")
138 handleCoolerPower(req, res);
139 else if (method ==
"cangetcoolerpower")
140 handleCanGetCoolerPower(req, res);
141 else if (method ==
"setccdtemperature")
142 handleSetCCDTemperature(req, res);
144 else if (method ==
"gain")
145 handleGain(req, res);
146 else if (method ==
"gainmin")
147 handleGainMin(req, res);
148 else if (method ==
"gainmax")
149 handleGainMax(req, res);
150 else if (method ==
"gains")
151 handleGains(req, res);
152 else if (method ==
"offset")
153 handleOffset(req, res);
154 else if (method ==
"offsetmin")
155 handleOffsetMin(req, res);
156 else if (method ==
"offsetmax")
157 handleOffsetMax(req, res);
158 else if (method ==
"offsets")
159 handleOffsets(req, res);
161 else if (method ==
"readoutmode")
162 handleReadoutMode(req, res);
163 else if (method ==
"readoutmodes")
164 handleReadoutModes(req, res);
165 else if (method ==
"sensortype")
166 handleSensorType(req, res);
167 else if (method ==
"bayeroffsetx")
168 handleBayerOffsetX(req, res);
169 else if (method ==
"bayeroffsety")
170 handleBayerOffsetY(req, res);
171 else if (method ==
"sensorname")
172 handleSensorName(req, res);
174 else if (method ==
"startexposure")
175 handleStartExposure(req, res);
176 else if (method ==
"stopexposure")
177 handleStopExposure(req, res);
178 else if (method ==
"abortexposure")
179 handleAbortExposure(req, res);
180 else if (method ==
"imageready")
181 handleImageReady(req, res);
182 else if (method ==
"camerastate")
183 handleCameraState(req, res);
184 else if (method ==
"percentcompleted")
185 handlePercentCompleted(req, res);
186 else if (method ==
"lastexposureduration")
187 handleLastExposureDuration(req, res);
188 else if (method ==
"lastexposurestarttime")
189 handleLastExposureStartTime(req, res);
190 else if (method ==
"exposuremin")
191 handleExposureMin(req, res);
192 else if (method ==
"exposuremax")
193 handleExposureMax(req, res);
195 else if (method ==
"imagearray")
196 handleImageArray(req, res);
197 else if (method ==
"imagearrayvariant")
198 handleImageArrayVariant(req, res);
200 else if (method ==
"ispulseguiding")
201 handleIsPulseGuiding(req, res);
202 else if (method ==
"pulseguide")
203 handlePulseGuide(req, res);
205 else if (method ==
"maxadu")
206 handleMaxADU(req, res);
207 else if (method ==
"electronsperadu")
208 handleElectronsPerADU(req, res);
209 else if (method ==
"fullwellcapacity")
210 handleFullWellCapacity(req, res);
216 {
"ErrorNumber", 1025},
217 {
"ErrorMessage",
"Method not implemented: " + method}
226 std::lock_guard<std::mutex> lock(m_Mutex);
228 const char *propName =
property.getName();
236 for (
auto &num : numberProperty)
238 if (num.isNameMatch(
"CCD_MAX_X"))
239 m_CameraXSize =
static_cast<int>(num.getValue());
240 else if (num.isNameMatch(
"CCD_MAX_Y"))
241 m_CameraYSize =
static_cast<int>(num.getValue());
242 else if (num.isNameMatch(
"CCD_PIXEL_SIZE_X"))
243 m_PixelSizeX = num.getValue();
244 else if (num.isNameMatch(
"CCD_PIXEL_SIZE_Y"))
245 m_PixelSizeY = num.getValue();
246 else if (num.isNameMatch(
"CCD_BITSPERPIXEL"))
247 m_BitsPerPixel =
static_cast<int>(num.getValue());
250 "Updated camera info: %dx%d, pixel size: %.2fx%.2f, BPP: %d",
251 m_CameraXSize, m_CameraYSize, m_PixelSizeX, m_PixelSizeY, m_BitsPerPixel);
257 for (
auto &num : numberProperty)
259 if (num.isNameMatch(
"HOR_BIN"))
260 m_BinX =
static_cast<int>(num.getValue());
261 else if (num.isNameMatch(
"VER_BIN"))
262 m_BinY =
static_cast<int>(num.getValue());
270 for (
auto &num : numberProperty)
272 if (num.isNameMatch(
"X"))
273 m_StartX =
static_cast<int>(num.getValue());
274 else if (num.isNameMatch(
"Y"))
275 m_StartY =
static_cast<int>(num.getValue());
276 else if (num.isNameMatch(
"WIDTH"))
277 m_NumX =
static_cast<int>(num.getValue());
278 else if (num.isNameMatch(
"HEIGHT"))
279 m_NumY =
static_cast<int>(num.getValue());
282 "Updated frame: (%d,%d) %dx%d", m_StartX, m_StartY, m_NumX, m_NumY);
288 for (
auto &num : numberProperty)
290 if (num.isNameMatch(
"Gain"))
292 m_Gain = num.getValue();
293 m_GainMin = num.getMin();
294 m_GainMax = num.getMax();
296 m_UsesCCDControlsForGainOffset =
true;
298 "Updated gain from CCD_CONTROLS: %.0f (Min: %.0f, Max: %.0f)",
299 m_Gain, m_GainMin, m_GainMax);
301 else if (num.isNameMatch(
"Offset"))
303 m_Offset = num.getValue();
304 m_OffsetMin = num.getMin();
305 m_OffsetMax = num.getMax();
307 m_UsesCCDControlsForGainOffset =
true;
309 "Updated offset from CCD_CONTROLS: %.0f (Min: %.0f, Max: %.0f)",
310 m_Offset, m_OffsetMin, m_OffsetMax);
318 m_CCDTemperature = numberProperty[0].getValue();
319 m_CanSetCCDTemperature = (
property.getPermission() !=
IP_RO);
321 m_CCDTemperature, m_CanSetCCDTemperature ?
"true" :
"false");
334 m_CoolerPower = numberProperty[0].getValue();
341 m_Gain = numberProperty[0].getValue();
342 m_GainMin = numberProperty[0].getMin();
343 m_GainMax = numberProperty[0].getMax();
346 m_Gain, m_GainMin, m_GainMax);
352 m_Offset = numberProperty[0].getValue();
353 m_OffsetMin = numberProperty[0].getMin();
354 m_OffsetMax = numberProperty[0].getMax();
357 m_Offset, m_OffsetMin, m_OffsetMax);
369 m_LastExposureDuration = numberProperty[0].getValue();
370 m_ExposureStartTime = std::chrono::steady_clock::now();
371 m_ImageReady =
false;
375 m_IsExposing =
false;
382 m_IsExposing =
false;
386 m_ExposureMin = numberProperty[0].getMin();
387 m_ExposureMax = numberProperty[0].getMax();
390 "Updated exposure state: %s, duration: %.3f, min: %.3f, max: %.3f",
391 m_IsExposing ?
"EXPOSING" :
"IDLE", m_LastExposureDuration, m_ExposureMin, m_ExposureMax);
397 if (blobProperty[0].getBlobLen() > 0)
400 const uint8_t* fitsData =
static_cast<const uint8_t*
>(blobProperty[0].getBlob());
401 size_t fitsSize = blobProperty[0].getBlobLen();
403 if (extractImageFromFITS(fitsData, fitsSize, m_LastImageData,
404 m_LastImageWidth, m_LastImageHeight,
405 m_LastImageBPP, m_LastImageNAxis))
409 m_PercentCompleted = 100.0;
411 "Image ready: %dx%d, %d-bit, %d-axis",
412 m_LastImageWidth, m_LastImageHeight, m_LastImageBPP, m_LastImageNAxis);
425 for (
auto &text : textProperty)
427 if (text.isNameMatch(
"CFA_OFFSET_X"))
428 m_BayerOffsetX = atoi(text.getText());
429 else if (text.isNameMatch(
"CFA_OFFSET_Y"))
430 m_BayerOffsetY = atoi(text.getText());
431 else if (text.isNameMatch(
"CFA_TYPE"))
433 std::string cfaType = text.getText();
434 if (cfaType ==
"RGGB")
436 else if (cfaType ==
"BGGR")
438 else if (cfaType ==
"GRBG")
440 else if (cfaType ==
"GBRG")
442 else if (cfaType ==
"CMYG")
449 "Updated Bayer info: type=%d, offset=(%d,%d)",
450 m_SensorType, m_BayerOffsetX, m_BayerOffsetY);
474void CameraBridge::extractTransactionIDs(
const httplib::Request &req, uint32_t &clientID, uint32_t &serverID)
480 if (req.
has_param(
"ClientTransactionID"))
484 clientID =
static_cast<uint32_t
>(std::stoul(req.
get_param_value(
"ClientTransactionID")));
492 else if (!req.
body.empty())
495 httplib::detail::parse_query_text(req.
body, params);
497 if (params.count(
"ClientTransactionID"))
501 clientID =
static_cast<uint32_t
>(std::stoul(params.find(
"ClientTransactionID")->second));
516void CameraBridge::sendResponse(
httplib::Response &res,
bool success,
const std::string &errorMessage)
524 {
"ClientTransactionID", clientID},
525 {
"ServerTransactionID", serverID},
526 {
"ErrorNumber", success ? 0 : 1},
527 {
"ErrorMessage", success ?
"" : errorMessage}
534void CameraBridge::sendResponse(
httplib::Response &res,
const T &value,
bool success,
535 const std::string &errorMessage,
int clientID,
int serverID)
540 {
"ClientTransactionID", clientID},
541 {
"ServerTransactionID", serverID},
542 {
"ErrorNumber", success ? 0 : 1},
543 {
"ErrorMessage", success ?
"" : errorMessage}
551 bool success,
const std::string &errorMessage)
553 uint32_t clientID, serverID;
554 extractTransactionIDs(req, clientID, serverID);
555 sendResponse(res, value, success, errorMessage, clientID, serverID);
560 const std::string &errorMessage)
562 uint32_t clientID, serverID;
563 extractTransactionIDs(req, clientID, serverID);
567 {
"ClientTransactionID", clientID},
568 {
"ServerTransactionID", serverID},
569 {
"ErrorNumber", success ? 0 : 1},
570 {
"ErrorMessage", success ?
"" : errorMessage}
576template void CameraBridge::sendResponse<std::string>(
httplib::Response &res,
const std::string &value,
bool success,
577 const std::string &errorMessage,
int clientID,
int serverID);
578template void CameraBridge::sendResponse<double>(
httplib::Response &res,
const double &value,
bool success,
579 const std::string &errorMessage,
int clientID,
int serverID);
580template void CameraBridge::sendResponse<int>(
httplib::Response &res,
const int &value,
bool success,
581 const std::string &errorMessage,
int clientID,
int serverID);
582template void CameraBridge::sendResponse<bool>(
httplib::Response &res,
const bool &value,
bool success,
583 const std::string &errorMessage,
int clientID,
int serverID);
584template void CameraBridge::sendResponse<json>(
httplib::Response &res,
const json &value,
bool success,
585 const std::string &errorMessage,
int clientID,
int serverID);
589 const std::string &value,
bool success,
const std::string &errorMessage);
591 const double &value,
bool success,
const std::string &errorMessage);
593 const int &value,
bool success,
const std::string &errorMessage);
595 const bool &value,
bool success,
const std::string &errorMessage);
597 const json &value,
bool success,
const std::string &errorMessage);
600bool CameraBridge::extractImageFromFITS(
const uint8_t* fitsData,
size_t fitsSize,
601 std::vector<uint8_t> &rawData,
int &width,
int &height,
602 int &bitsPerPixel,
int &naxis)
604 if (!fitsData || fitsSize == 0)
607 fitsfile *fptr =
nullptr;
609 long naxes[3] = {0, 0, 0};
613 void *memptr = (
void *)fitsData;
614 if (fits_open_memfile(&fptr,
"", READONLY, &memptr, &fitsSize, 0,
nullptr, &status))
621 if (fits_get_img_param(fptr, 3, &bitpix, &naxis, naxes, &status))
624 fits_close_file(fptr, &status);
628 width =
static_cast<int>(naxes[0]);
629 height =
static_cast<int>(naxes[1]);
630 bitsPerPixel = abs(bitpix);
633 long totalPixels = naxes[0] * naxes[1];
635 totalPixels *= naxes[2];
638 size_t bytesPerPixel = bitsPerPixel / 8;
639 rawData.resize(totalPixels * bytesPerPixel);
645 switch (bitsPerPixel)
658 fits_close_file(fptr, &status);
662 if (fits_read_img(fptr, datatype, firstPixel, totalPixels,
nullptr, rawData.data(),
nullptr, &status))
665 fits_close_file(fptr, &status);
669 fits_close_file(fptr, &status);
672 convertCoordinateSystem(rawData, width, height, bytesPerPixel);
675 "Extracted FITS image: %dx%d, %d-bit, %d-axis, %zu bytes",
676 width, height, bitsPerPixel, naxis, rawData.size());
681void CameraBridge::convertCoordinateSystem(std::vector<uint8_t> &imageData,
int width,
int height,
int bytesPerPixel)
686 size_t rowSize = width * bytesPerPixel;
687 std::vector<uint8_t> tempRow(rowSize);
689 for (
int y = 0; y < height / 2; y++)
691 uint8_t* topRow = imageData.data() + (y * rowSize);
692 uint8_t* bottomRow = imageData.data() + ((height - 1 - y) * rowSize);
695 std::memcpy(tempRow.data(), topRow, rowSize);
696 std::memcpy(topRow, bottomRow, rowSize);
697 std::memcpy(bottomRow, tempRow.data(), rowSize);
701void CameraBridge::formatImageAsJSON(
const std::vector<uint8_t> &rawData,
int width,
int height,
708 json imageArray = json::array();
713 for (
int x = 0; x < width; x++)
715 json column = json::array();
716 for (
int y = 0; y < height; y++)
718 int pixelIndex = (y * width + x);
719 int32_t pixelValue = 0;
721 switch (bitsPerPixel)
724 pixelValue =
static_cast<int32_t
>(rawData[pixelIndex]);
727 pixelValue =
static_cast<int32_t
>(
reinterpret_cast<const uint16_t*
>(rawData.data())[pixelIndex]);
730 pixelValue =
static_cast<int32_t
>(
reinterpret_cast<const uint32_t*
>(rawData.data())[pixelIndex]);
743 for (
int x = 0; x < width; x++)
745 json column = json::array();
746 for (
int y = 0; y < height; y++)
748 json pixel = json::array();
749 for (
int p = 0; p < planes; p++)
751 int pixelIndex = (p * width * height) + (y * width + x);
752 int32_t pixelValue = 0;
754 switch (bitsPerPixel)
757 pixelValue =
static_cast<int32_t
>(rawData[pixelIndex]);
760 pixelValue =
static_cast<int32_t
>(
reinterpret_cast<const uint16_t*
>(rawData.data())[pixelIndex]);
763 pixelValue =
static_cast<int32_t
>(
reinterpret_cast<const uint32_t*
>(rawData.data())[pixelIndex]);
775 response = imageArray;
void handleName(const httplib::Request &req, httplib::Response &res) override
void handleDriverInfo(const httplib::Request &req, httplib::Response &res) override
std::string getDeviceName() const override
std::string getUniqueID() const override
void handleDriverVersion(const httplib::Request &req, httplib::Response &res) override
CameraBridge(INDI::BaseDevice device, int deviceNumber)
void handleConnected(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
void handleDescription(const httplib::Request &req, httplib::Response &res) override
void handleInterfaceVersion(const httplib::Request &req, httplib::Response &res) override
void sendNewSwitch(const INDI::PropertySwitch &switchProperty)
static DeviceManager * getInstance()
void sendNewNumber(const INDI::PropertyNumber &numberProperty)
Class to provide basic INDI device functionality.
const char * getDeviceName() const
uint16_t getDriverInterface() const
getDriverInterface returns ORed values of DRIVER_INTERFACE. It presents the device classes supported ...
const char * getName() const
Provides generic container for INDI properties.
bool isNameMatch(const char *otherName) 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
void push_back(basic_json &&val)
add an object to an array
#define DEBUGDEVICE(device, priority, msg)
#define DEBUGFDEVICE(device, priority, msg,...)
std::multimap< std::string, std::string > Params
basic_json<> json
default specialization
bool has_param(const std::string &key) const
std::string get_param_value(const std::string &key, size_t id=0) const
void set_content(const char *s, size_t n, const std::string &content_type)