Instrument Neutral Distributed Interface INDI  2.0.2
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 
40 static std::unique_ptr<PegasusUCH> uch(new PegasusUCH());
41 
46 {
47  setVersion(1, 1);
48 }
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 
188 bool 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 
253 bool 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 
318 std::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 
332 bool 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 
360 bool PegasusUCH::reboot()
361 {
362  return sendCommand("PF", nullptr);
363 }
364 
365 
369 bool 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 
385 bool 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 
402 void 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 
423 void 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 
448 void 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 
475 void 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 
502 bool 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 
547 void 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  strncpy(response, s.c_str(), PEGASUS_LEN);
556 }
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the intial connection to the device...
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
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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 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()
Initilize properties initial state and value. The child class must implement this function.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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
Initilize properties initial state and value. The child class must implement this function.
Definition: pegasus_uch.cpp:53
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.
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_ATMOST1
Definition: indiapi.h:174
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:566
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
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:291
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
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:158
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:198
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:235
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#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
#define MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
One switch descriptor.
Switch vector property descriptor.
Definition: indiapi.h:367
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250