25static std::unique_ptr<AlpacaFilterWheel> alpacaFilterWheel(
new AlpacaFilterWheel());
34 return "Alpaca Filter Wheel";
45 ServerAddressTP[HOST_INDEX].
fill(
"HOST",
"Host", m_Host.c_str());
46 ServerAddressTP[PORT_INDEX].
fill(
"PORT",
"Port", std::to_string(m_Port).c_str());
48 ServerAddressTP.
load();
51 if (ServerAddressTP[HOST_INDEX].text !=
nullptr && strlen(ServerAddressTP[HOST_INDEX].text) > 0)
53 m_Host = ServerAddressTP[HOST_INDEX].text;
55 if (ServerAddressTP[PORT_INDEX].text !=
nullptr && strlen(ServerAddressTP[PORT_INDEX].text) > 0)
57 m_Port = std::stoi(ServerAddressTP[PORT_INDEX].text);
61 DeviceInfoTP[DESCRIPTION].
fill(
"DESCRIPTION",
"Description",
"");
62 DeviceInfoTP[DRIVER_INFO].
fill(
"DRIVER_INFO",
"Driver Info",
"");
63 DeviceInfoTP[DRIVER_VERSION].
fill(
"DRIVER_VERSION",
"Driver Version",
"");
64 DeviceInfoTP[INTERFACE_VERSION].
fill(
"INTERFACE_VERSION",
"Interface Version",
"");
68 FocusOffsetsNP[0].
fill(
"OFFSET_0",
"Dark Offset",
"%.0f", -1000, 1000, 1, 0);
69 FocusOffsetsNP[1].
fill(
"OFFSET_1",
"IR Offset",
"%.0f", -1000, 1000, 1, 0);
70 FocusOffsetsNP[2].
fill(
"OFFSET_2",
"LP Offset",
"%.0f", -1000, 1000, 1, 0);
106 LOG_INFO(
"Connecting to alpaca FilterWheel...");
112 LOG_ERROR(
"Failed to create HTTP client");
116 m_AlpacaClient->set_connection_timeout(5, 0);
117 m_AlpacaClient->set_read_timeout(10, 0);
121 if (!sendAlpacaGET(
"/connected", response))
123 LOG_ERROR(
"Failed to connect to Alpaca device");
127 LOG_INFO(
"Connected to Alpaca device");
130 if (!sendAlpacaPUT(
"/connected",
"Connected=true", response))
132 LOG_ERROR(
"Failed to set device connected state");
137 if (sendAlpacaGET(
"/description", response) && response.
contains(
"Value"))
139 std::string desc = response[
"Value"].
get<std::string>();
140 DeviceInfoTP[DESCRIPTION].setText(desc.c_str());
143 if (sendAlpacaGET(
"/driverinfo", response) && response.
contains(
"Value"))
145 std::string info = response[
"Value"].
get<std::string>();
146 DeviceInfoTP[DRIVER_INFO].setText(info.c_str());
149 if (sendAlpacaGET(
"/driverversion", response) && response.
contains(
"Value"))
151 std::string ver = response[
"Value"].
get<std::string>();
152 DeviceInfoTP[DRIVER_VERSION].setText(ver.c_str());
155 if (sendAlpacaGET(
"/interfaceversion", response) && response.
contains(
"Value"))
157 int ifver = response[
"Value"].
get<
int>();
158 DeviceInfoTP[INTERFACE_VERSION].setText(std::to_string(ifver).c_str());
161 DeviceInfoTP.
apply();
164 if (!setupFilterWheel())
166 LOG_ERROR(
"Failed to setup filter wheel");
170 LOG_INFO(
"alpaca FilterWheel connected successfully");
176 LOG_INFO(
"Disconnecting alpaca FilterWheel...");
180 sendAlpacaPUT(
"/connected",
"Connected=false", response);
182 m_AlpacaClient.reset();
184 LOG_INFO(
"alpaca FilterWheel disconnected");
188bool AlpacaFilterWheel::setupFilterWheel()
193 if (sendAlpacaGET(
"/names", response) && response.
contains(
"Value"))
195 auto names = response[
"Value"];
196 if (names.is_array() && !names.empty())
204 for (
size_t i = 0; i < names.size(); i++)
206 std::string name = names[i].get<std::string>();
210 snprintf(filterName,
MAXINDINAME,
"FILTER_SLOT_NAME_%zu", i + 1);
211 snprintf(filterLabel,
MAXINDILABEL,
"Filter#%zu", i + 1);
214 oneText.
fill(filterName, filterLabel, name.c_str());
217 LOGF_INFO(
"Filter %zu: %s", i, name.c_str());
224 FilterSlotNP[0].setMax(
static_cast<double>(names.size()));
229 LOGF_INFO(
"Found %zu filters", names.size());
234 if (sendAlpacaGET(
"/focusoffsets", response) && response.
contains(
"Value"))
236 auto offsets = response[
"Value"];
237 if (offsets.is_array())
239 for (
size_t i = 0; i < offsets.size() && i < 3; i++)
241 int offset = offsets[i].
get<
int>();
242 FocusOffsetsNP[i].setValue(offset);
243 LOGF_INFO(
"Filter %zu focus offset: %d steps", i, offset);
245 FocusOffsetsNP.
apply();
253 LOGF_INFO(
"Current filter position: %d", currentPos);
264 int targetPos = position - 1;
267 LOGF_INFO(
"Selecting filter position %d (%s)", targetPos, filterName);
271 if (currentPos == targetPos)
273 LOGF_INFO(
"Already at position %d, no movement needed", targetPos);
279 std::string data =
"Position=" + std::to_string(targetPos);
281 if (!sendAlpacaPUT(
"/position", data, response))
283 LOGF_ERROR(
"Failed to set filter position to %d", targetPos);
288 if (response.
contains(
"ErrorNumber"))
290 int errorNum = response[
"ErrorNumber"].
get<
int>();
293 std::string errorMsg = response.
contains(
"ErrorMessage") ?
294 response[
"ErrorMessage"].
get<std::string>() :
"Unknown error";
295 LOGF_ERROR(
"Error setting filter position: %d - %s", errorNum, errorMsg.c_str());
300 LOGF_INFO(
"Filter position set to %d", targetPos);
312 if (!sendAlpacaGET(
"/position", response))
314 LOG_ERROR(
"Failed to query filter position");
320 int pos = response[
"Value"].
get<
int>();
331 ServerAddressTP.
save(fp);
343 ServerAddressTP.
update(texts, names, n);
345 m_Host = ServerAddressTP[HOST_INDEX].text;
346 m_Port = std::stoi(ServerAddressTP[PORT_INDEX].text);
348 ServerAddressTP.
apply();
351 LOGF_INFO(
"Server address updated: %s:%d", m_Host.c_str(), m_Port);
359bool AlpacaFilterWheel::sendAlpacaGET(
const std::string &endpoint,
nlohmann::json &response)
364 std::string url =
"/api/v1/filterwheel/" + std::to_string(m_DeviceNumber) + endpoint;
368 auto res = m_AlpacaClient->Get(url.c_str());
372 LOGF_ERROR(
"HTTP GET failed for %s", url.c_str());
376 if (res->status != 200)
378 LOGF_ERROR(
"HTTP GET returned status %d for %s", res->status, url.c_str());
384 response = nlohmann::json::parse(res->body);
387 if (response.
contains(
"ErrorNumber"))
389 int errorNum = response[
"ErrorNumber"].
get<
int>();
392 std::string errorMsg = response.
contains(
"ErrorMessage") ?
393 response[
"ErrorMessage"].
get<std::string>() :
"Unknown error";
394 LOGF_WARN(
"Alpaca error %d: %s", errorNum, errorMsg.c_str());
408bool AlpacaFilterWheel::sendAlpacaPUT(
const std::string &endpoint,
const std::string &data,
nlohmann::json &response)
413 std::string url =
"/api/v1/filterwheel/" + std::to_string(m_DeviceNumber) + endpoint;
416 std::string fullData = data;
417 if (!fullData.empty())
419 fullData +=
"ClientID=" + std::to_string(m_ClientID);
420 fullData +=
"&ClientTransactionID=" + std::to_string(++m_TransactionID);
422 LOGF_DEBUG(
"PUT %s: %s", url.c_str(), fullData.c_str());
424 auto res = m_AlpacaClient->Put(url.c_str(), fullData,
"application/x-www-form-urlencoded");
428 LOGF_ERROR(
"HTTP PUT failed for %s", url.c_str());
432 if (res->status != 200)
434 LOGF_ERROR(
"HTTP PUT returned status %d for %s", res->status, url.c_str());
440 response = nlohmann::json::parse(res->body);
443 if (response.
contains(
"ErrorNumber"))
445 int errorNum = response[
"ErrorNumber"].
get<
int>();
446 if (errorNum != 0 && errorNum != 1279)
448 std::string errorMsg = response.
contains(
"ErrorMessage") ?
449 response[
"ErrorMessage"].
get<std::string>() :
"Unknown error";
450 LOGF_WARN(
"Alpaca error %d: %s", errorNum, errorMsg.c_str());
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool Disconnect() override
Disconnect from device.
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual const char * getDefaultName() override
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool SelectFilter(int position) override
Select a new filter position.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual int QueryFilter() override
Return current filter position.
INDI::PropertyText getText(const char *name) const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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)
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber FilterSlotNP
INDI::PropertyText FilterNameTP
void SelectFilterDone(int newpos)
The child class calls this function when the hardware successfully finished selecting a new filter wh...
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
void setFilterConnection(const uint8_t &value)
setFilterConnection Set Filter connection mode. Child class should call this in the constructor befor...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
void push(WidgetView< T > &&item)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
bool isNameMatch(const char *otherName) const
const char * getGroupName() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const char *const texts[], const char *const names[], int n)
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 * CONNECTION_TAB
CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlle...
const char * FILTER_TAB
FILTER_TAB Where all the properties for filter wheels are located.
const char * INFO_TAB
INFO_TAB Where all the properties for general information are located.
#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,...)
void fill(const char *name, const char *label, const char *initialText)