26static std::unique_ptr<AlpacaFocuser> alpacaFocuser(
new AlpacaFocuser());
41 return "Alpaca Focuser";
57 DeviceInfoTP[DESCRIPTION].
fill(
"DESCRIPTION",
"Description",
"");
58 DeviceInfoTP[DRIVER_INFO].
fill(
"DRIVER_INFO",
"Driver Info",
"");
59 DeviceInfoTP[DRIVER_VERSION].
fill(
"DRIVER_VERSION",
"Driver Version",
"");
60 DeviceInfoTP[INTERFACE_VERSION].
fill(
"INTERFACE_VERSION",
"Interface Version",
"");
64 TemperatureNP[0].
fill(
"TEMPERATURE",
"Temperature (°C)",
"%.2f", -50, 100, 0, 0);
68 TempCompSP[TEMP_COMP_ENABLE].
fill(
"TEMP_COMP_ENABLE",
"Enable",
ISS_OFF);
69 TempCompSP[TEMP_COMP_DISABLE].
fill(
"TEMP_COMP_DISABLE",
"Disable",
ISS_ON);
88 if (m_TempCompAvailable)
108 LOG_INFO(
"Connecting to alpaca Focuser...");
121 LOG_ERROR(
"Failed to create HTTP client");
125 m_AlpacaClient->set_connection_timeout(5, 0);
126 m_AlpacaClient->set_read_timeout(10, 0);
130 if (!sendAlpacaGET(
"/connected", response))
132 LOG_ERROR(
"Failed to connect to Alpaca device");
136 LOG_INFO(
"Connected to Alpaca device");
139 if (!sendAlpacaPUT(
"/connected",
"Connected=true", response))
141 LOG_ERROR(
"Failed to set device connected state");
146 if (sendAlpacaGET(
"/description", response) && response.
contains(
"Value"))
148 std::string desc = response[
"Value"].
get<std::string>();
149 DeviceInfoTP[DESCRIPTION].setText(desc.c_str());
152 if (sendAlpacaGET(
"/driverinfo", response) && response.
contains(
"Value"))
154 std::string info = response[
"Value"].
get<std::string>();
155 DeviceInfoTP[DRIVER_INFO].setText(info.c_str());
158 if (sendAlpacaGET(
"/driverversion", response) && response.
contains(
"Value"))
160 std::string ver = response[
"Value"].
get<std::string>();
161 DeviceInfoTP[DRIVER_VERSION].setText(ver.c_str());
164 if (sendAlpacaGET(
"/interfaceversion", response) && response.
contains(
"Value"))
166 int ifver = response[
"Value"].
get<
int>();
167 DeviceInfoTP[INTERFACE_VERSION].setText(std::to_string(ifver).c_str());
170 DeviceInfoTP.
apply();
179 LOG_INFO(
"alpaca Focuser connected successfully");
186 LOG_INFO(
"Disconnecting alpaca Focuser...");
190 sendAlpacaPUT(
"/connected",
"Connected=false", response);
192 m_AlpacaClient.reset();
194 LOG_INFO(
"alpaca Focuser disconnected");
198bool AlpacaFocuser::setupFocuser()
203 if (sendAlpacaGET(
"/maxstep", response) && response.
contains(
"Value"))
205 int maxStep = response[
"Value"].
get<
int>();
209 LOGF_INFO(
"Focuser max position: %d steps", maxStep);
213 int currentPos = getPosition();
219 LOGF_INFO(
"Current focuser position: %d steps", currentPos);
223 if (sendAlpacaGET(
"/temperature", response) && response.
contains(
"Value"))
225 double temp = response[
"Value"].
get<
double>();
226 TemperatureNP[0].setValue(temp);
227 TemperatureNP.
apply();
228 LOGF_INFO(
"Focuser temperature: %.2f°C", temp);
232 if (sendAlpacaGET(
"/absolute", response) && response.
contains(
"Value"))
234 bool absolute = response[
"Value"].
get<
bool>();
237 LOG_WARN(
"Focuser does not support absolute positioning!");
241 LOG_INFO(
"Absolute positioning confirmed");
246 if (sendAlpacaGET(
"/tempcompavailable", response) && response.
contains(
"Value"))
248 m_TempCompAvailable = response[
"Value"].
get<
bool>();
249 LOGF_INFO(
"Temperature compensation available: %s", m_TempCompAvailable ?
"yes" :
"no");
251 if (m_TempCompAvailable && sendAlpacaGET(
"/tempcomp", response) && response.
contains(
"Value"))
253 bool tempComp = response[
"Value"].
get<
bool>();
255 TempCompSP[tempComp ? TEMP_COMP_ENABLE : TEMP_COMP_DISABLE].
setState(
ISS_ON);
268 LOGF_ERROR(
"Target position %u exceeds maximum %u", targetTicks,
273 LOGF_INFO(
"Moving to absolute position: %u", targetTicks);
276 std::string data =
"Position=" + std::to_string(targetTicks);
278 if (!sendAlpacaPUT(
"/move", data, response))
280 LOGF_ERROR(
"Failed to move to position %u", targetTicks);
285 if (response.
contains(
"ErrorNumber"))
287 int errorNum = response[
"ErrorNumber"].
get<
int>();
290 std::string errorMsg = response.
contains(
"ErrorMessage") ?
291 response[
"ErrorMessage"].
get<std::string>() :
"Unknown error";
292 LOGF_ERROR(
"Error moving focuser: %d - %s", errorNum, errorMsg.c_str());
297 m_TargetPosition = targetTicks;
305 LOG_INFO(
"Aborting focuser movement");
309 if (!sendAlpacaPUT(
"/halt",
"", response))
319 LOG_INFO(
"Focuser movement halted");
330 if (sendAlpacaGET(
"/temperature", response) && response.
contains(
"Value"))
332 double temp = response[
"Value"].
get<
double>();
333 if (std::abs(temp - TemperatureNP[0].getValue()) > 0.1)
335 TemperatureNP[0].setValue(temp);
336 TemperatureNP.
apply();
341 if (m_TempCompAvailable && sendAlpacaGET(
"/tempcomp", response) && response.
contains(
"Value"))
343 bool tempComp = response[
"Value"].
get<
bool>();
344 int activeIndex = tempComp ? TEMP_COMP_ENABLE : TEMP_COMP_DISABLE;
345 if (TempCompSP[activeIndex].getState() !=
ISS_ON)
357 bool moving = isMoving();
365 int currentPos = getPosition();
372 LOGF_INFO(
"Focuser reached position: %d", currentPos);
378 int currentPos = getPosition();
390bool AlpacaFocuser::isMoving()
394 if (!sendAlpacaGET(
"/ismoving", response))
401 return response[
"Value"].
get<
bool>();
407int AlpacaFocuser::getPosition()
411 if (!sendAlpacaGET(
"/position", response))
418 return response[
"Value"].
get<
int>();
430 TempCompSP.
update(states, names, n);
435 std::string data = std::string(
"TempComp=") + (enable ?
"true" :
"false");
437 if (!sendAlpacaPUT(
"/tempcomp", data, response))
439 LOG_ERROR(
"Failed to set temperature compensation");
445 LOGF_INFO(
"Temperature compensation %s", enable ?
"enabled" :
"disabled");
465bool AlpacaFocuser::sendAlpacaGET(
const std::string &endpoint,
nlohmann::json &response)
470 std::string url =
"/api/v1/focuser/" + std::to_string(m_DeviceNumber) + endpoint;
474 auto res = m_AlpacaClient->Get(url.c_str());
478 LOGF_ERROR(
"HTTP GET failed for %s", url.c_str());
482 if (res->status != 200)
484 LOGF_ERROR(
"HTTP GET returned status %d for %s", res->status, url.c_str());
490 response = nlohmann::json::parse(res->body);
493 if (response.
contains(
"ErrorNumber"))
495 int errorNum = response[
"ErrorNumber"].
get<
int>();
498 std::string errorMsg = response.
contains(
"ErrorMessage") ?
499 response[
"ErrorMessage"].
get<std::string>() :
"Unknown error";
500 LOGF_WARN(
"Alpaca error %d: %s", errorNum, errorMsg.c_str());
514bool AlpacaFocuser::sendAlpacaPUT(
const std::string &endpoint,
const std::string &data,
nlohmann::json &response)
519 std::string url =
"/api/v1/focuser/" + std::to_string(m_DeviceNumber) + endpoint;
522 std::string fullData = data;
523 if (!fullData.empty())
525 fullData +=
"ClientID=" + std::to_string(m_ClientID);
526 fullData +=
"&ClientTransactionID=" + std::to_string(++m_TransactionID);
528 LOGF_DEBUG(
"PUT %s: %s", url.c_str(), fullData.c_str());
530 auto res = m_AlpacaClient->Put(url.c_str(), fullData,
"application/x-www-form-urlencoded");
534 LOGF_ERROR(
"HTTP PUT failed for %s", url.c_str());
538 if (res->status != 200)
540 LOGF_ERROR(
"HTTP PUT returned status %d for %s", res->status, url.c_str());
546 response = nlohmann::json::parse(res->body);
549 if (response.
contains(
"ErrorNumber"))
551 int errorNum = response[
"ErrorNumber"].
get<
int>();
554 std::string errorMsg = response.
contains(
"ErrorMessage") ?
555 response[
"ErrorMessage"].
get<std::string>() :
"Unknown error";
556 LOGF_WARN(
"Alpaca error %d: %s", errorNum, errorMsg.c_str());
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual const char * getDefaultName() override
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Processes incoming new text values from the client for this device. Delegates processing to the contr...
virtual bool Disconnect() override
Disconnect from device.
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
void setDefaultHost(const char *addressHost)
void setDefaultPort(uint32_t addressPort)
virtual const char * host() const
virtual uint32_t port() const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
void addDebugControl()
Add Debug control to the driver.
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusMaxPosNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
Connection::TCP * tcpConnection
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Processes incoming new text values from the client for this device. Delegates processing to the contr...
void setSupportedConnections(const uint8_t &value)
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
a class to store JSON values
bool contains(const typename object_t::key_type &key) const
check the existence of an element in a JSON object
auto get() const noexcept(noexcept(std::declval< const basic_json_t & >().template get_impl< ValueType >(detail::priority_tag< 4 > {}))) -> decltype(std::declval< const basic_json_t & >().template get_impl< ValueType >(detail::priority_tag< 4 > {}))
get a (pointer) value (explicit)
general exception of the basic_json class
const char * what() const noexcept override
returns the explanatory string
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
#define LOGF_INFO(fmt,...)
#define LOGF_WARN(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)