Instrument Neutral Distributed Interface INDI 2.2.5
pegasus_uch.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2021 Chrysikos Efstathios. All rights reserved.
3
4 Pegasus USB Control Hub
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 The full GNU General Public License is included in this distribution in the
22 file called LICENSE.
23*******************************************************************************/
24
25#include "pegasus_uch.h"
26#include "indicom.h"
28
29#include <memory>
30#include <regex>
31#include <termios.h>
32#include <cstring>
33#include <sys/ioctl.h>
34#include <chrono>
35#include <math.h>
36#include <iomanip>
37
38
39
40static std::unique_ptr<PegasusUCH> uch(new PegasusUCH());
41
49
54{
56
59
63
64 // Reboot
65 IUFillSwitch(&RebootS[0], "REBOOT", "Reboot Device", ISS_OFF);
66 IUFillSwitchVector(&RebootSP, RebootS, 1, getDeviceName(), "REBOOT_DEVICE", "Device", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1,
67 60, IPS_IDLE);
68
69 // Led
70 IUFillSwitch(&PowerLEDS[POWER_LED_ON], "POWER_LED_ON", "On", ISS_ON);
71 IUFillSwitch(&PowerLEDS[POWER_LED_OFF], "POWER_LED_OFF", "Off", ISS_OFF);
72 IUFillSwitchVector(&PowerLEDSP, PowerLEDS, 2, getDeviceName(), "POWER_LED", "LED", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60,
73 IPS_IDLE);
74
75 // USB
76 IUFillSwitch(&USBPort1S[USB_OFF], "USBPORT1_OFF", "Off", ISS_ON);
77 IUFillSwitch(&USBPort1S[USB_ON], "USBPORT1_ON", "On", ISS_OFF);
78 IUFillSwitchVector(&USBPort1SP, USBPort1S, 2, getDeviceName(), "USBPort1", "", USB_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
79
80 IUFillSwitch(&USBPort2S[USB_OFF], "USBPORT2_OFF", "Off", ISS_ON);
81 IUFillSwitch(&USBPort2S[USB_ON], "USBPORT2_ON", "On", ISS_OFF);
82 IUFillSwitchVector(&USBPort2SP, USBPort2S, 2, getDeviceName(), "USBPort2", "", USB_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
83
84 IUFillSwitch(&USBPort3S[USB_OFF], "USBPORT3_OFF", "Off", ISS_ON);
85 IUFillSwitch(&USBPort3S[USB_ON], "USBPORT3_ON", "On", ISS_OFF);
86 IUFillSwitchVector(&USBPort3SP, USBPort3S, 2, getDeviceName(), "USBPort3", "", USB_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
87
88 IUFillSwitch(&USBPort4S[USB_OFF], "USBPORT4_OFF", "Off", ISS_ON);
89 IUFillSwitch(&USBPort4S[USB_ON], "USBPORT4_ON", "On", ISS_OFF);
90 IUFillSwitchVector(&USBPort4SP, USBPort4S, 2, getDeviceName(), "USBPort4", "", USB_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
91
92 IUFillSwitch(&USBPort5S[USB_OFF], "USBPORT5_OFF", "Off", ISS_ON);
93 IUFillSwitch(&USBPort5S[USB_ON], "USBPORT5_ON", "On", ISS_OFF);
94 IUFillSwitchVector(&USBPort5SP, USBPort5S, 2, getDeviceName(), "USBPort5", "", USB_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
95
96 IUFillSwitch(&USBPort6S[USB_OFF], "USBPORT1_OFF", "Off", ISS_ON);
97 IUFillSwitch(&USBPort6S[USB_ON], "USBPORT1_ON", "On", ISS_OFF);
98 IUFillSwitchVector(&USBPort6SP, USBPort6S, 2, getDeviceName(), "USBPort6", "", USB_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
99
100
101
105 IUFillText(&InfoT[INFO_VERSION], "VERSION", "Version", "NA");
106 IUFillText(&InfoT[INFO_UPTIME], "UPTIME", "Uptime (h)", "NA");
107 IUFillText(&InfoT[INFO_USBVOLTAGE], "USBVOLTAGE", "USB Voltage", "NA");
108 IUFillTextVector(&InfoTP, InfoT, 3, getDeviceName(), "INFO", "INFO", INFO_TAB, IP_RO, 60,
109 IPS_IDLE);
110
111
112
113
114
115
116
117
118
122 serialConnection = new Connection::Serial(this);
124 serialConnection->registerHandshake([&]()
125 {
126 return Handshake();
127 });
128 registerConnection(serialConnection);
129
130 return true;
131}
132
137{
139
140 if (isConnected())
141 {
142 // Main Control
143 defineProperty(&RebootSP);
144
145 // USB
146 defineProperty(&USBPort1SP);
147 defineProperty(&USBPort2SP);
148 defineProperty(&USBPort3SP);
149 defineProperty(&USBPort4SP);
150 defineProperty(&USBPort5SP);
151 defineProperty(&USBPort6SP);
152
153 // Led
154 defineProperty(&PowerLEDSP);
155
156 // Firmware
157 defineProperty(&InfoTP);
158 initialized = true;
159 }
160 else
161 {
162 // Main Control
163 deleteProperty(RebootSP.name);
164
165 // USB
166 deleteProperty(USBPort1SP.name);
167 deleteProperty(USBPort2SP.name);
168 deleteProperty(USBPort3SP.name);
169 deleteProperty(USBPort4SP.name);
170 deleteProperty(USBPort5SP.name);
171 deleteProperty(USBPort6SP.name);
172
173 // Led
174 deleteProperty(PowerLEDSP.name);
175
176 //Firmware
177 deleteProperty(InfoTP.name);
178
179 initialized = false;
180 }
181 return true;
182}
183
184
188bool PegasusUCH::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
189{
190 if (dev && !strcmp(dev, getDeviceName()))
191 {
192 // Reboot
193 if (!strcmp(name, RebootSP.name))
194 {
195 RebootSP.s = reboot() ? IPS_OK : IPS_ALERT;
196 IDSetSwitch(&RebootSP, nullptr);
197 LOG_INFO("Rebooting device...");
198 return true;
199 }
200
201
202
203 // Power LED
204 if (!strcmp(name, PowerLEDSP.name))
205 {
206 int prevIndex = IUFindOnSwitchIndex(&PowerLEDSP);
207 IUUpdateSwitch(&PowerLEDSP, states, names, n);
208 if (setPowerLEDEnabled(PowerLEDS[0].s == ISS_ON))
209 {
210 PowerLEDSP.s = IPS_OK;
211 }
212 else
213 {
214 IUResetSwitch(&PowerLEDSP);
215 PowerLEDS[prevIndex].s = ISS_ON;
216 PowerLEDSP.s = IPS_ALERT;
217 }
218
219 IDSetSwitch(&PowerLEDSP, nullptr);
220 return true;
221 }
222
223 if (!strcmp(name, USBPort1SP.name))
224 {
225 return this->setUSBPort(1, USBPort1S, USBPort1SP, states, names, n);
226 }
227 else if(!strcmp(name, USBPort2SP.name))
228 {
229 return this->setUSBPort(2, USBPort2S, USBPort2SP, states, names, n);
230 }
231 else if(!strcmp(name, USBPort3SP.name))
232 {
233 return this->setUSBPort(3, USBPort3S, USBPort3SP, states, names, n);
234 }
235 else if(!strcmp(name, USBPort4SP.name))
236 {
237 return this->setUSBPort(4, USBPort4S, USBPort4SP, states, names, n);
238 }
239 else if(!strcmp(name, USBPort5SP.name))
240 {
241 return this->setUSBPort(5, USBPort5S, USBPort5SP, states, names, n);
242 }
243 else if(!strcmp(name, USBPort6SP.name))
244 {
245 return this->setUSBPort(6, USBPort6S, USBPort6SP, states, names, n);
246 }
247 }
248
249 return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
250}
251
252
253bool PegasusUCH::setUSBPort(uint8_t port, ISwitch usbPortS[2], ISwitchVectorProperty sp, ISState * states, char * names[],
254 int n)
255{
256
257
258 int prevIndex = IUFindOnSwitchIndex(&sp);
259 IUUpdateSwitch(&sp, states, names, n);
260 if (setUSBPortEnabled(port, usbPortS[1].s == ISS_ON))
261 {
262 sp.s = IPS_OK;
263 }
264 else
265 {
266 IUResetSwitch(&sp);
267 usbPortS[prevIndex].s = ISS_ON;
268 sp.s = IPS_ALERT;
269 }
270
271 IDSetSwitch(&sp, nullptr);
272 return true;
273}
274
275
280{
281 return "Pegasus UCH";
282}
283
284
289{
291 IUSaveConfigSwitch(fp, &PowerLEDSP);
292 return true;
293}
294
295
300{
301 if (!isConnected() || initialized == false)
302 {
304 return;
305 }
306
307
308 this->updateUSBPower();
309 this->updateUpTime();
310
312}
313
314
318std::vector<std::string> PegasusUCH::split(const std::string &input, const std::string &regex)
319{
320 // passing -1 as the submatch index parameter performs splitting
321 std::regex re(regex);
322 std::sregex_token_iterator
323 first{input.begin(), input.end(), re, -1},
324 last;
325 return {first, last};
326}
327
328
332bool PegasusUCH::Handshake()
333{
334 PortFD = serialConnection->getPortFD();
335
336 char response[PEGASUS_LEN] = {0};
337 if(sendCommand("P#", response))
338 {
339 if(strstr("UCH_OK", response) != nullptr)
340 {
341 setFirmwareVersion();
342 setBootstrapUSB();
343 this->initialized = true;
344 return true;
345 }
346 }
347 else
348 {
349 LOG_ERROR("Ack failed.");
350 }
351
352 this->initialized = false;
353 return false;
354}
355
356
360bool PegasusUCH::reboot()
361{
362 return sendCommand("PF", nullptr);
363}
364
365
369bool PegasusUCH::setPowerLEDEnabled(bool enabled)
370{
371 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
372 snprintf(cmd, PEGASUS_LEN, "PL:%d", enabled ? 1 : 0);
373 if (sendCommand(cmd, res))
374 {
375 return (!strcmp(res, cmd));
376 }
377
378 return false;
379}
380
381
385bool PegasusUCH::setUSBPortEnabled(uint8_t port, bool enabled)
386{
387 char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
388 snprintf(cmd, PEGASUS_LEN, "U%d:%d", port, enabled ? 1 : 0);
389 snprintf(expected, PEGASUS_LEN, "U%d:%d", port, enabled ? 1 : 0);
390 if (sendCommand(cmd, res))
391 {
392 return (!strcmp(res, expected));
393 }
394
395 return false;
396}
397
398
402void PegasusUCH::setFirmwareVersion()
403{
404 char response[PEGASUS_LEN] = {0};
405
406 if(sendCommand("PV", response))
407 {
408 IUSaveText(&InfoT[INFO_VERSION], response);
409 IDSetText(&InfoTP, nullptr);
410 }
411 else
412 {
413 InfoTP.s = IPS_ALERT;
414 LOG_ERROR("Error on updateFirmware.");
415 }
416}
417
418
419
423void PegasusUCH::setBootstrapUSB()
424{
425 char response[PEGASUS_LEN] = {0};
426 if(sendCommand("PA", response))
427 {
428 std::vector<std::string> result = split(response, ":");
429 auto usbPortStatus = result[2];
430
431
432 for(std::string::size_type i = 0; i < usbPortStatus.size(); ++i)
433 {
434 auto x = usbPort[i];
435 bool s = usbPortStatus[i] == '1' ? true : false;
436 x.S[USB_ON].s = s ? ISS_ON : ISS_OFF;
437 x.S[USB_OFF].s = s ? ISS_OFF : ISS_ON;
438 x.SP->s = IPS_OK;
439 IDSetSwitch(x.SP, nullptr);
440 }
441 }
442}
443
444
448void PegasusUCH::updateUSBPower()
449{
450 char response[PEGASUS_LEN] = {0};
451
452 if(sendCommand("PA", response))
453 {
454 std::vector<std::string> result = split(response, ":");
455
456
457 if(result.size() != 3)
458 {
459 LOGF_WARN("Received wrong number (%i) of data (%s). Retrying...", result.size(), response);
460 return;
461 }
462
463 std::string usbBusVoltage = result[1];
464
465
466 IUSaveText(&InfoT[INFO_USBVOLTAGE], usbBusVoltage.c_str());
467 IDSetText(&InfoTP, nullptr);
468 }
469}
470
471
475void PegasusUCH::updateUpTime()
476{
477 char response[PEGASUS_LEN] = {0};
478
479 if(sendCommand("PC", response))
480 {
481 std::vector<std::string> result = split(response, ":");
482
483
484 if(result.size() != 2)
485 {
486 LOGF_WARN("Received wrong number (%i) of data (%s). Retrying...", result.size(), response);
487 return;
488 }
489
490 std::chrono::milliseconds uptime(std::stol(result[1]));
491 using dhours = std::chrono::duration<double, std::ratio<3600>>;
492 std::stringstream ss;
493 ss << std::fixed << std::setprecision(3) << dhours(uptime).count();
494 IUSaveText(&InfoT[INFO_UPTIME], ss.str().c_str());
495 IDSetText(&InfoTP, nullptr);
496 }
497}
498
502bool PegasusUCH::sendCommand(const char * command, char * res)
503{
504 int nbytes_read = 0, nbytes_written = 0, rc = -1;
505 LOGF_DEBUG("CMD <%s>", command);
506
507 char errstr[MAXRBUF];
508
509 char cmd[PEGASUS_LEN] = {0};
510 snprintf(cmd, 10, "%s\n", command);
511
512 LOGF_DEBUG("CMD <%#02X>", cmd[0]);
513
514 tcflush(PortFD, TCIOFLUSH);
515
516 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
517 {
518 tty_error_msg(rc, errstr, MAXRBUF);
519 LOGF_ERROR("command: %s error: %s.", cmd, errstr);
520 return false;
521 }
522
523 if ((rc = tty_read_section(PortFD, res, 0xA, 3, &nbytes_read)) != TTY_OK)
524 {
525 tty_error_msg(rc, errstr, MAXRBUF);
526 LOGF_ERROR("command: %s error: %s.", cmd, errstr);
527 return false;
528 }
529
530 // Get rid of 0xA
531 res[nbytes_read - 1] = 0;
532
533 if(res[nbytes_read - 2] == '\r')
534 res[nbytes_read - 2] = 0;
535
536 LOGF_DEBUG("RES <%s>", res);
537
538 tcflush(PortFD, TCIOFLUSH);
539
540 return true;
541}
542
543
547void PegasusUCH::cleanupResponse(char *response)
548{
549 std::string s(response);
550 s.erase(std::remove_if(s.begin(), s.end(),
551 [](unsigned char x)
552 {
553 return std::isspace(x);
554 }), s.end());
555 snprintf(response, PEGASUS_LEN, "%s", s.c_str());
556}
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...
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....
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
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 deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
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.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
const char * getDefaultName() override
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
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
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
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(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
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
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
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.
Definition indidevapi.c:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
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.
Definition indidevapi.c:416
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#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
#define LOG_INFO(txt)
Definition indilogger.h:74
uint8_t cmd[4]
Definition pwc-ioctl.h:2
One switch descriptor.
Switch vector property descriptor.
Definition indiapi.h:365
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248