Instrument Neutral Distributed Interface INDI  2.0.2
pegasus_upb.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2018 Jasem Mutlaq. All rights reserved.
3 
4  Pegasus Ultimate Power Box Driver.
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_upb.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 // We declare an auto pointer to PegasusUPB.
39 static std::unique_ptr<PegasusUPB> upb(new PegasusUPB());
40 
41 PegasusUPB::PegasusUPB() : FI(this), WI(this)
42 {
43  setVersion(1, 6);
44 
45  lastSensorData.reserve(21);
46  lastPowerData.reserve(4);
47  lastStepperData.reserve(4);
48  lastDewAggData.reserve(1);
49 }
50 
52 {
54 
56 
63 
65  WI::initProperties(ENVIRONMENT_TAB, ENVIRONMENT_TAB);
66 
68 
72  // Cycle all power on/off
73  IUFillSwitch(&PowerCycleAllS[POWER_CYCLE_ON], "POWER_CYCLE_ON", "All On", ISS_OFF);
74  IUFillSwitch(&PowerCycleAllS[POWER_CYCLE_OFF], "POWER_CYCLE_OFF", "All Off", ISS_OFF);
75  IUFillSwitchVector(&PowerCycleAllSP, PowerCycleAllS, 2, getDeviceName(), "POWER_CYCLE", "Cycle Power", MAIN_CONTROL_TAB,
76  IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
77 
78  // Reboot
79  IUFillSwitch(&RebootS[0], "REBOOT", "Reboot Device", ISS_OFF);
80  IUFillSwitchVector(&RebootSP, RebootS, 1, getDeviceName(), "REBOOT_DEVICE", "Device", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1,
81  60, IPS_IDLE);
82 
83  // Power Sensors
84  IUFillNumber(&PowerSensorsN[SENSOR_VOLTAGE], "SENSOR_VOLTAGE", "Voltage (V)", "%4.2f", 0, 999, 100, 0);
85  IUFillNumber(&PowerSensorsN[SENSOR_CURRENT], "SENSOR_CURRENT", "Current (A)", "%4.2f", 0, 999, 100, 0);
86  IUFillNumber(&PowerSensorsN[SENSOR_POWER], "SENSOR_POWER", "Power (W)", "%4.2f", 0, 999, 100, 0);
87  IUFillNumberVector(&PowerSensorsNP, PowerSensorsN, 3, getDeviceName(), "POWER_SENSORS", "Sensors", MAIN_CONTROL_TAB, IP_RO,
88  60, IPS_IDLE);
89 
90  // Overall Power Consumption
91  IUFillNumber(&PowerConsumptionN[CONSUMPTION_AVG_AMPS], "CONSUMPTION_AVG_AMPS", "Avg. Amps", "%4.2f", 0, 999, 100, 0);
92  IUFillNumber(&PowerConsumptionN[CONSUMPTION_AMP_HOURS], "CONSUMPTION_AMP_HOURS", "Amp Hours", "%4.2f", 0, 999, 100, 0);
93  IUFillNumber(&PowerConsumptionN[CONSUMPTION_WATT_HOURS], "CONSUMPTION_WATT_HOURS", "Watt Hours", "%4.2f", 0, 999, 100, 0);
94  IUFillNumberVector(&PowerConsumptionNP, PowerConsumptionN, 3, getDeviceName(), "POWER_CONSUMPTION", "Consumption",
96 
100 
101  // Dew Labels. Need to delare them here to use in the Power usage section
102  IUFillText(&DewControlsLabelsT[0], "DEW_LABEL_1", "Dew A", "Dew A");
103  IUFillText(&DewControlsLabelsT[1], "DEW_LABEL_2", "Dew B", "Dew B");
104  IUFillText(&DewControlsLabelsT[2], "DEW_LABEL_3", "Dew C", "Dew C");
105  IUFillTextVector(&DewControlsLabelsTP, DewControlsLabelsT, 3, getDeviceName(), "DEW_CONTROL_LABEL", "Dew Labels",
106  DEW_TAB, IP_WO, 60, IPS_IDLE);
107 
108  char dewLabel[MAXINDILABEL];
109 
110  // Turn on/off power and power boot up
111  memset(dewLabel, 0, MAXINDILABEL);
112  int dewRC = IUGetConfigText(getDeviceName(), DewControlsLabelsTP.name, DewControlsLabelsT[0].name, dewLabel,
113  MAXINDILABEL);
114  IUFillSwitch(&AutoDewV2S[DEW_PWM_A], "DEW_A", dewRC == -1 ? "Dew A" : dewLabel, ISS_OFF);
115  memset(dewLabel, 0, MAXINDILABEL);
116  dewRC = IUGetConfigText(getDeviceName(), DewControlsLabelsTP.name, DewControlsLabelsT[1].name, dewLabel,
117  MAXINDILABEL);
118  IUFillSwitch(&AutoDewV2S[DEW_PWM_B], "DEW_B", dewRC == -1 ? "Dew B" : dewLabel, ISS_OFF);
119  memset(dewLabel, 0, MAXINDILABEL);
120  dewRC = IUGetConfigText(getDeviceName(), DewControlsLabelsTP.name, DewControlsLabelsT[2].name, dewLabel,
121  MAXINDILABEL);
122  IUFillSwitch(&AutoDewV2S[DEW_PWM_C], "DEW_C", dewRC == -1 ? "Dew C" : dewLabel, ISS_OFF);
123  IUFillSwitchVector(&AutoDewV2SP, AutoDewV2S, 3, getDeviceName(), "AUTO_DEW", "Auto Dew", DEW_TAB, IP_RW, ISR_NOFMANY, 60,
124  IPS_IDLE);
125 
126  // Dew Labels with custom labels
127  IUFillText(&DewControlsLabelsT[0], "DEW_LABEL_1", "Dew A", AutoDewV2S[0].label);
128  IUFillText(&DewControlsLabelsT[1], "DEW_LABEL_2", "Dew B", AutoDewV2S[1].label);
129  IUFillText(&DewControlsLabelsT[2], "DEW_LABEL_3", "Dew C", AutoDewV2S[2].label);
130  IUFillTextVector(&DewControlsLabelsTP, DewControlsLabelsT, 3, getDeviceName(), "DEW_CONTROL_LABEL", "DEW Labels",
131  DEW_TAB, IP_WO, 60, IPS_IDLE);
132  // Power Labels
133  IUFillText(&PowerControlsLabelsT[0], "POWER_LABEL_1", "Port 1", "Port 1");
134  IUFillText(&PowerControlsLabelsT[1], "POWER_LABEL_2", "Port 2", "Port 2");
135  IUFillText(&PowerControlsLabelsT[2], "POWER_LABEL_3", "Port 3", "Port 3");
136  IUFillText(&PowerControlsLabelsT[3], "POWER_LABEL_4", "Port 4", "Port 4");
137  IUFillTextVector(&PowerControlsLabelsTP, PowerControlsLabelsT, 4, getDeviceName(), "POWER_CONTROL_LABEL", "Power Labels",
138  POWER_TAB, IP_WO, 60, IPS_IDLE);
139 
140  char portLabel[MAXINDILABEL];
141 
142  // Turn on/off power and power boot up
143  memset(portLabel, 0, MAXINDILABEL);
144  int portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[0].name, portLabel,
145  MAXINDILABEL);
146  IUFillSwitch(&PowerControlS[0], "POWER_CONTROL_1", portRC == -1 ? "Port 1" : portLabel, ISS_OFF);
147 
148  memset(portLabel, 0, MAXINDILABEL);
149  portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[1].name, portLabel,
150  MAXINDILABEL);
151  IUFillSwitch(&PowerControlS[1], "POWER_CONTROL_2", portRC == -1 ? "Port 2" : portLabel, ISS_OFF);
152 
153  memset(portLabel, 0, MAXINDILABEL);
154  portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[2].name, portLabel,
155  MAXINDILABEL);
156  IUFillSwitch(&PowerControlS[2], "POWER_CONTROL_3", portRC == -1 ? "Port 3" : portLabel, ISS_OFF);
157 
158  memset(portLabel, 0, MAXINDILABEL);
159  portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[3].name, portLabel,
160  MAXINDILABEL);
161  IUFillSwitch(&PowerControlS[3], "POWER_CONTROL_4", portRC == -1 ? "Port 4" : portLabel, ISS_OFF);
162 
163  IUFillSwitchVector(&PowerControlSP, PowerControlS, 4, getDeviceName(), "POWER_CONTROL", "Power Control", POWER_TAB, IP_RW,
164  ISR_NOFMANY, 60, IPS_IDLE);
165 
166  // Power Labels
167  IUFillText(&PowerControlsLabelsT[0], "POWER_LABEL_1", "Port 1", PowerControlS[0].label);
168  IUFillText(&PowerControlsLabelsT[1], "POWER_LABEL_2", "Port 2", PowerControlS[1].label);
169  IUFillText(&PowerControlsLabelsT[2], "POWER_LABEL_3", "Port 3", PowerControlS[2].label);
170  IUFillText(&PowerControlsLabelsT[3], "POWER_LABEL_4", "Port 4", PowerControlS[3].label);
171  IUFillTextVector(&PowerControlsLabelsTP, PowerControlsLabelsT, 4, getDeviceName(), "POWER_CONTROL_LABEL", "Power Labels",
172  POWER_TAB, IP_WO, 60, IPS_IDLE);
173 
174  // Current Draw
175  IUFillNumber(&PowerCurrentN[0], "POWER_CURRENT_1", PowerControlS[0].label, "%4.2f A", 0, 1000, 0, 0);
176  IUFillNumber(&PowerCurrentN[1], "POWER_CURRENT_2", PowerControlS[1].label, "%4.2f A", 0, 1000, 0, 0);
177  IUFillNumber(&PowerCurrentN[2], "POWER_CURRENT_3", PowerControlS[2].label, "%4.2f A", 0, 1000, 0, 0);
178  IUFillNumber(&PowerCurrentN[3], "POWER_CURRENT_4", PowerControlS[3].label, "%4.2f A", 0, 1000, 0, 0);
179  IUFillNumberVector(&PowerCurrentNP, PowerCurrentN, 4, getDeviceName(), "POWER_CURRENT", "Current Draw", POWER_TAB, IP_RO,
180  60, IPS_IDLE);
181 
182  // Power on Boot
183  IUFillSwitch(&PowerOnBootS[0], "POWER_PORT_1", PowerControlS[0].label, ISS_ON);
184  IUFillSwitch(&PowerOnBootS[1], "POWER_PORT_2", PowerControlS[1].label, ISS_ON);
185  IUFillSwitch(&PowerOnBootS[2], "POWER_PORT_3", PowerControlS[2].label, ISS_ON);
186  IUFillSwitch(&PowerOnBootS[3], "POWER_PORT_4", PowerControlS[3].label, ISS_ON);
187  IUFillSwitchVector(&PowerOnBootSP, PowerOnBootS, 4, getDeviceName(), "POWER_ON_BOOT", "Power On Boot", POWER_TAB, IP_RW,
188  ISR_NOFMANY, 60, IPS_IDLE);
189 
190  // Over Current
191  IUFillLight(&OverCurrentL[0], "POWER_PORT_1", PowerControlS[0].label, IPS_OK);
192  IUFillLight(&OverCurrentL[1], "POWER_PORT_2", PowerControlS[1].label, IPS_OK);
193  IUFillLight(&OverCurrentL[2], "POWER_PORT_3", PowerControlS[2].label, IPS_OK);
194  IUFillLight(&OverCurrentL[3], "POWER_PORT_4", PowerControlS[3].label, IPS_OK);
195 
196  char tempLabel[MAXINDILABEL + 5];
197  memset(tempLabel, 0, MAXINDILABEL + 5);
198  sprintf(tempLabel, "%s %s", "Dew:", AutoDewV2S[0].label);
199  IUFillLight(&OverCurrentL[4], "DEW_A", tempLabel, IPS_OK);
200  memset(tempLabel, 0, MAXINDILABEL);
201  sprintf(tempLabel, "%s %s", "Dew:", AutoDewV2S[1].label);
202  IUFillLight(&OverCurrentL[5], "DEW_B", tempLabel, IPS_OK);
203  memset(tempLabel, 0, MAXINDILABEL);
204  sprintf(tempLabel, "%s %s", "Dew:", AutoDewV2S[2].label);
205  IUFillLight(&OverCurrentL[6], "DEW_C", tempLabel, IPS_OK);
206  IUFillLightVector(&OverCurrentLP, OverCurrentL, 7, getDeviceName(), "POWER_OVER_CURRENT", "Over Current", POWER_TAB,
207  IPS_IDLE);
208 
209  // Power LED
210  IUFillSwitch(&PowerLEDS[POWER_LED_ON], "POWER_LED_ON", "On", ISS_ON);
211  IUFillSwitch(&PowerLEDS[POWER_LED_OFF], "POWER_LED_OFF", "Off", ISS_OFF);
212  IUFillSwitchVector(&PowerLEDSP, PowerLEDS, 2, getDeviceName(), "POWER_LED", "LED", POWER_TAB, IP_RW, ISR_1OFMANY, 60,
213  IPS_IDLE);
214 
215  IUFillNumber(&AdjustableOutputN[0], "ADJUSTABLE_VOLTAGE_VALUE", "Voltage (V)", "%.f", 3, 12, 1, 12);
216  IUFillNumberVector(&AdjustableOutputNP, AdjustableOutputN, 1, getDeviceName(), "ADJUSTABLE_VOLTAGE", "Adj. Output",
217  POWER_TAB, IP_RW, 60, IPS_IDLE);
218 
219 
223 
224  // Automatic Dew v1
225  IUFillSwitch(&AutoDewS[INDI_ENABLED], "INDI_ENABLED", "Enabled", ISS_OFF);
226  IUFillSwitch(&AutoDewS[INDI_DISABLED], "INDI_DISABLED", "Disabled", ISS_ON);
227  IUFillSwitchVector(&AutoDewSP, AutoDewS, 2, getDeviceName(), "AUTO_DEW", "Auto Dew", DEW_TAB, IP_RW, ISR_1OFMANY, 60,
228  IPS_IDLE);
229 
230  // Automatic Dew Aggressiveness v2
231  IUFillNumber(&AutoDewAggN[AUTO_DEW_AGG], "AUTO_DEW_AGG_VALUE", "Auto Dew Agg (50-250)", "%.2f", 50, 250, 20, 0);
232  IUFillNumberVector(&AutoDewAggNP, AutoDewAggN, 1, getDeviceName(), "AUTO_DEW_AGG", "Auto Dew Agg", DEW_TAB, IP_RW, 60,
233  IPS_IDLE);
234 
235  // Dew PWM
236  IUFillNumber(&DewPWMN[DEW_PWM_A], "DEW_A", AutoDewV2S[0].label, "%.2f %%", 0, 100, 10, 0);
237  IUFillNumber(&DewPWMN[DEW_PWM_B], "DEW_B", AutoDewV2S[1].label, "%.2f %%", 0, 100, 10, 0);
238  IUFillNumber(&DewPWMN[DEW_PWM_C], "DEW_C", AutoDewV2S[2].label, "%.2f %%", 0, 100, 10, 0);
239  IUFillNumberVector(&DewPWMNP, DewPWMN, 3, getDeviceName(), "DEW_PWM", "Dew PWM", DEW_TAB, IP_RW, 60, IPS_IDLE);
240 
241  // Dew current draw
242  IUFillNumber(&DewCurrentDrawN[DEW_PWM_A], "DEW_CURRENT_A", AutoDewV2S[0].label, "%4.2f A", 0, 1000, 10, 0);
243  IUFillNumber(&DewCurrentDrawN[DEW_PWM_B], "DEW_CURRENT_B", AutoDewV2S[1].label, "%4.2f A", 0, 1000, 10, 0);
244  IUFillNumber(&DewCurrentDrawN[DEW_PWM_C], "DEW_CURRENT_C", AutoDewV2S[2].label, "%4.2f A", 0, 1000, 10, 0);
245  IUFillNumberVector(&DewCurrentDrawNP, DewCurrentDrawN, 3, getDeviceName(), "DEW_CURRENT", "Dew Current", DEW_TAB, IP_RO, 60,
246  IPS_IDLE);
247 
251 
252  // USB Hub control v1
253  IUFillSwitch(&USBControlS[INDI_ENABLED], "INDI_ENABLED", "Enabled", ISS_ON);
254  IUFillSwitch(&USBControlS[INDI_DISABLED], "INDI_DISABLED", "Disabled", ISS_OFF);
255  IUFillSwitchVector(&USBControlSP, USBControlS, 2, getDeviceName(), "USB_HUB_CONTROL", "Hub", USB_TAB, IP_RW, ISR_1OFMANY,
256  60, IPS_IDLE);
257 
258  // USB Labels
259  IUFillText(&USBControlsLabelsT[0], "USB_LABEL_1", "USB3 Port1", "USB3 Port1");
260  IUFillText(&USBControlsLabelsT[1], "USB_LABEL_2", "USB3 Port2", "USB3 Port2");
261  IUFillText(&USBControlsLabelsT[2], "USB_LABEL_3", "USB3 Port3", "USB3 Port3");
262  IUFillText(&USBControlsLabelsT[3], "USB_LABEL_4", "USB3 Port4", "USB3 Port4");
263  IUFillText(&USBControlsLabelsT[4], "USB_LABEL_5", "USB2 Port5", "USB2 Port5");
264  IUFillText(&USBControlsLabelsT[5], "USB_LABEL_6", "USB2 Port6", "USB2 Port6");
265 
266  IUFillTextVector(&USBControlsLabelsTP, USBControlsLabelsT, 6, getDeviceName(), "USB_CONTROL_LABEL", "USB Labels",
267  USB_TAB, IP_WO, 60, IPS_IDLE);
268 
269  // USB Hub control v2
270 
271  char USBLabel[MAXINDILABEL];
272 
273  // Turn on/off power and power boot up
274  memset(USBLabel, 0, MAXINDILABEL);
275  int USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[0].name, USBLabel,
276  MAXINDILABEL);
277  IUFillSwitch(&USBControlV2S[0], "PORT_1", USBRC == -1 ? "USB3 Port1" : USBLabel, ISS_ON);
278  memset(USBLabel, 0, MAXINDILABEL);
279  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[1].name, USBLabel,
280  MAXINDILABEL);
281  IUFillSwitch(&USBControlV2S[1], "PORT_2", USBRC == -1 ? "USB3 Port2" : USBLabel, ISS_ON);
282  memset(USBLabel, 0, MAXINDILABEL);
283  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[2].name, USBLabel,
284  MAXINDILABEL);
285  IUFillSwitch(&USBControlV2S[2], "PORT_3", USBRC == -1 ? "USB3 Port3" : USBLabel, ISS_ON);
286  memset(USBLabel, 0, MAXINDILABEL);
287  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[3].name, USBLabel,
288  MAXINDILABEL);
289  IUFillSwitch(&USBControlV2S[3], "PORT_4", USBRC == -1 ? "USB3 Port4" : USBLabel, ISS_ON);
290  memset(USBLabel, 0, MAXINDILABEL);
291  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[4].name, USBLabel,
292  MAXINDILABEL);
293  IUFillSwitch(&USBControlV2S[4], "PORT_5", USBRC == -1 ? "USB2 Port5" : USBLabel, ISS_ON);
294  memset(USBLabel, 0, MAXINDILABEL);
295  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[5].name, USBLabel,
296  MAXINDILABEL);
297  IUFillSwitch(&USBControlV2S[5], "PORT_6", USBRC == -1 ? "USB2 Port6" : USBLabel, ISS_ON);
298 
299  IUFillSwitchVector(&USBControlV2SP, USBControlV2S, 6, getDeviceName(), "USB_PORT_CONTROL", "Ports", USB_TAB, IP_RW,
300  ISR_NOFMANY, 60, IPS_IDLE);
301 
302  // USB Labels update with custom values
303  IUFillText(&USBControlsLabelsT[0], "USB_LABEL_1", "USB3 Port1", USBControlV2S[0].label);
304  IUFillText(&USBControlsLabelsT[1], "USB_LABEL_2", "USB3 Port2", USBControlV2S[1].label);
305  IUFillText(&USBControlsLabelsT[2], "USB_LABEL_3", "USB3 Port3", USBControlV2S[2].label);
306  IUFillText(&USBControlsLabelsT[3], "USB_LABEL_4", "USB3 Port4", USBControlV2S[3].label);
307  IUFillText(&USBControlsLabelsT[4], "USB_LABEL_5", "USB2 Port5", USBControlV2S[4].label);
308  IUFillText(&USBControlsLabelsT[5], "USB_LABEL_6", "USB2 Port6", USBControlV2S[5].label);
309 
310  IUFillTextVector(&USBControlsLabelsTP, USBControlsLabelsT, 6, getDeviceName(), "USB_CONTROL_LABEL", "USB Labels",
311  USB_TAB, IP_WO, 60, IPS_IDLE);
312  // USB Hub Status
313  IUFillLight(&USBStatusL[0], "PORT_1", USBControlV2S[0].label, IPS_OK);
314  IUFillLight(&USBStatusL[1], "PORT_2", USBControlV2S[1].label, IPS_OK);
315  IUFillLight(&USBStatusL[2], "PORT_3", USBControlV2S[2].label, IPS_OK);
316  IUFillLight(&USBStatusL[3], "PORT_4", USBControlV2S[3].label, IPS_OK);
317  IUFillLight(&USBStatusL[4], "PORT_5", USBControlV2S[4].label, IPS_OK);
318  IUFillLight(&USBStatusL[5], "PORT_6", USBControlV2S[5].label, IPS_OK);
319  IUFillLightVector(&USBStatusLP, USBStatusL, 6, getDeviceName(), "USB_PORT_STATUS", "Status", USB_TAB, IPS_IDLE);
320 
324 
325  // Settings
326  // IUFillNumber(&FocusBacklashN[0], "SETTING_BACKLASH", "Backlash (steps)", "%.f", 0, 999, 100, 0);
327  IUFillNumber(&FocuserSettingsN[SETTING_MAX_SPEED], "SETTING_MAX_SPEED", "Max Speed (%)", "%.f", 0, 900, 100, 400);
328  IUFillNumberVector(&FocuserSettingsNP, FocuserSettingsN, 1, getDeviceName(), "FOCUSER_SETTINGS", "Settings", FOCUS_TAB,
329  IP_RW, 60, IPS_IDLE);
333  IUFillText(&FirmwareT[FIRMWARE_VERSION], "VERSION", "Version", "NA");
334  IUFillText(&FirmwareT[FIRMWARE_UPTIME], "UPTIME", "Uptime (h)", "NA");
335  IUFillTextVector(&FirmwareTP, FirmwareT, 2, getDeviceName(), "FIRMWARE_INFO", "Firmware", FIRMWARE_TAB, IP_RO, 60,
336  IPS_IDLE);
340  addParameter("WEATHER_TEMPERATURE", "Temperature (C)", -15, 35, 15);
341  addParameter("WEATHER_HUMIDITY", "Humidity %", 0, 100, 15);
342  addParameter("WEATHER_DEWPOINT", "Dew Point (C)", 0, 100, 15);
343  setCriticalParameter("WEATHER_TEMPERATURE");
344 
348  serialConnection = new Connection::Serial(this);
349  serialConnection->registerHandshake([&]()
350  {
351  return Handshake();
352  });
353  registerConnection(serialConnection);
354 
355  return true;
356 }
357 
359 {
361 
362  if (isConnected())
363  {
364  // Setup Parameters
365  setupParams();
366 
367  // Main Control
368  defineProperty(&PowerCycleAllSP);
369  defineProperty(&PowerSensorsNP);
370  defineProperty(&PowerConsumptionNP);
371  defineProperty(&RebootSP);
372 
373  // Power
374  defineProperty(&PowerControlSP);
375  defineProperty(&PowerControlsLabelsTP);
376  defineProperty(&PowerCurrentNP);
377  defineProperty(&PowerOnBootSP);
378  OverCurrentLP.nlp = (version == UPB_V1) ? 4 : 7;
379  defineProperty(&OverCurrentLP);
380  if (version == UPB_V1)
381  defineProperty(&PowerLEDSP);
382  if (version == UPB_V2)
383  defineProperty(&AdjustableOutputNP);
384 
385  // Dew
386  if (version == UPB_V1)
387  defineProperty(&AutoDewSP);
388  else
389  defineProperty(&AutoDewV2SP);
390 
391  DewControlsLabelsTP.ntp = (version == UPB_V1) ? 2 : 3;
392  defineProperty(&DewControlsLabelsTP);
393 
394  if (version == UPB_V2)
395  defineProperty(&AutoDewAggNP);
396 
397  DewPWMNP.nnp = (version == UPB_V1) ? 2 : 3;
398  defineProperty(&DewPWMNP);
399 
400  DewCurrentDrawNP.nnp = (version == UPB_V1) ? 2 : 3;
401  defineProperty(&DewCurrentDrawNP);
402 
403  // USB
404  defineProperty(&USBControlSP);
405  if (version == UPB_V2)
406  defineProperty(&USBControlV2SP);
407  if (version == UPB_V1)
408  defineProperty(&USBStatusLP);
409  defineProperty(&USBControlsLabelsTP);
410 
411  // Focuser
413  defineProperty(&FocuserSettingsNP);
414 
416 
417  // Firmware
418  defineProperty(&FirmwareTP);
419 
420  setupComplete = true;
421  }
422  else
423  {
424  // Main Control
425  deleteProperty(PowerCycleAllSP.name);
426  deleteProperty(PowerSensorsNP.name);
427  deleteProperty(PowerConsumptionNP.name);
428  deleteProperty(RebootSP.name);
429 
430  // Power
431  deleteProperty(PowerControlSP.name);
432  deleteProperty(PowerControlsLabelsTP.name);
433  deleteProperty(PowerCurrentNP.name);
434  deleteProperty(PowerOnBootSP.name);
435  deleteProperty(OverCurrentLP.name);
436  if (version == UPB_V1)
437  deleteProperty(PowerLEDSP.name);
438  if (version == UPB_V2)
439  deleteProperty(AdjustableOutputNP.name);
440 
441  // Dew
442  if (version == UPB_V1)
443  deleteProperty(AutoDewSP.name);
444  else
445  {
446  deleteProperty(AutoDewV2SP.name);
447  deleteProperty(DewControlsLabelsTP.name);
448  deleteProperty(AutoDewAggNP.name);
449  }
450 
451  deleteProperty(DewPWMNP.name);
452  deleteProperty(DewCurrentDrawNP.name);
453 
454  // USB
455  deleteProperty(USBControlSP.name);
456  if (version == UPB_V2)
457  deleteProperty(USBControlV2SP.name);
458  if (version == UPB_V1)
459  deleteProperty(USBStatusLP.name);
460  deleteProperty(USBControlsLabelsTP.name);
461 
462  // Focuser
464  deleteProperty(FocuserSettingsNP.name);
465 
467 
468  deleteProperty(FirmwareTP.name);
469 
470  setupComplete = false;
471  }
472 
473  return true;
474 }
475 
477 {
478  return "Pegasus UPB";
479 }
480 
484 bool PegasusUPB::Handshake()
485 {
486  char response[PEGASUS_LEN] = {0};
487  int nbytes_read = 0;
488  PortFD = serialConnection->getPortFD();
489 
490  LOG_DEBUG("CMD <P#>");
491 
492  if (isSimulation())
493  {
494  snprintf(response, PEGASUS_LEN, "UPB2_OK");
495  nbytes_read = 8;
496  }
497  else
498  {
499  int tty_rc = 0, nbytes_written = 0;
500  char command[PEGASUS_LEN] = {0};
501  tcflush(PortFD, TCIOFLUSH);
502  strncpy(command, "P#\n", PEGASUS_LEN);
503  if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
504  {
505  char errorMessage[MAXRBUF];
506  tty_error_msg(tty_rc, errorMessage, MAXRBUF);
507  LOGF_ERROR("Serial write error: %s", errorMessage);
508  return false;
509  }
510 
511  // Try first with stopChar as the stop character
512  if ( (tty_rc = tty_nread_section(PortFD, response, PEGASUS_LEN, stopChar, 1, &nbytes_read)) != TTY_OK)
513  {
514  // Try 0xA as the stop character
515  if (tty_rc == TTY_OVERFLOW || tty_rc == TTY_TIME_OUT)
516  {
517  tcflush(PortFD, TCIOFLUSH);
518  tty_write_string(PortFD, command, &nbytes_written);
519  stopChar = 0xA;
520  tty_rc = tty_nread_section(PortFD, response, PEGASUS_LEN, stopChar, 1, &nbytes_read);
521  }
522 
523  if (tty_rc != TTY_OK)
524  {
525  char errorMessage[MAXRBUF];
526  tty_error_msg(tty_rc, errorMessage, MAXRBUF);
527  LOGF_ERROR("Serial read error: %s", errorMessage);
528  return false;
529  }
530  }
531 
532  cleanupResponse(response);
533  tcflush(PortFD, TCIOFLUSH);
534  }
535 
536 
537  LOGF_DEBUG("RES <%s>", response);
538 
539  setupComplete = false;
540 
541  version = strstr(response, "UPB2_OK") ? UPB_V2 : UPB_V1;
542 
543  return true;
544 }
545 
549 bool PegasusUPB::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
550 {
551  if (dev && !strcmp(dev, getDeviceName()))
552  {
553  // Cycle all power on or off
554  if (!strcmp(name, PowerCycleAllSP.name))
555  {
556  IUUpdateSwitch(&PowerCycleAllSP, states, names, n);
557 
558  PowerCycleAllSP.s = IPS_ALERT;
559  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
560  snprintf(cmd, PEGASUS_LEN, "PZ:%d", IUFindOnSwitchIndex(&PowerCycleAllSP));
561  if (sendCommand(cmd, res))
562  {
563  PowerCycleAllSP.s = !strcmp(cmd, res) ? IPS_OK : IPS_ALERT;
564  }
565 
566  IUResetSwitch(&PowerCycleAllSP);
567  IDSetSwitch(&PowerCycleAllSP, nullptr);
568  return true;
569  }
570 
571  // Reboot
572  if (!strcmp(name, RebootSP.name))
573  {
574  RebootSP.s = reboot() ? IPS_OK : IPS_ALERT;
575  IDSetSwitch(&RebootSP, nullptr);
576  LOG_INFO("Rebooting device...");
577  return true;
578  }
579 
580  // Control Power per port
581  if (!strcmp(name, PowerControlSP.name))
582  {
583  bool failed = false;
584  for (int i = 0; i < n; i++)
585  {
586  if (!strcmp(names[i], PowerControlS[i].name) && states[i] != PowerControlS[i].s)
587  {
588  if (setPowerEnabled(i + 1, states[i] == ISS_ON) == false)
589  {
590  failed = true;
591  break;
592  }
593  }
594  }
595 
596  if (failed)
597  PowerControlSP.s = IPS_ALERT;
598  else
599  {
600  PowerControlSP.s = IPS_OK;
601  IUUpdateSwitch(&PowerControlSP, states, names, n);
602  }
603 
604  IDSetSwitch(&PowerControlSP, nullptr);
605  return true;
606  }
607 
608  // Power on boot
609  if (!strcmp(name, PowerOnBootSP.name))
610  {
611  IUUpdateSwitch(&PowerOnBootSP, states, names, n);
612  PowerOnBootSP.s = setPowerOnBoot() ? IPS_OK : IPS_ALERT;
613  IDSetSwitch(&PowerOnBootSP, nullptr);
614  saveConfig(true, PowerOnBootSP.name);
615  return true;
616  }
617 
618  // Auto Dew v1.
619  if ((!strcmp(name, AutoDewSP.name)) && (version == UPB_V1))
620  {
621  int prevIndex = IUFindOnSwitchIndex(&AutoDewSP);
622  IUUpdateSwitch(&AutoDewSP, states, names, n);
623  if (setAutoDewEnabled(AutoDewS[INDI_ENABLED].s == ISS_ON))
624  {
625  AutoDewSP.s = IPS_OK;
626  }
627  else
628  {
629  IUResetSwitch(&AutoDewSP);
630  AutoDewS[prevIndex].s = ISS_ON;
631  AutoDewSP.s = IPS_ALERT;
632  }
633 
634  IDSetSwitch(&AutoDewSP, nullptr);
635  return true;
636  }
637 
638  // Auto Dew v2.
639  if ((!strcmp(name, AutoDewV2SP.name)) && (version == UPB_V2))
640  {
641  ISState Dew1 = AutoDewV2S[DEW_PWM_A].s;
642  ISState Dew2 = AutoDewV2S[DEW_PWM_B].s;
643  ISState Dew3 = AutoDewV2S[DEW_PWM_C].s;
644  IUUpdateSwitch(&AutoDewV2SP, states, names, n);
645  if (toggleAutoDewV2())
646  {
647  Dew1 = AutoDewV2S[DEW_PWM_A].s;
648  Dew2 = AutoDewV2S[DEW_PWM_B].s;
649  Dew3 = AutoDewV2S[DEW_PWM_C].s;
650  if (Dew1 == ISS_OFF && Dew2 == ISS_OFF && Dew3 == ISS_OFF)
651  AutoDewV2SP.s = IPS_IDLE;
652  else
653  AutoDewV2SP.s = IPS_OK;
654  }
655  else
656  {
657  IUResetSwitch(&AutoDewV2SP);
658  AutoDewV2S[DEW_PWM_A].s = Dew1;
659  AutoDewV2S[DEW_PWM_B].s = Dew2;
660  AutoDewV2S[DEW_PWM_C].s = Dew3;
661  AutoDewV2SP.s = IPS_ALERT;
662  }
663 
664  IDSetSwitch(&AutoDewV2SP, nullptr);
665  return true;
666  }
667 
668  // USB Hub Control v1
669  if (!strcmp(name, USBControlSP.name))
670  {
671  int prevIndex = IUFindOnSwitchIndex(&USBControlSP);
672  IUUpdateSwitch(&USBControlSP, states, names, n);
673  if (setUSBHubEnabled(USBControlS[0].s == ISS_ON))
674  {
675  USBControlSP.s = IPS_OK;
676  }
677  else
678  {
679  IUResetSwitch(&USBControlSP);
680  USBControlS[prevIndex].s = ISS_ON;
681  USBControlSP.s = IPS_ALERT;
682  }
683 
684  IDSetSwitch(&USBControlSP, nullptr);
685  return true;
686  }
687 
688  // USB Hub Control v2
689  if (!strcmp(name, USBControlV2SP.name))
690  {
691  bool rc[6] = {false};
692  std::fill_n(rc, 6, true);
693  ISState ports[6] = {ISS_ON};
694 
695  for (int i = 0; i < USBControlV2SP.nsp; i++)
696  ports[i] = USBControlV2S[i].s;
697 
698  IUUpdateSwitch(&USBControlV2SP, states, names, n);
699  for (int i = 0; i < USBControlV2SP.nsp; i++)
700  {
701  if (ports[i] != USBControlV2S[i].s)
702  rc[i] = setUSBPortEnabled(i, USBControlV2S[i].s == ISS_ON);
703  }
704 
705  // All is OK
706  if (rc[0] && rc[1] && rc[2] && rc[3] && rc[4] && rc[5])
707  {
708  USBControlSP.s = IPS_OK;
709  }
710  else
711  {
712  IUResetSwitch(&USBControlV2SP);
713  for (int i = 0; i < USBControlV2SP.nsp; i++)
714  USBControlV2S[i].s = ports[i];
715  USBControlV2SP.s = IPS_ALERT;
716  }
717 
718  IDSetSwitch(&USBControlV2SP, nullptr);
719 
720  return true;
721  }
722 
723  // Focuser backlash
724  // if (!strcmp(name, FocusBacklashSP.name))
725  // {
726  // int prevIndex = IUFindOnSwitchIndex(&FocusBacklashSP);
727  // IUUpdateSwitch(&FocusBacklashSP, states, names, n);
728  // if (setFocuserBacklashEnabled(FocusBacklashS[0].s == ISS_ON))
729  // {
730  // FocusBacklashSP.s = IPS_OK;
731  // }
732  // else
733  // {
734  // IUResetSwitch(&FocusBacklashSP);
735  // FocusBacklashS[prevIndex].s = ISS_ON;
736  // FocusBacklashSP.s = IPS_ALERT;
737  // }
738 
739  // IDSetSwitch(&FocusBacklashSP, nullptr);
740  // return true;
741  // }
742 
743  // Power LED
744  if (!strcmp(name, PowerLEDSP.name) && (version == UPB_V1))
745  {
746  int prevIndex = IUFindOnSwitchIndex(&PowerLEDSP);
747  IUUpdateSwitch(&PowerLEDSP, states, names, n);
748  if (setPowerLEDEnabled(PowerLEDS[0].s == ISS_ON))
749  {
750  PowerLEDSP.s = IPS_OK;
751  }
752  else
753  {
754  IUResetSwitch(&PowerLEDSP);
755  PowerLEDS[prevIndex].s = ISS_ON;
756  PowerLEDSP.s = IPS_ALERT;
757  }
758 
759  IDSetSwitch(&PowerLEDSP, nullptr);
760  return true;
761  }
762 
763  if (strstr(name, "FOCUS"))
764  return FI::processSwitch(dev, name, states, names, n);
765  }
766 
767  return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
768 }
769 
773 bool PegasusUPB::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
774 {
775  if (dev && !strcmp(dev, getDeviceName()))
776  {
777  // Adjustable output
778  if (!strcmp(name, AdjustableOutputNP.name))
779  {
780  if (setAdjustableOutput(static_cast<uint8_t>(values[0])))
781  {
782  IUUpdateNumber(&AdjustableOutputNP, values, names, n);
783  AdjustableOutputNP.s = IPS_OK;
784  }
785  else
786  AdjustableOutputNP.s = IPS_ALERT;
787 
788  IDSetNumber(&AdjustableOutputNP, nullptr);
789  return true;
790  }
791 
792  // Dew PWM
793  if (!strcmp(name, DewPWMNP.name))
794  {
795  bool rc1 = false, rc2 = false, rc3 = false;
796  for (int i = 0; i < n; i++)
797  {
798  if (!strcmp(names[i], DewPWMN[DEW_PWM_A].name))
799  rc1 = setDewPWM(5, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
800  else if (!strcmp(names[i], DewPWMN[DEW_PWM_B].name))
801  rc2 = setDewPWM(6, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
802  else if (!strcmp(names[i], DewPWMN[DEW_PWM_C].name))
803  rc3 = setDewPWM(7, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
804  }
805 
806  DewPWMNP.s = (rc1 && rc2 && rc3) ? IPS_OK : IPS_ALERT;
807  if (DewPWMNP.s == IPS_OK)
808  IUUpdateNumber(&DewPWMNP, values, names, n);
809  IDSetNumber(&DewPWMNP, nullptr);
810  return true;
811  }
812 
813  // Auto Dew Aggressiveness
814  if (!strcmp(name, AutoDewAggNP.name))
815  {
816  if (setAutoDewAgg(values[0]))
817  {
818  AutoDewAggN[0].value = values[0];
819  AutoDewAggNP.s = IPS_OK;
820  }
821  else
822  {
823  AutoDewAggNP.s = IPS_ALERT;
824  }
825 
826  IDSetNumber(&AutoDewAggNP, nullptr);
827  return true;
828  }
829 
830  // Focuser Settings
831  if (!strcmp(name, FocuserSettingsNP.name))
832  {
833  if (setFocuserMaxSpeed(values[0]))
834  {
835  FocuserSettingsN[0].value = values[0];
836  FocuserSettingsNP.s = IPS_OK;
837  }
838  else
839  {
840  FocuserSettingsNP.s = IPS_ALERT;
841  }
842 
843  IDSetNumber(&FocuserSettingsNP, nullptr);
844  return true;
845  }
846 
847  if (strstr(name, "FOCUS_"))
848  return FI::processNumber(dev, name, values, names, n);
849 
850  if (strstr(name, "WEATHER_"))
851  return WI::processNumber(dev, name, values, names, n);
852  }
853  return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
854 }
855 
859 bool PegasusUPB::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
860 {
861  if (dev && !strcmp(dev, getDeviceName()))
862  {
863  // Power Labels
864  if (!strcmp(name, PowerControlsLabelsTP.name))
865  {
866  IUUpdateText(&PowerControlsLabelsTP, texts, names, n);
867  PowerControlsLabelsTP.s = IPS_OK;
868  LOG_INFO("Power port labels saved. Driver must be restarted for the labels to take effect.");
869  saveConfig();
870  IDSetText(&PowerControlsLabelsTP, nullptr);
871  return true;
872  }
873  // Dew Labels
874  if (!strcmp(name, DewControlsLabelsTP.name))
875  {
876  IUUpdateText(&DewControlsLabelsTP, texts, names, n);
877  DewControlsLabelsTP.s = IPS_OK;
878  LOG_INFO("Dew labels saved. Driver must be restarted for the labels to take effect.");
879  saveConfig();
880  IDSetText(&DewControlsLabelsTP, nullptr);
881  return true;
882  }
883  // USB Labels
884  if (!strcmp(name, USBControlsLabelsTP.name))
885  {
886  IUUpdateText(&USBControlsLabelsTP, texts, names, n);
887  USBControlsLabelsTP.s = IPS_OK;
888  LOG_INFO("USB labels saved. Driver must be restarted for the labels to take effect.");
889  saveConfig();
890  IDSetText(&USBControlsLabelsTP, nullptr);
891  return true;
892  }
893  }
894 
895  return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
896 }
897 
901 bool PegasusUPB::sendCommand(const char * cmd, char * res)
902 {
903  int nbytes_read = 0, nbytes_written = 0, tty_rc = 0;
904  LOGF_DEBUG("CMD <%s>", cmd);
905 
906  if (isSimulation())
907  {
908  if (!strcmp(cmd, "PS"))
909  {
910  strncpy(res, "PS:1111:12", PEGASUS_LEN);
911  }
912  else if (!strcmp(cmd, "PA"))
913  {
914  strncpy(res, "UPB2:12.0:0.9:10:24.8:37:9.1:1111:111111:153:153:0:0:0:0:0:70:0:0:0000000:0", PEGASUS_LEN);
915  }
916  else if (!strcmp(cmd, "PC"))
917  {
918  strncpy(res, "0.40:0.00:0.03:26969", PEGASUS_LEN);
919  }
920  else if (!strcmp(cmd, "SA"))
921  {
922  strncpy(res, "3000:0:0:10", PEGASUS_LEN);
923  }
924  else if (!strcmp(cmd, "SS"))
925  {
926  strncpy(res, "999", PEGASUS_LEN);
927  }
928  else if (!strcmp(cmd, "PD"))
929  {
930  strncpy(res, "210", PEGASUS_LEN);
931  }
932  else if (!strcmp(cmd, "PV"))
933  {
934  strncpy(res, "Sim v1.0", PEGASUS_LEN);
935  }
936  else if (res)
937  {
938  strncpy(res, cmd, PEGASUS_LEN);
939  }
940 
941  return true;
942  }
943 
944  for (int i = 0; i < 2; i++)
945  {
946  char command[PEGASUS_LEN] = {0};
947  tcflush(PortFD, TCIOFLUSH);
948  snprintf(command, PEGASUS_LEN, "%s\n", cmd);
949  if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
950  continue;
951 
952  if (!res)
953  {
954  tcflush(PortFD, TCIOFLUSH);
955  return true;
956  }
957 
958  if ( (tty_rc = tty_nread_section(PortFD, res, PEGASUS_LEN, stopChar, PEGASUS_TIMEOUT, &nbytes_read)) != TTY_OK
959  || nbytes_read == 1)
960  continue;
961 
962  tcflush(PortFD, TCIOFLUSH);
963 
964  cleanupResponse(res);
965  LOGF_DEBUG("RES <%s>", res);
966  return true;
967  }
968 
969  if (tty_rc != TTY_OK)
970  {
971  char errorMessage[MAXRBUF];
972  tty_error_msg(tty_rc, errorMessage, MAXRBUF);
973  LOGF_ERROR("Serial error: %s", errorMessage);
974  }
975 
976  return false;
977 }
978 
982 IPState PegasusUPB::MoveAbsFocuser(uint32_t targetTicks)
983 {
984  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
985  snprintf(cmd, PEGASUS_LEN, "SM:%u", targetTicks);
986  if (sendCommand(cmd, res))
987  {
988  return (!strcmp(res, cmd) ? IPS_BUSY : IPS_ALERT);
989  }
990 
991  return IPS_ALERT;
992 }
993 
998 {
999  return MoveAbsFocuser(dir == FOCUS_INWARD ? FocusAbsPosN[0].value - ticks : FocusAbsPosN[0].value + ticks);
1000 }
1001 
1006 {
1007  char res[PEGASUS_LEN] = {0};
1008  if (sendCommand("SH", res))
1009  {
1010  return (!strcmp(res, "SH"));
1011  }
1012 
1013  return false;
1014 }
1015 
1019 bool PegasusUPB::ReverseFocuser(bool enabled)
1020 {
1021  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1022  snprintf(cmd, PEGASUS_LEN, "SR:%d", enabled ? 1 : 0);
1023  if (sendCommand(cmd, res))
1024  {
1025  return (!strcmp(res, cmd));
1026  }
1027 
1028  return false;
1029 }
1030 
1034 bool PegasusUPB::SyncFocuser(uint32_t ticks)
1035 {
1036  char cmd[PEGASUS_LEN] = {0};
1037  snprintf(cmd, PEGASUS_LEN, "SC:%u", ticks);
1038  return sendCommand(cmd, nullptr);
1039 }
1040 
1045 {
1046  char cmd[PEGASUS_LEN] = {0};
1047  snprintf(cmd, PEGASUS_LEN, "SB:%d", steps);
1048  return sendCommand(cmd, nullptr);
1049 }
1050 
1054 bool PegasusUPB::setFocuserMaxSpeed(uint16_t maxSpeed)
1055 {
1056  char cmd[PEGASUS_LEN] = {0};
1057  snprintf(cmd, PEGASUS_LEN, "SS:%d", maxSpeed);
1058  return sendCommand(cmd, nullptr);
1059 }
1060 
1065 {
1066  char cmd[PEGASUS_LEN] = {0};
1067  snprintf(cmd, PEGASUS_LEN, "SB:%d", enabled ? 1 : 0);
1068  return sendCommand(cmd, nullptr);
1069 }
1070 
1074 bool PegasusUPB::setPowerEnabled(uint8_t port, bool enabled)
1075 {
1076  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1077  snprintf(cmd, PEGASUS_LEN, "P%d:%d", port, enabled ? 1 : 0);
1078  if (sendCommand(cmd, res))
1079  {
1080  return (!strcmp(res, cmd));
1081  }
1082 
1083  return false;
1084 }
1085 
1089 bool PegasusUPB::setPowerLEDEnabled(bool enabled)
1090 {
1091  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1092  snprintf(cmd, PEGASUS_LEN, "PL:%d", enabled ? 1 : 0);
1093  if (sendCommand(cmd, res))
1094  {
1095  return (!strcmp(res, cmd));
1096  }
1097 
1098  return false;
1099 }
1100 
1104 bool PegasusUPB::setAutoDewEnabled(bool enabled)
1105 {
1106  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1107  snprintf(cmd, PEGASUS_LEN, "PD:%d", enabled ? 1 : 0);
1108  if (sendCommand(cmd, res))
1109  {
1110  return (!strcmp(res, cmd));
1111  }
1112 
1113  return false;
1114 }
1115 
1119 bool PegasusUPB::setAutoDewAgg(uint8_t value)
1120 {
1121  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0};
1122  snprintf(cmd, PEGASUS_LEN, "PD:%03d", value);
1123  snprintf(expected, PEGASUS_LEN, "PD:%d", value);
1124  if (sendCommand(cmd, res))
1125  {
1126  return (!strcmp(res, expected));
1127  }
1128 
1129  return false;
1130 }
1134 bool PegasusUPB::setAdjustableOutput(uint8_t voltage)
1135 {
1136  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1137  snprintf(cmd, PEGASUS_LEN, "P8:%d", voltage);
1138  if (sendCommand(cmd, res))
1139  {
1140  return (!strcmp(res, cmd));
1141  }
1142 
1143  return false;
1144 }
1145 
1149 bool PegasusUPB::setPowerOnBoot()
1150 {
1151  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1152  snprintf(cmd, PEGASUS_LEN, "PE:%d%d%d%d", PowerOnBootS[0].s == ISS_ON ? 1 : 0,
1153  PowerOnBootS[1].s == ISS_ON ? 1 : 0,
1154  PowerOnBootS[2].s == ISS_ON ? 1 : 0,
1155  PowerOnBootS[3].s == ISS_ON ? 1 : 0);
1156  if (sendCommand(cmd, res))
1157  {
1158  return (!strcmp(res, "PE:1"));
1159  }
1160 
1161  return false;
1162 }
1163 
1167 bool PegasusUPB::getPowerOnBoot()
1168 {
1169  char res[PEGASUS_LEN] = {0};
1170  if (sendCommand("PS", res))
1171  {
1172  std::vector<std::string> result = split(res, ":");
1173  if (result.size() != 3)
1174  {
1175  LOGF_WARN("Received wrong number (%i) of power on boot data (%s). Retrying...", result.size(), res);
1176  return false;
1177  }
1178 
1179  const char *status = result[1].c_str();
1180  PowerOnBootS[0].s = (status[0] == '1') ? ISS_ON : ISS_OFF;
1181  PowerOnBootS[1].s = (status[1] == '1') ? ISS_ON : ISS_OFF;
1182  PowerOnBootS[2].s = (status[2] == '1') ? ISS_ON : ISS_OFF;
1183  PowerOnBootS[3].s = (status[3] == '1') ? ISS_ON : ISS_OFF;
1184 
1185  AdjustableOutputN[0].value = std::stod(result[2]);
1186  AdjustableOutputNP.s = IPS_OK;
1187 
1188  return true;
1189  }
1190 
1191  return false;
1192 }
1193 
1197 bool PegasusUPB::setDewPWM(uint8_t id, uint8_t value)
1198 {
1199  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0};
1200  snprintf(cmd, PEGASUS_LEN, "P%d:%03d", id, value);
1201  snprintf(expected, PEGASUS_LEN, "P%d:%d", id, value);
1202  if (sendCommand(cmd, res))
1203  {
1204  return (!strcmp(res, expected));
1205  }
1206 
1207  return false;
1208 }
1209 
1213 bool PegasusUPB::setUSBHubEnabled(bool enabled)
1214 {
1215  char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1216  snprintf(cmd, PEGASUS_LEN, "PU:%d", enabled ? 1 : 0);
1217  snprintf(expected, PEGASUS_LEN, "PU:%d", enabled ? 0 : 1);
1218  if (sendCommand(cmd, res))
1219  {
1220  return (!strcmp(res, expected));
1221  }
1222 
1223  return false;
1224 }
1225 
1229 bool PegasusUPB::setUSBPortEnabled(uint8_t port, bool enabled)
1230 {
1231  char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1232  snprintf(cmd, PEGASUS_LEN, "U%d:%d", port + 1, enabled ? 1 : 0);
1233  snprintf(expected, PEGASUS_LEN, "U%d:%d", port + 1, enabled ? 1 : 0);
1234  if (sendCommand(cmd, res))
1235  {
1236  return (!strcmp(res, expected));
1237  }
1238 
1239  return false;
1240 }
1241 
1245 bool PegasusUPB::toggleAutoDewV2()
1246 {
1247  char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1248 
1249  uint8_t value = 0;
1250 
1251  if (IUFindOnSwitchIndex(&AutoDewV2SP) == -1)
1252  value = 0;
1253  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_B].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1254  value = 1;
1255  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_B].s == ISS_ON)
1256  value = 5;
1257  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1258  value = 6;
1259  else if (AutoDewV2S[DEW_PWM_B].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1260  value = 7;
1261  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON)
1262  value = 2;
1263  else if (AutoDewV2S[DEW_PWM_B].s == ISS_ON)
1264  value = 3;
1265  else if (AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1266  value = 4;
1267 
1268  snprintf(cmd, PEGASUS_LEN, "PD:%d", value);
1269  snprintf(expected, PEGASUS_LEN, "PD:%d", value);
1270  if (sendCommand(cmd, res))
1271  {
1272  return (!strcmp(res, expected));
1273  }
1274 
1275  return false;
1276 }
1277 
1282 {
1283  // Save CCD Config
1285  FI::saveConfigItems(fp);
1286  WI::saveConfigItems(fp);
1287 
1288  IUSaveConfigSwitch(fp, &PowerLEDSP);
1289  IUSaveConfigSwitch(fp, &AutoDewSP);
1290  if (version == UPB_V2)
1291  IUSaveConfigNumber(fp, &AutoDewAggNP);
1292  IUSaveConfigNumber(fp, &FocuserSettingsNP);
1293  IUSaveConfigText(fp, &PowerControlsLabelsTP);
1294  IUSaveConfigText(fp, &DewControlsLabelsTP);
1295  IUSaveConfigText(fp, &USBControlsLabelsTP);
1296  return true;
1297 }
1298 
1303 {
1304  if (!isConnected() || setupComplete == false)
1305  {
1307  return;
1308  }
1309 
1310  if (getSensorData())
1311  {
1312  getPowerData();
1313  getStepperData();
1314 
1315  if (version == UPB_V2)
1316  getDewAggData();
1317  }
1318 
1320 }
1321 
1325 bool PegasusUPB::sendFirmware()
1326 {
1327  char res[PEGASUS_LEN] = {0};
1328  if (sendCommand("PV", res))
1329  {
1330  LOGF_INFO("Detected firmware %s", res);
1331  IUSaveText(&FirmwareT[FIRMWARE_VERSION], res);
1332  IDSetText(&FirmwareTP, nullptr);
1333  return true;
1334  }
1335 
1336  return false;
1337 }
1338 
1342 bool PegasusUPB::sensorUpdated(const std::vector<std::string> &result, uint8_t start, uint8_t end)
1343 {
1344  if (lastSensorData.empty())
1345  return true;
1346 
1347  for (uint8_t index = start; index <= end; index++)
1348  {
1349  if (index >= lastSensorData.size() or result[index] != lastSensorData[index])
1350  return true;
1351  }
1352 
1353  return false;
1354 }
1355 
1359 bool PegasusUPB::stepperUpdated(const std::vector<std::string> &result, u_int8_t index)
1360 {
1361  if (lastStepperData.empty())
1362  return true;
1363 
1364  return index >= lastStepperData.size() or result[index] != lastSensorData[index];
1365 }
1366 
1370 bool PegasusUPB::getSensorData()
1371 {
1372  char res[PEGASUS_LEN] = {0};
1373  if (sendCommand("PA", res))
1374  {
1375  std::vector<std::string> result = split(res, ":");
1376  if ( (version == UPB_V1 && result.size() != 19) ||
1377  (version == UPB_V2 && result.size() != 21))
1378  {
1379  LOGF_WARN("Received wrong number (%i) of detailed sensor data (%s). Retrying...", result.size(), res);
1380  return false;
1381  }
1382 
1383  if (result == lastSensorData)
1384  return true;
1385 
1386  // Power Sensors
1387  PowerSensorsN[SENSOR_VOLTAGE].value = std::stod(result[1]);
1388  PowerSensorsN[SENSOR_CURRENT].value = std::stod(result[2]);
1389  PowerSensorsN[SENSOR_POWER].value = std::stod(result[3]);
1390  PowerSensorsNP.s = IPS_OK;
1391  //if (lastSensorData[0] != result[0] || lastSensorData[1] != result[1] || lastSensorData[2] != result[2])
1392  if (sensorUpdated(result, 0, 2))
1393  IDSetNumber(&PowerSensorsNP, nullptr);
1394 
1395  // Environment Sensors
1396  setParameterValue("WEATHER_TEMPERATURE", std::stod(result[4]));
1397  setParameterValue("WEATHER_HUMIDITY", std::stod(result[5]));
1398  setParameterValue("WEATHER_DEWPOINT", std::stod(result[6]));
1399  //if (lastSensorData[4] != result[4] || lastSensorData[5] != result[5] || lastSensorData[6] != result[6])
1400  if (sensorUpdated(result, 4, 6))
1401  {
1403  IDSetLight(&critialParametersLP, nullptr);
1404  ParametersNP.s = IPS_OK;
1405  IDSetNumber(&ParametersNP, nullptr);
1406  }
1407 
1408  // Port Status
1409  const char * portStatus = result[7].c_str();
1410  PowerControlS[0].s = (portStatus[0] == '1') ? ISS_ON : ISS_OFF;
1411  PowerControlS[1].s = (portStatus[1] == '1') ? ISS_ON : ISS_OFF;
1412  PowerControlS[2].s = (portStatus[2] == '1') ? ISS_ON : ISS_OFF;
1413  PowerControlS[3].s = (portStatus[3] == '1') ? ISS_ON : ISS_OFF;
1414  //if (lastSensorData[7] != result[7])
1415  if (sensorUpdated(result, 7, 7))
1416  IDSetSwitch(&PowerControlSP, nullptr);
1417 
1418  // Hub Status
1419  const char * usb_status = result[8].c_str();
1420  if (version == UPB_V1)
1421  {
1422  USBControlS[0].s = (usb_status[0] == '0') ? ISS_ON : ISS_OFF;
1423  USBControlS[1].s = (usb_status[0] == '0') ? ISS_OFF : ISS_ON;
1424  USBStatusL[0].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1425  USBStatusL[1].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1426  USBStatusL[2].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1427  USBStatusL[3].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1428  USBStatusL[4].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1429  //if (lastSensorData[8] != result[8])
1430  if (sensorUpdated(result, 8, 8))
1431  {
1432  USBControlSP.s = (IUFindOnSwitchIndex(&USBControlSP) == 0) ? IPS_OK : IPS_IDLE;
1433  IDSetSwitch(&USBControlSP, nullptr);
1434  IDSetLight(&USBStatusLP, nullptr);
1435  }
1436  }
1437  else
1438  {
1439  USBControlV2S[0].s = (usb_status[0] == '1') ? ISS_ON : ISS_OFF;
1440  USBControlV2S[1].s = (usb_status[1] == '1') ? ISS_ON : ISS_OFF;
1441  USBControlV2S[2].s = (usb_status[2] == '1') ? ISS_ON : ISS_OFF;
1442  USBControlV2S[3].s = (usb_status[3] == '1') ? ISS_ON : ISS_OFF;
1443  USBControlV2S[4].s = (usb_status[4] == '1') ? ISS_ON : ISS_OFF;
1444  USBControlV2S[5].s = (usb_status[5] == '1') ? ISS_ON : ISS_OFF;
1445  USBControlV2SP.s = IPS_OK;
1446  //if (lastSensorData[8] != result[8])
1447  if (sensorUpdated(result, 8, 8))
1448  {
1449  IDSetSwitch(&USBControlV2SP, nullptr);
1450  }
1451  }
1452 
1453  // From here, we get differences between v1 and v2 readings
1454  int index = 9;
1455  // Dew PWM
1456  DewPWMN[DEW_PWM_A].value = std::stod(result[index]) / 255.0 * 100.0;
1457  DewPWMN[DEW_PWM_B].value = std::stod(result[index + 1]) / 255.0 * 100.0;
1458  if (version == UPB_V2)
1459  DewPWMN[DEW_PWM_C].value = std::stod(result[index + 2]) / 255.0 * 100.0;
1460  // if (lastSensorData[index] != result[index] ||
1461  // lastSensorData[index + 1] != result[index + 1] ||
1462  // (version == UPB_V2 && lastSensorData[index +2] != result[index + 2]))
1463  if (sensorUpdated(result, index, version == UPB_V1 ? index + 1 : index + 2))
1464  IDSetNumber(&DewPWMNP, nullptr);
1465 
1466  index = (version == UPB_V1) ? 11 : 12;
1467 
1468  const double ampDivision = (version == UPB_V1) ? 400.0 : 480.0;
1469 
1470  // Current draw
1471  PowerCurrentN[0].value = std::stod(result[index]) / ampDivision;
1472  PowerCurrentN[1].value = std::stod(result[index + 1]) / ampDivision;
1473  PowerCurrentN[2].value = std::stod(result[index + 2]) / ampDivision;
1474  PowerCurrentN[3].value = std::stod(result[index + 3]) / ampDivision;
1475  // if (lastSensorData[index] != result[index] ||
1476  // lastSensorData[index + 1] != result[index + 1] ||
1477  // lastSensorData[index + 2] != result[index + 2] ||
1478  // lastSensorData[index + 3] != result[index + 3])
1479  if (sensorUpdated(result, index, index + 3))
1480  IDSetNumber(&PowerCurrentNP, nullptr);
1481 
1482  index = (version == UPB_V1) ? 15 : 16;
1483 
1484  DewCurrentDrawN[DEW_PWM_A].value = std::stod(result[index]) / ampDivision;
1485  DewCurrentDrawN[DEW_PWM_B].value = std::stod(result[index + 1]) / ampDivision;
1486  if (version == UPB_V2)
1487  DewCurrentDrawN[DEW_PWM_C].value = std::stod(result[index + 2]) / 700;
1488  // if (lastSensorData[index] != result[index] ||
1489  // lastSensorData[index + 1] != result[index + 1] ||
1490  // (version == UPB_V2 && lastSensorData[index + 2] != result[index + 2]))
1491  if (sensorUpdated(result, index, version == UPB_V1 ? index + 1 : index + 2))
1492  IDSetNumber(&DewCurrentDrawNP, nullptr);
1493 
1494  index = (version == UPB_V1) ? 17 : 19;
1495 
1496  // Over Current
1497  //if (lastSensorData[index] != result[index])
1498  if (sensorUpdated(result, index, index))
1499  {
1500  const char * over_curent = result[index].c_str();
1501  OverCurrentL[0].s = (over_curent[0] == '0') ? IPS_OK : IPS_ALERT;
1502  OverCurrentL[1].s = (over_curent[1] == '0') ? IPS_OK : IPS_ALERT;
1503  OverCurrentL[2].s = (over_curent[2] == '0') ? IPS_OK : IPS_ALERT;
1504  OverCurrentL[3].s = (over_curent[3] == '0') ? IPS_OK : IPS_ALERT;
1505  if (version == UPB_V2)
1506  {
1507  OverCurrentL[4].s = (over_curent[4] == '0') ? IPS_OK : IPS_ALERT;
1508  OverCurrentL[5].s = (over_curent[5] == '0') ? IPS_OK : IPS_ALERT;
1509  OverCurrentL[6].s = (over_curent[6] == '0') ? IPS_OK : IPS_ALERT;
1510  }
1511 
1512  IDSetLight(&OverCurrentLP, nullptr);
1513  }
1514 
1515  index = (version == UPB_V1) ? 18 : 20;
1516 
1517  // Auto Dew
1518  if (version == UPB_V1)
1519  {
1520  //if (lastSensorData[index] != result[index])
1521  if (sensorUpdated(result, index, index))
1522  {
1523  AutoDewS[INDI_ENABLED].s = (std::stoi(result[index]) == 1) ? ISS_ON : ISS_OFF;
1524  AutoDewS[INDI_DISABLED].s = (std::stoi(result[index]) == 1) ? ISS_OFF : ISS_ON;
1525  IDSetSwitch(&AutoDewSP, nullptr);
1526  }
1527  }
1528  else
1529  {
1530  //if (lastSensorData[index] != result[index])
1531  if (sensorUpdated(result, index, index))
1532  {
1533  int value = std::stoi(result[index]);
1534  IUResetSwitch(&AutoDewV2SP);
1535  switch (value)
1536  {
1537  case 1:
1538  AutoDewV2S[DEW_PWM_A].s = AutoDewV2S[DEW_PWM_B].s = AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1539  break;
1540 
1541  case 2:
1542  AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1543  break;
1544 
1545  case 3:
1546  AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1547  break;
1548 
1549  case 4:
1550  AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1551  break;
1552 
1553  case 5:
1554  AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1555  AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1556  break;
1557 
1558  case 6:
1559  AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1560  AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1561  break;
1562 
1563  case 7:
1564  AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1565  AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1566  break;
1567  default:
1568  break;
1569  }
1570  IDSetSwitch(&AutoDewV2SP, nullptr);
1571  }
1572  }
1573 
1574  lastSensorData = result;
1575  return true;
1576  }
1577 
1578  return false;
1579 }
1580 
1584 bool PegasusUPB::getPowerData()
1585 {
1586  char res[PEGASUS_LEN] = {0};
1587  if (sendCommand("PC", res))
1588  {
1589  std::vector<std::string> result = split(res, ":");
1590  if (result.size() != 4)
1591  {
1592  LOGF_WARN("Received wrong number (%i) of power sensor data (%s). Retrying...", result.size(), res);
1593  return false;
1594  }
1595 
1596  if (result == lastPowerData)
1597  return true;
1598 
1599  PowerConsumptionN[CONSUMPTION_AVG_AMPS].value = std::stod(result[0]);
1600  PowerConsumptionN[CONSUMPTION_AMP_HOURS].value = std::stod(result[1]);
1601  PowerConsumptionN[CONSUMPTION_WATT_HOURS].value = std::stod(result[2]);
1602  PowerConsumptionNP.s = IPS_OK;
1603  IDSetNumber(&PowerConsumptionNP, nullptr);
1604 
1605  try
1606  {
1607  std::chrono::milliseconds uptime(std::stol(result[3]));
1608  using dhours = std::chrono::duration<double, std::ratio<3600>>;
1609  std::stringstream ss;
1610  ss << std::fixed << std::setprecision(3) << dhours(uptime).count();
1611  IUSaveText(&FirmwareT[FIRMWARE_UPTIME], ss.str().c_str());
1612  }
1613  catch(...)
1614  {
1615  // Uptime not critical, so just put debug statement on failure.
1616  IUSaveText(&FirmwareT[FIRMWARE_UPTIME], "NA");
1617  LOGF_DEBUG("Failed to process uptime: %s", result[3].c_str());
1618  }
1619  IDSetText(&FirmwareTP, nullptr);
1620 
1621 
1622  lastPowerData = result;
1623  return true;
1624  }
1625 
1626  return false;
1627 }
1628 
1632 bool PegasusUPB::getStepperData()
1633 {
1634  char res[PEGASUS_LEN] = {0};
1635  if (sendCommand("SA", res))
1636  {
1637  std::vector<std::string> result = split(res, ":");
1638  if (result.size() != 4)
1639  {
1640  LOGF_WARN("Received wrong number (%i) of stepper sensor data (%s). Retrying...", result.size(), res);
1641  return false;
1642  }
1643 
1644  if (result == lastStepperData)
1645  return true;
1646 
1647  FocusAbsPosN[0].value = std::stoi(result[0]);
1648  focusMotorRunning = (std::stoi(result[1]) == 1);
1649 
1650  if (FocusAbsPosNP.s == IPS_BUSY && focusMotorRunning == false)
1651  {
1654  IDSetNumber(&FocusAbsPosNP, nullptr);
1655  IDSetNumber(&FocusRelPosNP, nullptr);
1656  }
1657  else if (stepperUpdated(result, 0))
1658  IDSetNumber(&FocusAbsPosNP, nullptr);
1659 
1660  FocusReverseS[INDI_ENABLED].s = (std::stoi(result[2]) == 1) ? ISS_ON : ISS_OFF;
1661  FocusReverseS[INDI_DISABLED].s = (std::stoi(result[2]) == 1) ? ISS_OFF : ISS_ON;
1662 
1663  if (stepperUpdated(result, 1))
1664  IDSetSwitch(&FocusReverseSP, nullptr);
1665 
1666  uint16_t backlash = std::stoi(result[3]);
1667  if (backlash == 0)
1668  {
1669  FocusBacklashN[0].value = backlash;
1672  if (stepperUpdated(result, 3))
1673  {
1674  IDSetSwitch(&FocusBacklashSP, nullptr);
1675  IDSetNumber(&FocuserSettingsNP, nullptr);
1676  }
1677  }
1678  else
1679  {
1682  FocusBacklashN[0].value = backlash;
1683  if (stepperUpdated(result, 3))
1684  {
1685  IDSetSwitch(&FocusBacklashSP, nullptr);
1686  IDSetNumber(&FocuserSettingsNP, nullptr);
1687  }
1688  }
1689 
1690  lastStepperData = result;
1691  return true;
1692  }
1693 
1694  return false;
1695 }
1696 bool PegasusUPB::getDewAggData()
1697 {
1698  char res[PEGASUS_LEN] = {0};
1699  if (sendCommand("DA", res))
1700  {
1701  std::vector<std::string> result = split(res, ":");
1702  if (result.size() != 2)
1703  {
1704  LOGF_WARN("Received wrong number (%i) of dew aggresiveness data (%s). Retrying...", result.size(), res);
1705  return false;
1706  }
1707 
1708  if (result == lastDewAggData)
1709  return true;
1710 
1711  AutoDewAggN[0].value = std::stod(result[1]);
1712  AutoDewAggNP.s = IPS_OK;
1713  IDSetNumber(&AutoDewAggNP, nullptr);
1714 
1715  lastDewAggData = result;
1716  return true;
1717  }
1718  return false;
1719 }
1723 bool PegasusUPB::reboot()
1724 {
1725  return sendCommand("PF", nullptr);
1726 }
1727 
1731 std::vector<std::string> PegasusUPB::split(const std::string &input, const std::string &regex)
1732 {
1733  // passing -1 as the submatch index parameter performs splitting
1734  std::regex re(regex);
1735  std::sregex_token_iterator
1736  first{input.begin(), input.end(), re, -1},
1737  last;
1738  return {first, last};
1739 }
1740 
1744 bool PegasusUPB::setupParams()
1745 {
1746  if (version == UPB_V2)
1747  getPowerOnBoot();
1748 
1749  sendFirmware();
1750 
1751  // Get Max Focuser Speed
1752  char res[PEGASUS_LEN] = {0};
1753  if (sendCommand("SS", res))
1754  {
1755  try
1756  {
1757  uint32_t value = std::stol(res);
1758  if (value == UINT16_MAX)
1759  {
1760  LOGF_WARN("Invalid maximum speed detected: %u. Please set maximum speed appropriate for your motor focus type (0-900)",
1761  value);
1762  FocuserSettingsNP.s = IPS_ALERT;
1763  }
1764  else
1765  {
1766  FocuserSettingsN[SETTING_MAX_SPEED].value = value;
1767  FocuserSettingsNP.s = IPS_OK;
1768  }
1769  }
1770  catch(...)
1771  {
1772  LOGF_WARN("Failed to process focuser max speed: %s", res);
1773  FocuserSettingsNP.s = IPS_ALERT;
1774  }
1775  }
1776 
1777  return false;
1778 }
1779 
1783 void PegasusUPB::cleanupResponse(char *response)
1784 {
1785  std::string s(response);
1786  s.erase(std::remove_if(s.begin(), s.end(),
1787  [](unsigned char x)
1788  {
1789  return std::isspace(x);
1790  }), s.end());
1791  strncpy(response, s.c_str(), PEGASUS_LEN);
1792 }
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 ...
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....
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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.
bool isSimulation() const
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.
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 bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
Provides interface to implement focuser functionality.
ISwitchVectorProperty FocusBacklashSP
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
ISwitchVectorProperty FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
void initProperties(const char *groupName)
Initilize focuser properties. It is recommended to call this function within initProperties() of your...
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
Provides interface to implement weather reporting functionality.
bool syncCriticalParameters()
updateWeatherState Send update weather state to client
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
bool setCriticalParameter(std::string param)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process weather number properties.
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save parameters ranges in the config file.
ILightVectorProperty critialParametersLP
INumberVectorProperty ParametersNP
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning)
addParameter Add a physical weather measurable parameter to the weather driver. The weather value has...
void initProperties(const char *statusGroup, const char *paramsGroup)
Initilize focuser properties. It is recommended to call this function within initProperties() of your...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
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 IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: pegasus_upb.cpp:51
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
const char * getDefaultName() override
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
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.
const char * FOCUS_TAB
FOCUS_TAB Where all the properties for focuser are located.
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
@ IP_WO
Definition: indiapi.h:185
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
#define MAXINDILABEL
Definition: indiapi.h:192
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_NOFMANY
Definition: indiapi.h:175
@ ISR_ATMOST1
Definition: indiapi.h:174
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
@ TTY_TIME_OUT
Definition: indicom.h:154
@ TTY_OVERFLOW
Definition: indicom.h:158
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
void IUFillLight(ILight *lp, const char *name, const char *label, IPState s)
Assign attributes for a light property. The light's auxiliary elements will be set to NULL.
Definition: indidevapi.c:169
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.
Definition: indidevapi.c:272
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 IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name, const char *label, const char *group, IPState s)
Assign attributes for a light vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:255
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 IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
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 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.
Definition: indidevapi.c:180
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidevapi.c:20
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 IDSetLight(const ILightVectorProperty *lvp, const char *fmt,...)
Definition: indidriver.c:1251
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition: indidriver.c:889
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define MAXRBUF
Definition: indiserver.cpp:102
@ value
the parser finished reading a JSON value
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:421
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250