Skip to content

Commit 2958e2d

Browse files
committed
Several fixes to INDI Alpaca drivers. Use better defaults. Fix Camera driver connection.
1 parent 1076b15 commit 2958e2d

6 files changed

Lines changed: 216 additions & 208 deletions

File tree

drivers/ccd/indi_alpaca_ccd.cpp

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,18 @@ AlpacaCCD::AlpacaCCD()
5151

5252
void AlpacaCCD::setDefaultServerAddress(const char *host, const char *port, bool force)
5353
{
54+
// Only apply defaults if no value was loaded from saved config (mirrors TCP plugin pattern).
55+
// m_ConfigHost/m_ConfigPort are empty when no config file entry exists for this device,
56+
// meaning this is either a first run or the user has never saved settings.
5457
if (host != nullptr)
5558
{
56-
const char *currentHost = ServerAddressTP[0].getText();
57-
if (force || currentHost == nullptr || strlen(currentHost) == 0)
59+
if (force || m_ConfigHost.empty())
5860
ServerAddressTP[0].setText(host);
5961
}
6062

6163
if (port != nullptr)
6264
{
63-
const char *currentPort = ServerAddressTP[1].getText();
64-
if (force || currentPort == nullptr || strlen(currentPort) == 0)
65+
if (force || m_ConfigPort.empty())
6566
ServerAddressTP[1].setText(port);
6667
}
6768
}
@@ -76,11 +77,21 @@ bool AlpacaCCD::initProperties()
7677
CoolerSP[INDI_DISABLED].fill("COOLER_OFF", "OFF", ISS_ON);
7778
CoolerSP.fill(getDeviceName(), "CCD_COOLER", "Cooler", MAIN_CONTROL_TAB, IP_WO, ISR_1OFMANY, 0, IPS_IDLE);
7879

79-
// Setup server address properties
80-
ServerAddressTP[0].fill("HOST", "Host", "");
81-
ServerAddressTP[1].fill("PORT", "Port", "11111");
80+
// Try to load server address from saved config (mirrors TCP plugin pattern).
81+
// m_ConfigHost/m_ConfigPort are empty if no config was previously saved.
82+
char configHost[MAXINDINAME] = {0};
83+
char configPort[MAXINDINAME] = {0};
84+
if (IUGetConfigText(getDeviceName(), "SERVER_ADDRESS", "HOST", configHost, MAXINDINAME) == 0)
85+
m_ConfigHost = configHost;
86+
if (IUGetConfigText(getDeviceName(), "SERVER_ADDRESS", "PORT", configPort, MAXINDINAME) == 0)
87+
m_ConfigPort = configPort;
88+
89+
// Setup server address properties using config values (empty string if no config saved).
90+
// Subclasses call setDefaultServerAddress() after initProperties() to set their own defaults,
91+
// which will only take effect if m_ConfigHost/m_ConfigPort are still empty.
92+
ServerAddressTP[0].fill("HOST", "Host", m_ConfigHost.c_str());
93+
ServerAddressTP[1].fill("PORT", "Port", m_ConfigPort.c_str());
8294
ServerAddressTP.fill(getDeviceName(), "SERVER_ADDRESS", "Server", CONNECTION_TAB, IP_RW, 60, IPS_IDLE);
83-
ServerAddressTP.load();
8495

