31#define FILTERCUBE_TIMEOUT 5
32#define FILTERCUBE_STATUS_TIMEOUT 2
33#define FILTERCUBE_MIN_FIRMWARE 20260312.0
34#define FILTERCUBE_MODEL "WFC50"
36static std::unique_ptr<RasaFilterCube> rasa_filtercube(
new RasaFilterCube());
47 return "Wanderer FilterCube";
60 IUFillNumber(&DeviceIDN[0],
"DEVICE_ID",
"ID",
"%0.0f", 0, 10, 1, 0);
64 IUFillText(&DeviceInfoT[0],
"MODEL",
"Model",
"");
65 IUFillText(&DeviceInfoT[1],
"FIRMWARE",
"Firmware",
"");
81 DeviceIDN[0].value = mDeviceID;
98 for (
int i = 0; i < FILTERCUBE_MAX_FILTERS; i++)
101 char filterLabel[16];
103 snprintf(filterName,
sizeof(filterName),
"FILTER_SLOT_NAME_%d", i + 1);
104 snprintf(filterLabel,
sizeof(filterLabel),
"Filter %d", i + 1);
105 snprintf(filterText,
sizeof(filterText),
"%c", mFilterLetters[i]);
108 oneWidget.
fill(filterName, filterLabel, filterText);
124 for (
int i = 0; i < FILTERCUBE_MAX_FILTERS; i++)
131 while (*p ==
' ' || *p ==
'\t')
134 if (c >=
'a' && c <=
'z')
137 bool singleLetter = (c >=
'B' && c <=
'Z') &&
138 (after ==
'\0' || after ==
' ' || after ==
'\t' || after ==
'\r' || after ==
'\n');
143 LOGF_ERROR(
"Filter %d name '%s' invalid — must be a single letter (B-Z).", i + 1, txt);
145 char orig[2] = { mFilterLetters[i] ? mFilterLetters[i] :
'X',
'\0' };
151 char norm[2] = { c,
'\0' };
154 int code = (161 + i) * 10000 + (c -
'A' + 1);
156 snprintf(
cmd,
sizeof(
cmd),
"%d", code);
160 LOGF_ERROR(
"Failed to set filter %d name to %c.", i + 1, c);
165 mFilterLetters[i] = c;
166 LOGF_INFO(
"Filter %d name set to %c.", i + 1, c);
172 FilterNameTP.
apply(
"Invalid filter name(s): must be a single letter (B-Z). Invalid entries reverted.");
182 tcflush(
PortFD, TCIOFLUSH);
185 char field[64] = {0};
186 int rc = 0, nbytes_read = 0;
188 constexpr int maxResync = 32;
189 bool foundModel =
false;
191 for (
int skip = 0; skip < maxResync; ++skip)
195 LOG_ERROR(
"Failed to read from RASA FilterCube during handshake.");
199 while (nbytes_read > 0 && (field[nbytes_read - 1] ==
'A' || field[nbytes_read - 1] ==
'\r' || field[nbytes_read - 1] ==
'\n'))
202 field[nbytes_read] =
'\0';
206 while (*p ==
' ' || *p ==
'\t')
209 std::memmove(field, p, std::strlen(p) + 1);
213 snprintf(mModel,
sizeof(mModel),
"%s", field);
221 LOG_ERROR(
"RASA FilterCube model identifier (WFC50) not found.");
227 LOG_ERROR(
"Failed to read firmware version from RASA FilterCube.");
230 while (nbytes_read > 0 && (field[nbytes_read - 1] ==
'A' || field[nbytes_read - 1] ==
'\r' || field[nbytes_read - 1] ==
'\n'))
233 field[nbytes_read] =
'\0';
235 snprintf(mFirmware,
sizeof(mFirmware),
"%s", field);
237 double fwVersion = std::atof(mFirmware);
240 LOGF_ERROR(
"RASA FilterCube firmware %.0f is too old (minimum: %d). "
241 "Please update the firmware in WandererEmpire.",
245 LOGF_INFO(
"RASA FilterCube firmware: %s", mFirmware);
249 LOG_ERROR(
"Failed to read current filter position from RASA FilterCube.");
252 while (nbytes_read > 0 && (field[nbytes_read - 1] ==
'A' || field[nbytes_read - 1] ==
'\r' || field[nbytes_read - 1] ==
'\n'))
255 field[nbytes_read] =
'\0';
257 current = std::atoi(field);
258 if (current >= 1 && current <= FILTERCUBE_MAX_FILTERS)
264 char namesField[16] = {0};
267 LOG_WARN(
"Failed to read filter names from RASA FilterCube; using defaults.");
271 while (nbytes_read > 0 && (namesField[nbytes_read - 1] ==
'A' || namesField[nbytes_read - 1] ==
'\r' || namesField[nbytes_read - 1] ==
'\n'))
274 namesField[nbytes_read] =
'\0';
276 if (nbytes_read >= FILTERCUBE_MAX_FILTERS)
278 for (
int i = 0; i < FILTERCUBE_MAX_FILTERS; i++)
279 mFilterLetters[i] = namesField[i];
281 mFilterLetters[0], mFilterLetters[1], mFilterLetters[2],
282 mFilterLetters[3], mFilterLetters[4]);
286 for (
int i = 0; i < FILTERCUBE_MAX_FILTERS; i++)
290 LOGF_WARN(
"Failed to read offset %d from RASA FilterCube.", i + 1);
297 LOG_WARN(
"Failed to read device ID from RASA FilterCube.");
301 while (nbytes_read > 0 && (field[nbytes_read - 1] ==
'A' || field[nbytes_read - 1] ==
'\r' || field[nbytes_read - 1] ==
'\n'))
304 field[nbytes_read] =
'\0';
306 mDeviceID = std::atoi(field);
309 LOGF_INFO(
"RASA FilterCube connected - Firmware: %s, ID: %d, Position: %d",
334 if (position < 1 || position > FILTERCUBE_MAX_FILTERS)
336 LOGF_ERROR(
"Requested filter position %d is out of range (1-%d).", position, FILTERCUBE_MAX_FILTERS);
357 LOG_ERROR(
"Failed to send move command to RASA FilterCube.");
361 tcflush(
PortFD, TCIFLUSH);
364 constexpr int maxAttempts = 150;
366 while (attempts++ < maxAttempts)
369 if (readCurrentFilterFromStatus(current))
385 LOG_ERROR(
"Timed out waiting for RASA FilterCube to reach target position.");
391 if (dev !=
nullptr && std::strcmp(dev,
getDeviceName()) == 0)
394 if (std::strcmp(DeviceIDNP.
name, name) == 0)
398 int id =
static_cast<int>(DeviceIDN[0].value);
399 if (id < 0 || id > 10)
408 snprintf(
cmd,
sizeof(
cmd),
"%d", 1900000 +
id);
429bool RasaFilterCube::sendCommand(
const char *command,
char *response,
int responseLen,
int timeoutSeconds)
431 int nbytes_written = 0;
440 const int linelen = std::snprintf(line,
sizeof(line),
"%s\n", command);
441 if (linelen <= 0 || linelen >=
static_cast<int>(
sizeof(line)))
443 LOG_ERROR(
"RASA FilterCube command too long for transmit buffer.");
447 if (response !=
nullptr && responseLen > 1)
448 tcflush(
PortFD, TCIFLUSH);
454 LOGF_ERROR(
"Error sending command to RASA FilterCube: %s", errstr);
458 if (response ==
nullptr || responseLen <= 1)
470 while (nbytes_read > 0 && (response[nbytes_read - 1] ==
'\r' || response[nbytes_read - 1] ==
'\n'))
473 response[nbytes_read] =
'\0';
475 if (nbytes_read >= responseLen)
476 nbytes_read = responseLen - 1;
477 response[nbytes_read] =
'\0';
484void filtercubeStripField(
char *field,
int &nbytes_read)
486 while (nbytes_read > 0)
488 char c = field[nbytes_read - 1];
489 if (c ==
'A' || c ==
'\r' || c ==
'\n')
492 field[nbytes_read] =
'\0';
497 field[nbytes_read] =
'\0';
500void filtercubeTrimLeft(
char *s)
503 while (*p ==
' ' || *p ==
'\t' || *p ==
'\r' || *p ==
'\n')
506 std::memmove(s, p, std::strlen(p) + 1);
510bool RasaFilterCube::readCurrentFilterFromStatus(
int &position)
514 char field[64] = {0};
519 constexpr int maxResync = 32;
521 for (
int skip = 0; skip < maxResync; ++skip)
525 filtercubeStripField(field, nbytes_read);
527 filtercubeTrimLeft(field);
536 filtercubeStripField(field, nbytes_read);
538 int current = std::atoi(field);
539 LOGF_DEBUG(
"status: position field='%s' -> %d", field, current);
540 if (current >= 1 && current <= FILTERCUBE_MAX_FILTERS)
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
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)
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options 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 bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Connection::Serial * serialConnection
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
void setFilterConnection(const uint8_t &value)
setFilterConnection Set Filter connection mode. Child class should call this in the constructor befor...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
int PortFD
For Serial & TCP connections.
void push(WidgetView< T > &&item)
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * getGroupName() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
INDI::PropertyViewText * getText() const
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
bool Handshake() override
perform handshake with device to check communication
bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
bool GetFilterNames() override
Obtains a list of filter names from the hardware and initializes the FilterNameTP property....
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
bool SelectFilter(int position) override
Select a new filter position.
bool SetFilterNames() override
Set filter names as defined by the client for each filter position. The desired filter names are stor...
const char * getDefaultName() override
int QueryFilter() override
Return current filter position.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Implementations for common driver routines.
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#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,...)
#define FILTERCUBE_TIMEOUT
#define FILTERCUBE_MIN_FIRMWARE
#define FILTERCUBE_STATUS_TIMEOUT
void fill(const char *name, const char *label, const char *initialText)