Instrument Neutral Distributed Interface INDI 2.2.5
Excalibur.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2022 RBFocus. All rights reserved.
3 Copyright(c) 2025 Jasem Mutlaq. All rights reserved.
4
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the Free
7 Software Foundation; either version 2 of the License, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19
20 The full GNU General Public License is included in this distribution in the
21 file called LICENSE.
22*******************************************************************************/
23
24#include "Excalibur.h"
25
26#include "indicom.h"
28
29#include <cerrno>
30#include <cstring>
31#include <memory>
32#include <chrono>
33#include <thread>
34
35#include <termios.h>
36#include <unistd.h>
37#include <inttypes.h>
38#include <sys/ioctl.h>
39#include <math.h>
40
41static std::unique_ptr<Excalibur> flatmaster(new Excalibur());
42
50
55{
57
60
62
63 LightIntensityNP[0].setMin(0);
64 LightIntensityNP[0].setMax(3000);
65 LightIntensityNP[0].setStep(100);
66
68
69 serialConnection = new Connection::Serial(this);
71 serialConnection->registerHandshake([&]()
72 {
73 return Ack();
74 });
75
76
77 registerConnection(serialConnection);
78 return true;
79}
80
85{
87
90
91 if (isConnected())
92 {
93 getParkingStatus();
94 getLightIntensity();
95 }
96
97 return true;
98}
99
104{
105 return "RBF Excalibur";
106}
107
111bool Excalibur::Ack()
112{
113 PortFD = serialConnection->getPortFD();
114
115 char response[16] = {0};
116 // Try up to 3 times before giving up
117 for (int i = 0; i < 3; i++)
118 {
119 if(sendCommand("#", response))
120 {
121 if(strstr("FLAT.FLAP!#", response) != nullptr)
122 {
125 LightSP.apply();
126 return true;
127
128 }
129 }
130 std::this_thread::sleep_for(std::chrono::milliseconds(100));
131 }
132 LOG_ERROR("Ack failed.");
133 return false;
134
135}
136
145
150{
151 char response[20] = {0};
152 char cmd[16] = {0};
153 snprintf(cmd, 16, "L%d##", enable ? static_cast<int>(LightIntensityNP[0].getValue()) : 0);
154 return sendCommand(cmd, response);
155}
156
161{
162 // If light box is not on, then we simply accept the value as-is
163 // without dispatching the command to the device
164 if (value > 0 && LightSP[FLAT_LIGHT_ON].getState() != ISS_ON)
165 return true;
166
167 if( ParkCapSP[0].getState() != ISS_ON)
168 {
169 LOG_ERROR("You must Park eXcalibur first.");
170 return false;
171 }
172
173 char cmd[DRIVER_RES] = {0};
174 snprintf(cmd, 30, "L%d##", value);
175 return sendCommand(cmd);
176}
177
182{
183 return sendCommand("S1#") ? IPS_BUSY : IPS_ALERT;
184}
185
190{
191 return sendCommand("S0#") ? IPS_BUSY : IPS_ALERT;
192}
193
198{
199 if (!isConnected())
200 return;
201
202 getParkingStatus();
203
205}
206
210bool Excalibur::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
211{
212 if (LI::processNumber(dev, name, values, names, n))
213 return true;
214
215 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
216}
217
221bool Excalibur::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
222{
223 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
224 {
225 if (LI::processText(dev, name, texts, names, n))
226 return true;
227 }
228
229 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
230}
231
235bool Excalibur::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
236{
237 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
238 {
239 if (DI::processSwitch(dev, name, states, names, n))
240 return true;
241 if (LI::processSwitch(dev, name, states, names, n))
242 return true;
243 }
244
245 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
246}
247
252{
253 LI::snoop(root);
255}
256
265
269void Excalibur::getLightIntensity()
270{
271 char res[DRIVER_RES] = {0};
272
273 // Get Light Intensity Status
274 sendCommand("O#", res);
275
276 int32_t pos;
277 int rc = sscanf(res, "%d#", &pos);
278
279 auto previousIntensity = LightIntensityNP[0].getValue();
280
281 if (rc > 0)
282 {
283 LightIntensityNP[0].setValue(pos);
284 // Only update if necessary
285 if (previousIntensity != pos)
287 }
288
289 auto haveLight = pos > 0;
290
291 // If we have light, but switch is off, then turn it on and vice versa
292 if ( (haveLight && LightSP[FLAT_LIGHT_OFF].getState() == ISS_ON) || (!haveLight
293 && LightSP[FLAT_LIGHT_ON].getState() == ISS_ON) )
294 {
295 LightSP.reset();
298 LightSP.apply();
299 }
300
301}
305void Excalibur::getParkingStatus()
306{
307 char res[DRIVER_RES] = {0};
308
309 // Get Dust Cap Status
310 sendCommand("P#", res);
311 int32_t pos2;
312 sscanf(res, "%d#", &pos2);
313
314 auto isClosed = pos2 <= 0;
315
317 {
320 // Parked? If closed, then yes
322 // Unparked?
325 }
326}
327
331bool Excalibur::sendCommand(const char *command, char *res)
332{
333 int nbytes_written = 0, nbytes_read = 0, rc = -1;
334 char errstr[MAXRBUF];
335
336 char cmd[20] = {0};
337 snprintf(cmd, 20, "%s", command);
338
339 LOGF_DEBUG("CMD <%#02X>", cmd[0]);
340
341 tcflush(PortFD, TCIOFLUSH);
342
343 if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
344 {
345 char errstr[MAXRBUF] = {0};
346 tty_error_msg(rc, errstr, MAXRBUF);
347 LOGF_ERROR("Serial write error: %s.", errstr);
348 return false;
349 }
350 if (res == nullptr)
351 return true;
352
353 if ((rc = tty_read_section(PortFD, res, DRIVER_DEL, 5, &nbytes_read)) != TTY_OK)
354 {
355 tty_error_msg(rc, errstr, MAXRBUF);
356 LOGF_ERROR("command: %s error: %s.", cmd, errstr);
357 return false;
358 }
359
360 // Remove the #
361 res[nbytes_read - 1] = 0;
362
363 LOGF_DEBUG("RES <%s>", res);
364
365 tcflush(PortFD, TCIOFLUSH);
366
367 return true;
368}
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Provides interface to implement remotely controlled dust cover.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition Excalibur.cpp:84
virtual bool EnableLightBox(bool enable) override
EnableLightBox Turn on/off on a light box. Must be implemented in the child class.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition Excalibur.cpp:54
virtual IPState ParkCap() override
Park dust cap (close cover). Must be implemented by child.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool Disconnect() override
Disconnect from device.
virtual bool SetLightBoxBrightness(uint16_t value) override
setBrightness Set light level. Must be implemented in the child class, if supported.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual IPState UnParkCap() override
unPark dust cap (open cover). Must be implemented by child.
const char * getDefaultName() override
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
bool isConnected() const
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
virtual bool Disconnect()
Disconnect from device.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
INDI::PropertySwitch ParkCapSP
bool updateProperties()
updateProperties Defines or Delete properties based on default device connection status
void initProperties(const char *group, uint32_t capabilities=0)
Initialize dust cap properties. It is recommended to call this function within initProperties() of yo...
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process dust cap switch properties.
void initProperties(const char *group, uint32_t capabilities)
Initialize light box properties. It is recommended to call this function within initProperties() of y...
INDI::PropertyNumber LightIntensityNP
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process light box switch properties.
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process light box text properties.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process light box number properties.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
Provides interface to implement controllable light box/switch device.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:653
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
#define MAXRBUF
Definition indidriver.c:52
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
uint8_t cmd[4]
Definition pwc-ioctl.h:2