8596
// Setup device number property
8697
DeviceNumberNP[0].fill("DEVICE_NUMBER", "Device Number", "%.0f", 0, 10, 1, 0);
@@ -1015,12 +1026,7 @@ bool AlpacaCCD::sendAlpacaPUT(const std::string& endpoint, const nlohmann::json&
10151026
std::string url = getAlpacaURL(endpoint);
10161027
std::string form_data = buildAlpacaFormData(request);
10171028

1018-
httplib::Headers headers =
1019-
{
1020-
{"Content-Type", "application/x-www-form-urlencoded"}
1021-
};
1022-
1023-
auto result = httpClient->Put(url.c_str(), headers, form_data, "application/x-www-form-urlencoded");
1029+
auto result = httpClient->Put(url, form_data, "application/x-www-form-urlencoded");
10241030

10251031
if (!result)
10261032
{

drivers/ccd/indi_alpaca_ccd.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ class AlpacaCCD : public INDI::CCD
103103
void setDefaultServerAddress(const char *host, const char *port, bool force = false);
104104

105105
private:
106+
// Config-loaded values (empty = no saved config, mirrors TCP plugin pattern)
107+
std::string m_ConfigHost;
108+
std::string m_ConfigPort;
109+
106110
// Internal state variables
107111
bool m_ExposureInProgress {false};
108112
std::string m_CurrentReadoutModeName {"0"}; // Default to mode 0

drivers/ccd/indi_seestar_ccd.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ bool SeestarCCD::initProperties()
5858
{
5959
// Call parent class first
6060
AlpacaCCD::initProperties();
61-
61+
6262
// Set Seestar-specific default connection parameters
63-
setDefaultServerAddress("seestar.local", "32323");
64-
63+
setDefaultServerAddress("SeeStar.local", "32323");
64+
6565
// Remove cooler capability (Seestar doesn't have active cooling)
6666
uint32_t cap = GetCCDCapability();
6767
cap &= ~CCD_HAS_COOLER;
6868
SetCCDCapability(cap);
69-
69+
7070
return true;
7171
}
7272

@@ -78,13 +78,13 @@ bool SeestarCCD::Connect()
7878
// Call base class Connect
7979
if (!AlpacaCCD::Connect())
8080
return false;
81-
81+
8282
// Force remove cooler capability after base class may have added it
8383
// (Seestar reports cansetccdtemperature=true but we don't want to expose cooler controls)
8484
uint32_t cap = GetCCDCapability();
8585
cap &= ~CCD_HAS_COOLER;
8686
SetCCDCapability(cap);
87-
87+
8888
return true;
8989
}
9090

@@ -95,15 +95,15 @@ bool SeestarCCD::updateProperties()
9595
{
9696
// Call base class first
9797
AlpacaCCD::updateProperties();
98-
98+
9999
// Remove cooler properties if they were added by base class
100100
if (isConnected())
101101
{
102102
deleteProperty("CCD_COOLER");
103103
deleteProperty("CCD_TEMPERATURE");
104104
deleteProperty("CCD_COOLER_POWER");
105105
}
106-
106+
107107
return true;
108108
}
109109

@@ -125,7 +125,7 @@ std::string SeestarCCD::buildAlpacaFormData(const nlohmann::json& request)
125125
// Seestar requires method parameters FIRST, then ClientID/ClientTransactionID
126126
// This is opposite of standard Alpaca ordering
127127
std::string form_data;
128-
128+
129129
// Add method-specific parameters first
130130
for (auto& [key, value] : request.items())
131131
{
@@ -177,8 +177,8 @@ bool SeestarCCD::parseImageBytesMetadata(ImageBytesMetadata* metadata, size_t bo
177177

178178
// Log the raw metadata before corrections
179179
LOGF_INFO("Seestar raw metadata: Rank=%d, Dim1=%d, Dim2=%d, Dim3=%d, TransType=%d, DataStart=%d",
180-
metadata->Rank, metadata->Dimension1, metadata->Dimension2, metadata->Dimension3,
181-
metadata->TransmissionElementType, metadata->DataStart);
180+
metadata->Rank, metadata->Dimension1, metadata->Dimension2, metadata->Dimension3,
181+
metadata->TransmissionElementType, metadata->DataStart);
182182

183183
// Apply Seestar-specific workarounds
184184
// Bug #1: Dimension1 and Dimension2 are swapped

drivers/focuser/indi_alpaca_focuser.cpp

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
INDI alpaca Focuser Driver
3-
3+
44
Copyright (C) 2024 Gord Tulloch
55
66
This library is free software; you can redistribute it and/or
@@ -29,13 +29,11 @@ AlpacaFocuser::AlpacaFocuser()
2929
{
3030
setVersion(1, 0);
3131
FI::SetCapability(FOCUSER_CAN_ABS_MOVE | FOCUSER_CAN_ABORT);
32-
33-
tcpConnection = new Connection::TCP(this);
32+
setSupportedConnections(CONNECTION_TCP);
3433
}
3534

3635
AlpacaFocuser::~AlpacaFocuser()
3736
{
38-
delete tcpConnection;
3937
}
4038

4139
const char *AlpacaFocuser::getDefaultName()
@@ -48,11 +46,12 @@ bool AlpacaFocuser::initProperties()
4846
INDI::Focuser::initProperties();
4947

5048
// Use built-in TCP connection with default alpaca.local:32323
51-
setActiveConnection(tcpConnection);
5249
tcpConnection->setDefaultHost("alpaca.local");
5350
tcpConnection->setDefaultPort(32323);
54-
tcpConnection->registerHandshake([&]() { return Handshake(); });
55-
registerConnection(tcpConnection);
51+
tcpConnection->registerHandshake([&]()
52+
{
53+
return Handshake();
54+
});
5655

5756
// Device information
5857
DeviceInfoTP[DESCRIPTION].fill("DESCRIPTION", "Description", "");
@@ -241,7 +240,7 @@ IPState AlpacaFocuser::MoveAbsFocuser(uint32_t targetTicks)
241240
// Validate range
242241
if (targetTicks > FocusMaxPosNP[0].getValue())
243242
{
244-
LOGF_ERROR("Target position %u exceeds maximum %u", targetTicks,
243+
LOGF_ERROR("Target position %u exceeds maximum %u", targetTicks,
245244
static_cast<uint32_t>(FocusMaxPosNP[0].getValue()));
246245
return IPS_ALERT;
247246
}
@@ -250,7 +249,7 @@ IPState AlpacaFocuser::MoveAbsFocuser(uint32_t targetTicks)
250249

251250
nlohmann::json response;
252251
std::string data = "Position=" + std::to_string(targetTicks);
253-
252+
254253
if (!sendAlpacaPUT("/move", data, response))
255254
{
256255
LOGF_ERROR("Failed to move to position %u", targetTicks);
@@ -263,16 +262,16 @@ IPState AlpacaFocuser::MoveAbsFocuser(uint32_t targetTicks)
263262
int errorNum = response["ErrorNumber"].get<int>();
264263
if (errorNum != 0)
265264
{
266-
std::string errorMsg = response.contains("ErrorMessage") ?
267-
response["ErrorMessage"].get<std::string>() : "Unknown error";
265+
std::string errorMsg = response.contains("ErrorMessage") ?
266+
response["ErrorMessage"].get<std::string>() : "Unknown error";
268267
LOGF_ERROR("Error moving focuser: %d - %s", errorNum, errorMsg.c_str());
269268
return IPS_ALERT;
270269
}
271270
}
272271

273272
m_TargetPosition = targetTicks;
274273
m_Moving = true;
275-
274+
276275
return IPS_BUSY;
277276
}
278277

@@ -281,7 +280,7 @@ bool AlpacaFocuser::AbortFocuser()
281280
LOG_INFO("Aborting focuser movement");
282281

283282
nlohmann::json response;
284-
283+
285284
if (!sendAlpacaPUT("/halt", "", response))
286285
{
287286
LOG_ERROR("Failed to halt focuser");
@@ -291,7 +290,7 @@ bool AlpacaFocuser::AbortFocuser()
291290
m_Moving = false;
292291
FocusAbsPosNP.setState(IPS_IDLE);
293292
FocusAbsPosNP.apply();
294-
293+
295294
LOG_INFO("Focuser movement halted");
296295
return true;
297296
}
@@ -317,20 +316,20 @@ void AlpacaFocuser::TimerHit()
317316
if (m_Moving)
318317
{
319318
bool moving = isMoving();
320-
319+
321320
if (!moving)
322321
{
323322
// Movement completed
324323
m_Moving = false;
325-
324+
326325
// Update current position
327326
int currentPos = getPosition();
328327
if (currentPos >= 0)
329328
{
330329
FocusAbsPosNP[0].setValue(currentPos);
331330
FocusAbsPosNP.setState(IPS_OK);
332331
FocusAbsPosNP.apply();
333-
332+
334333
LOGF_INFO("Focuser reached position: %d", currentPos);
335334
}
336335
}
@@ -352,7 +351,7 @@ void AlpacaFocuser::TimerHit()
352351
bool AlpacaFocuser::isMoving()
353352
{
354353
nlohmann::json response;
355-
354+
356355
if (!sendAlpacaGET("/ismoving", response))
357356
{
358357
return false;
@@ -369,7 +368,7 @@ bool AlpacaFocuser::isMoving()
369368
int AlpacaFocuser::getPosition()
370369
{
371370
nlohmann::json response;
372-
371+
373372
if (!sendAlpacaGET("/position", response))
374373
{
375374
return -1;
@@ -399,11 +398,11 @@ bool AlpacaFocuser::sendAlpacaGET(const std::string &endpoint, nlohmann::json &r
399398
return false;
400399

401400
std::string url = "/api/v1/focuser/" + std::to_string(m_DeviceNumber) + endpoint;
402-
401+
403402
LOGF_DEBUG("GET %s", url.c_str());
404-
403+
405404
auto res = m_AlpacaClient->Get(url.c_str());
406-
405+
407406
if (!res)
408407
{
409408
LOGF_ERROR("HTTP GET failed for %s", url.c_str());
@@ -419,20 +418,20 @@ bool AlpacaFocuser::sendAlpacaGET(const std::string &endpoint, nlohmann::json &r
419418
try
420419
{
421420
response = nlohmann::json::parse(res->body);
422-
421+
423422
// Check for Alpaca errors
424423
if (response.contains("ErrorNumber"))
425424
{
426425
int errorNum = response["ErrorNumber"].get<int>();
427426
if (errorNum != 0)
428427
{
429-
std::string errorMsg = response.contains("ErrorMessage") ?
430-
response["ErrorMessage"].get<std::string>() : "Unknown error";
428+
std::string errorMsg = response.contains("ErrorMessage") ?
429+
response["ErrorMessage"].get<std::string>() : "Unknown error";
431430
LOGF_WARN("Alpaca error %d: %s", errorNum, errorMsg.c_str());
432431
return false;
433432
}
434433
}
435-
434+
436435
return true;
437436
}
438437
catch (const nlohmann::json::exception &e)
@@ -448,18 +447,18 @@ bool AlpacaFocuser::sendAlpacaPUT(const std::string &endpoint, const std::string
448447
return false;
449448

450449
std::string url = "/api/v1/focuser/" + std::to_string(m_DeviceNumber) + endpoint;
451-
450+
452451
// Add client info to data
453452
std::string fullData = data;
454453
if (!fullData.empty())
455454
fullData += "&";
456455
fullData += "ClientID=" + std::to_string(m_ClientID);
457456
fullData += "&ClientTransactionID=" + std::to_string(++m_TransactionID);
458-
457+
459458
LOGF_DEBUG("PUT %s: %s", url.c_str(), fullData.c_str());
460-
459+
461460
auto res = m_AlpacaClient->Put(url.c_str(), fullData, "application/x-www-form-urlencoded");
462-
461+
463462
if (!res)
464463
{
465464
LOGF_ERROR("HTTP PUT failed for %s", url.c_str());
@@ -475,20 +474,20 @@ bool AlpacaFocuser::sendAlpacaPUT(const std::string &endpoint, const std::string
475474
try
476475
{
477476
response = nlohmann::json::parse(res->body);
478-
477+
479478
// Check for Alpaca errors
480479
if (response.contains("ErrorNumber"))
481480
{
482481
int errorNum = response["ErrorNumber"].get<int>();
483482
if (errorNum != 0)
484483
{
485-
std::string errorMsg = response.contains("ErrorMessage") ?
486-
response["ErrorMessage"].get<std::string>() : "Unknown error";
484+
std::string errorMsg = response.contains("ErrorMessage") ?
485+
response["ErrorMessage"].get<std::string>() : "Unknown error";
487486
LOGF_WARN("Alpaca error %d: %s", errorNum, errorMsg.c_str());
488487
return false;
489488
}
490489
}
491-
490+
492491
return true;
493492
}
494493
catch (const nlohmann::json::exception &e)

0 commit comments

Comments
 (0)