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"
27 #include "connectionplugins/connectionserial.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 
PegasusUPB()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 
initProperties()51 bool PegasusUPB::initProperties()
52 {
53     INDI::DefaultDevice::initProperties();
54 
55     setDriverInterface(AUX_INTERFACE | FOCUSER_INTERFACE | WEATHER_INTERFACE);
56 
57     FI::SetCapability(FOCUSER_CAN_ABS_MOVE |
58                       FOCUSER_CAN_REL_MOVE |
59                       FOCUSER_CAN_REVERSE  |
60                       FOCUSER_CAN_SYNC     |
61                       FOCUSER_CAN_ABORT    |
62                       FOCUSER_HAS_BACKLASH);
63 
64     FI::initProperties(FOCUS_TAB);
65     WI::initProperties(ENVIRONMENT_TAB, ENVIRONMENT_TAB);
66 
67     addAuxControls();
68 
69     ////////////////////////////////////////////////////////////////////////////
70     /// Main Control Panel
71     ////////////////////////////////////////////////////////////////////////////
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",
95                        MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
96 
97     ////////////////////////////////////////////////////////////////////////////
98     /// Power Group
99     ////////////////////////////////////////////////////////////////////////////
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 
220     ////////////////////////////////////////////////////////////////////////////
221     /// Dew Group
222     ////////////////////////////////////////////////////////////////////////////
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 
248     ////////////////////////////////////////////////////////////////////////////
249     /// USB Group
250     ////////////////////////////////////////////////////////////////////////////
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 
321     ////////////////////////////////////////////////////////////////////////////
322     /// Focuser Group
323     ////////////////////////////////////////////////////////////////////////////
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);
330     ////////////////////////////////////////////////////////////////////////////
331     /// Firmware Group
332     ////////////////////////////////////////////////////////////////////////////
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);
337     ////////////////////////////////////////////////////////////////////////////
338     /// Environment Group
339     ////////////////////////////////////////////////////////////////////////////
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 
345     ////////////////////////////////////////////////////////////////////////////
346     /// Serial Connection
347     ////////////////////////////////////////////////////////////////////////////
348     serialConnection = new Connection::Serial(this);
349     serialConnection->registerHandshake([&]()
350     {
351         return Handshake();
352     });
353     registerConnection(serialConnection);
354 
355     return true;
356 }
357 
updateProperties()358 bool PegasusUPB::updateProperties()
359 {
360     INDI::DefaultDevice::updateProperties();
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
412         FI::updateProperties();
413         defineProperty(&FocuserSettingsNP);
414 
415         WI::updateProperties();
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
463         FI::updateProperties();
464         deleteProperty(FocuserSettingsNP.name);
465 
466         WI::updateProperties();
467 
468         deleteProperty(FirmwareTP.name);
469 
470         setupComplete = false;
471     }
472 
473     return true;
474 }
475 
getDefaultName()476 const char * PegasusUPB::getDefaultName()
477 {
478     return "Pegasus UPB";
479 }
480 
481 //////////////////////////////////////////////////////////////////////
482 ///
483 //////////////////////////////////////////////////////////////////////
Handshake()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 
546 //////////////////////////////////////////////////////////////////////
547 ///
548 //////////////////////////////////////////////////////////////////////
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)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] = {true};
692             ISState ports[6] = {ISS_ON};
693 
694             for (int i = 0; i < USBControlV2SP.nsp; i++)
695                 ports[i] = USBControlV2S[i].s;
696 
697             IUUpdateSwitch(&USBControlV2SP, states, names, n);
698             for (int i = 0; i < USBControlV2SP.nsp; i++)
699             {
700                 if (ports[i] != USBControlV2S[i].s)
701                     rc[i] = setUSBPortEnabled(i, USBControlV2S[i].s == ISS_ON);
702             }
703 
704             // All is OK
705             if (rc[0] && rc[1] && rc[2] && rc[3] && rc[4] && rc[5])
706             {
707                 USBControlSP.s = IPS_OK;
708             }
709             else
710             {
711                 IUResetSwitch(&USBControlV2SP);
712                 for (int i = 0; i < USBControlV2SP.nsp; i++)
713                     USBControlV2S[i].s = ports[i];
714                 USBControlV2SP.s = IPS_ALERT;
715             }
716 
717             IDSetSwitch(&USBControlSP, nullptr);
718             return true;
719         }
720 
721         // Focuser backlash
722         //        if (!strcmp(name, FocusBacklashSP.name))
723         //        {
724         //            int prevIndex = IUFindOnSwitchIndex(&FocusBacklashSP);
725         //            IUUpdateSwitch(&FocusBacklashSP, states, names, n);
726         //            if (setFocuserBacklashEnabled(FocusBacklashS[0].s == ISS_ON))
727         //            {
728         //                FocusBacklashSP.s = IPS_OK;
729         //            }
730         //            else
731         //            {
732         //                IUResetSwitch(&FocusBacklashSP);
733         //                FocusBacklashS[prevIndex].s = ISS_ON;
734         //                FocusBacklashSP.s = IPS_ALERT;
735         //            }
736 
737         //            IDSetSwitch(&FocusBacklashSP, nullptr);
738         //            return true;
739         //        }
740 
741         // Power LED
742         if (!strcmp(name, PowerLEDSP.name) && (version == UPB_V1))
743         {
744             int prevIndex = IUFindOnSwitchIndex(&PowerLEDSP);
745             IUUpdateSwitch(&PowerLEDSP, states, names, n);
746             if (setPowerLEDEnabled(PowerLEDS[0].s == ISS_ON))
747             {
748                 PowerLEDSP.s = IPS_OK;
749             }
750             else
751             {
752                 IUResetSwitch(&PowerLEDSP);
753                 PowerLEDS[prevIndex].s = ISS_ON;
754                 PowerLEDSP.s = IPS_ALERT;
755             }
756 
757             IDSetSwitch(&PowerLEDSP, nullptr);
758             return true;
759         }
760 
761         if (strstr(name, "FOCUS"))
762             return FI::processSwitch(dev, name, states, names, n);
763     }
764 
765     return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
766 }
767 
768 //////////////////////////////////////////////////////////////////////
769 ///
770 //////////////////////////////////////////////////////////////////////
ISNewNumber(const char * dev,const char * name,double values[],char * names[],int n)771 bool PegasusUPB::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
772 {
773     if (dev && !strcmp(dev, getDeviceName()))
774     {
775         // Adjustable output
776         if (!strcmp(name, AdjustableOutputNP.name))
777         {
778             if (setAdjustableOutput(static_cast<uint8_t>(values[0])))
779             {
780                 IUUpdateNumber(&AdjustableOutputNP, values, names, n);
781                 AdjustableOutputNP.s = IPS_OK;
782             }
783             else
784                 AdjustableOutputNP.s = IPS_ALERT;
785 
786             IDSetNumber(&AdjustableOutputNP, nullptr);
787             return true;
788         }
789 
790         // Dew PWM
791         if (!strcmp(name, DewPWMNP.name))
792         {
793             bool rc1 = false, rc2 = false, rc3 = false;
794             for (int i = 0; i < n; i++)
795             {
796                 if (!strcmp(names[i], DewPWMN[DEW_PWM_A].name))
797                     rc1 = setDewPWM(5, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
798                 else if (!strcmp(names[i], DewPWMN[DEW_PWM_B].name))
799                     rc2 = setDewPWM(6, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
800                 else if (!strcmp(names[i], DewPWMN[DEW_PWM_C].name))
801                     rc3 = setDewPWM(7, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
802             }
803 
804             DewPWMNP.s = (rc1 && rc2 && rc3) ? IPS_OK : IPS_ALERT;
805             if (DewPWMNP.s == IPS_OK)
806                 IUUpdateNumber(&DewPWMNP, values, names, n);
807             IDSetNumber(&DewPWMNP, nullptr);
808             return true;
809         }
810 
811         // Auto Dew Aggressiveness
812         if (!strcmp(name, AutoDewAggNP.name))
813         {
814             if (setAutoDewAgg(values[0]))
815             {
816                 AutoDewAggN[0].value = values[0];
817                 AutoDewAggNP.s = IPS_OK;
818             }
819             else
820             {
821                 AutoDewAggNP.s = IPS_ALERT;
822             }
823 
824             IDSetNumber(&AutoDewAggNP, nullptr);
825             return true;
826         }
827 
828         // Focuser Settings
829         if (!strcmp(name, FocuserSettingsNP.name))
830         {
831             if (setFocuserMaxSpeed(values[0]))
832             {
833                 FocuserSettingsN[0].value = values[0];
834                 FocuserSettingsNP.s = IPS_OK;
835             }
836             else
837             {
838                 FocuserSettingsNP.s = IPS_ALERT;
839             }
840 
841             IDSetNumber(&FocuserSettingsNP, nullptr);
842             return true;
843         }
844 
845         if (strstr(name, "FOCUS_"))
846             return FI::processNumber(dev, name, values, names, n);
847 
848         if (strstr(name, "WEATHER_"))
849             return WI::processNumber(dev, name, values, names, n);
850     }
851     return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
852 }
853 
854 //////////////////////////////////////////////////////////////////////
855 ///
856 //////////////////////////////////////////////////////////////////////
ISNewText(const char * dev,const char * name,char * texts[],char * names[],int n)857 bool PegasusUPB::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
858 {
859     if (dev && !strcmp(dev, getDeviceName()))
860     {
861         // Power Labels
862         if (!strcmp(name, PowerControlsLabelsTP.name))
863         {
864             IUUpdateText(&PowerControlsLabelsTP, texts, names, n);
865             PowerControlsLabelsTP.s = IPS_OK;
866             LOG_INFO("Power port labels saved. Driver must be restarted for the labels to take effect.");
867             saveConfig();
868             IDSetText(&PowerControlsLabelsTP, nullptr);
869             return true;
870         }
871         // Dew Labels
872         if (!strcmp(name, DewControlsLabelsTP.name))
873         {
874             IUUpdateText(&DewControlsLabelsTP, texts, names, n);
875             DewControlsLabelsTP.s = IPS_OK;
876             LOG_INFO("Dew labels saved. Driver must be restarted for the labels to take effect.");
877             saveConfig();
878             IDSetText(&DewControlsLabelsTP, nullptr);
879             return true;
880         }
881         // USB Labels
882         if (!strcmp(name, USBControlsLabelsTP.name))
883         {
884             IUUpdateText(&USBControlsLabelsTP, texts, names, n);
885             USBControlsLabelsTP.s = IPS_OK;
886             LOG_INFO("USB labels saved. Driver must be restarted for the labels to take effect.");
887             saveConfig();
888             IDSetText(&USBControlsLabelsTP, nullptr);
889             return true;
890         }
891     }
892 
893     return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
894 }
895 
896 //////////////////////////////////////////////////////////////////////
897 ///
898 //////////////////////////////////////////////////////////////////////
sendCommand(const char * cmd,char * res)899 bool PegasusUPB::sendCommand(const char * cmd, char * res)
900 {
901     int nbytes_read = 0, nbytes_written = 0, tty_rc = 0;
902     LOGF_DEBUG("CMD <%s>", cmd);
903 
904     if (isSimulation())
905     {
906         if (!strcmp(cmd, "PS"))
907         {
908             strncpy(res, "PS:1111:12", PEGASUS_LEN);
909         }
910         else if (!strcmp(cmd, "PA"))
911         {
912             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);
913         }
914         else if (!strcmp(cmd, "PC"))
915         {
916             strncpy(res, "0.40:0.00:0.03:26969", PEGASUS_LEN);
917         }
918         else if (!strcmp(cmd, "SA"))
919         {
920             strncpy(res, "3000:0:0:10", PEGASUS_LEN);
921         }
922         else if (!strcmp(cmd, "SS"))
923         {
924             strncpy(res, "999", PEGASUS_LEN);
925         }
926         else if (!strcmp(cmd, "PD"))
927         {
928             strncpy(res, "210", PEGASUS_LEN);
929         }
930         else if (!strcmp(cmd, "PV"))
931         {
932             strncpy(res, "Sim v1.0", PEGASUS_LEN);
933         }
934         else if (res)
935         {
936             strncpy(res, cmd, PEGASUS_LEN);
937         }
938 
939         return true;
940     }
941 
942     for (int i = 0; i < 2; i++)
943     {
944         char command[PEGASUS_LEN] = {0};
945         tcflush(PortFD, TCIOFLUSH);
946         snprintf(command, PEGASUS_LEN, "%s\n", cmd);
947         if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
948             continue;
949 
950         if (!res)
951         {
952             tcflush(PortFD, TCIOFLUSH);
953             return true;
954         }
955 
956         if ( (tty_rc = tty_nread_section(PortFD, res, PEGASUS_LEN, stopChar, PEGASUS_TIMEOUT, &nbytes_read)) != TTY_OK
957                 || nbytes_read == 1)
958             continue;
959 
960         tcflush(PortFD, TCIOFLUSH);
961 
962         cleanupResponse(res);
963         LOGF_DEBUG("RES <%s>", res);
964         return true;
965     }
966 
967     if (tty_rc != TTY_OK)
968     {
969         char errorMessage[MAXRBUF];
970         tty_error_msg(tty_rc, errorMessage, MAXRBUF);
971         LOGF_ERROR("Serial error: %s", errorMessage);
972     }
973 
974     return false;
975 }
976 
977 //////////////////////////////////////////////////////////////////////
978 ///
979 //////////////////////////////////////////////////////////////////////
MoveAbsFocuser(uint32_t targetTicks)980 IPState PegasusUPB::MoveAbsFocuser(uint32_t targetTicks)
981 {
982     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
983     snprintf(cmd, PEGASUS_LEN, "SM:%u", targetTicks);
984     if (sendCommand(cmd, res))
985     {
986         return (!strcmp(res, cmd) ? IPS_BUSY : IPS_ALERT);
987     }
988 
989     return IPS_ALERT;
990 }
991 
992 //////////////////////////////////////////////////////////////////////
993 ///
994 //////////////////////////////////////////////////////////////////////
MoveRelFocuser(FocusDirection dir,uint32_t ticks)995 IPState PegasusUPB::MoveRelFocuser(FocusDirection dir, uint32_t ticks)
996 {
997     return MoveAbsFocuser(dir == FOCUS_INWARD ? FocusAbsPosN[0].value - ticks : FocusAbsPosN[0].value + ticks);
998 }
999 
1000 //////////////////////////////////////////////////////////////////////
1001 ///
1002 //////////////////////////////////////////////////////////////////////
AbortFocuser()1003 bool PegasusUPB::AbortFocuser()
1004 {
1005     char res[PEGASUS_LEN] = {0};
1006     if (sendCommand("SH", res))
1007     {
1008         return (!strcmp(res, "SH"));
1009     }
1010 
1011     return false;
1012 }
1013 
1014 //////////////////////////////////////////////////////////////////////
1015 ///
1016 //////////////////////////////////////////////////////////////////////
ReverseFocuser(bool enabled)1017 bool PegasusUPB::ReverseFocuser(bool enabled)
1018 {
1019     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1020     snprintf(cmd, PEGASUS_LEN, "SR:%d", enabled ? 1 : 0);
1021     if (sendCommand(cmd, res))
1022     {
1023         return (!strcmp(res, cmd));
1024     }
1025 
1026     return false;
1027 }
1028 
1029 //////////////////////////////////////////////////////////////////////
1030 ///
1031 //////////////////////////////////////////////////////////////////////
SyncFocuser(uint32_t ticks)1032 bool PegasusUPB::SyncFocuser(uint32_t ticks)
1033 {
1034     char cmd[PEGASUS_LEN] = {0};
1035     snprintf(cmd, PEGASUS_LEN, "SC:%u", ticks);
1036     return sendCommand(cmd, nullptr);
1037 }
1038 
1039 //////////////////////////////////////////////////////////////////////
1040 ///
1041 //////////////////////////////////////////////////////////////////////
SetFocuserBacklash(int32_t steps)1042 bool PegasusUPB::SetFocuserBacklash(int32_t steps)
1043 {
1044     char cmd[PEGASUS_LEN] = {0};
1045     snprintf(cmd, PEGASUS_LEN, "SB:%d", steps);
1046     return sendCommand(cmd, nullptr);
1047 }
1048 
1049 //////////////////////////////////////////////////////////////////////
1050 ///
1051 //////////////////////////////////////////////////////////////////////
setFocuserMaxSpeed(uint16_t maxSpeed)1052 bool PegasusUPB::setFocuserMaxSpeed(uint16_t maxSpeed)
1053 {
1054     char cmd[PEGASUS_LEN] = {0};
1055     snprintf(cmd, PEGASUS_LEN, "SS:%d", maxSpeed);
1056     return sendCommand(cmd, nullptr);
1057 }
1058 
1059 //////////////////////////////////////////////////////////////////////
1060 ///
1061 //////////////////////////////////////////////////////////////////////
SetFocuserBacklashEnabled(bool enabled)1062 bool PegasusUPB::SetFocuserBacklashEnabled(bool enabled)
1063 {
1064     char cmd[PEGASUS_LEN] = {0};
1065     snprintf(cmd, PEGASUS_LEN, "SB:%d", enabled ? 1 : 0);
1066     return sendCommand(cmd, nullptr);
1067 }
1068 
1069 //////////////////////////////////////////////////////////////////////
1070 ///
1071 //////////////////////////////////////////////////////////////////////
setPowerEnabled(uint8_t port,bool enabled)1072 bool PegasusUPB::setPowerEnabled(uint8_t port, bool enabled)
1073 {
1074     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1075     snprintf(cmd, PEGASUS_LEN, "P%d:%d", port, enabled ? 1 : 0);
1076     if (sendCommand(cmd, res))
1077     {
1078         return (!strcmp(res, cmd));
1079     }
1080 
1081     return false;
1082 }
1083 
1084 //////////////////////////////////////////////////////////////////////
1085 ///
1086 //////////////////////////////////////////////////////////////////////
setPowerLEDEnabled(bool enabled)1087 bool PegasusUPB::setPowerLEDEnabled(bool enabled)
1088 {
1089     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1090     snprintf(cmd, PEGASUS_LEN, "PL:%d", enabled ? 1 : 0);
1091     if (sendCommand(cmd, res))
1092     {
1093         return (!strcmp(res, cmd));
1094     }
1095 
1096     return false;
1097 }
1098 
1099 //////////////////////////////////////////////////////////////////////
1100 ///
1101 //////////////////////////////////////////////////////////////////////
setAutoDewEnabled(bool enabled)1102 bool PegasusUPB::setAutoDewEnabled(bool enabled)
1103 {
1104     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1105     snprintf(cmd, PEGASUS_LEN, "PD:%d", enabled ? 1 : 0);
1106     if (sendCommand(cmd, res))
1107     {
1108         return (!strcmp(res, cmd));
1109     }
1110 
1111     return false;
1112 }
1113 
1114 //////////////////////////////////////////////////////////////////////
1115 ///
1116 //////////////////////////////////////////////////////////////////////
setAutoDewAgg(uint8_t value)1117 bool PegasusUPB::setAutoDewAgg(uint8_t value)
1118 {
1119     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0};
1120     snprintf(cmd, PEGASUS_LEN, "PD:%03d", value);
1121     snprintf(expected, PEGASUS_LEN, "PD:%d", value);
1122     if (sendCommand(cmd, res))
1123     {
1124         return (!strcmp(res, expected));
1125     }
1126 
1127     return false;
1128 }
1129 //////////////////////////////////////////////////////////////////////
1130 ///
1131 //////////////////////////////////////////////////////////////////////
setAdjustableOutput(uint8_t voltage)1132 bool PegasusUPB::setAdjustableOutput(uint8_t voltage)
1133 {
1134     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1135     snprintf(cmd, PEGASUS_LEN, "P8:%d", voltage);
1136     if (sendCommand(cmd, res))
1137     {
1138         return (!strcmp(res, cmd));
1139     }
1140 
1141     return false;
1142 }
1143 
1144 //////////////////////////////////////////////////////////////////////
1145 ///
1146 //////////////////////////////////////////////////////////////////////
setPowerOnBoot()1147 bool PegasusUPB::setPowerOnBoot()
1148 {
1149     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1150     snprintf(cmd, PEGASUS_LEN, "PE:%d%d%d%d", PowerOnBootS[0].s == ISS_ON ? 1 : 0,
1151              PowerOnBootS[1].s == ISS_ON ? 1 : 0,
1152              PowerOnBootS[2].s == ISS_ON ? 1 : 0,
1153              PowerOnBootS[3].s == ISS_ON ? 1 : 0);
1154     if (sendCommand(cmd, res))
1155     {
1156         return (!strcmp(res, "PE:1"));
1157     }
1158 
1159     return false;
1160 }
1161 
1162 //////////////////////////////////////////////////////////////////////
1163 ///
1164 //////////////////////////////////////////////////////////////////////
getPowerOnBoot()1165 bool PegasusUPB::getPowerOnBoot()
1166 {
1167     char res[PEGASUS_LEN] = {0};
1168     if (sendCommand("PS", res))
1169     {
1170         std::vector<std::string> result = split(res, ":");
1171         if (result.size() != 3)
1172         {
1173             LOGF_WARN("Received wrong number (%i) of power on boot data (%s). Retrying...", result.size(), res);
1174             return false;
1175         }
1176 
1177         const char *status = result[1].c_str();
1178         PowerOnBootS[0].s = (status[0] == '1') ? ISS_ON : ISS_OFF;
1179         PowerOnBootS[1].s = (status[1] == '1') ? ISS_ON : ISS_OFF;
1180         PowerOnBootS[2].s = (status[2] == '1') ? ISS_ON : ISS_OFF;
1181         PowerOnBootS[3].s = (status[3] == '1') ? ISS_ON : ISS_OFF;
1182 
1183         AdjustableOutputN[0].value = std::stod(result[2]);
1184         AdjustableOutputNP.s = IPS_OK;
1185 
1186         return true;
1187     }
1188 
1189     return false;
1190 }
1191 
1192 //////////////////////////////////////////////////////////////////////
1193 ///
1194 //////////////////////////////////////////////////////////////////////
setDewPWM(uint8_t id,uint8_t value)1195 bool PegasusUPB::setDewPWM(uint8_t id, uint8_t value)
1196 {
1197     char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0};
1198     snprintf(cmd, PEGASUS_LEN, "P%d:%03d", id, value);
1199     snprintf(expected, PEGASUS_LEN, "P%d:%d", id, value);
1200     if (sendCommand(cmd, res))
1201     {
1202         return (!strcmp(res, expected));
1203     }
1204 
1205     return false;
1206 }
1207 
1208 //////////////////////////////////////////////////////////////////////
1209 ///
1210 //////////////////////////////////////////////////////////////////////
setUSBHubEnabled(bool enabled)1211 bool PegasusUPB::setUSBHubEnabled(bool enabled)
1212 {
1213     char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1214     snprintf(cmd, PEGASUS_LEN, "PU:%d", enabled ? 1 : 0);
1215     snprintf(expected, PEGASUS_LEN, "PU:%d", enabled ? 0 : 1);
1216     if (sendCommand(cmd, res))
1217     {
1218         return (!strcmp(res, expected));
1219     }
1220 
1221     return false;
1222 }
1223 
1224 //////////////////////////////////////////////////////////////////////
1225 ///
1226 //////////////////////////////////////////////////////////////////////
setUSBPortEnabled(uint8_t port,bool enabled)1227 bool PegasusUPB::setUSBPortEnabled(uint8_t port, bool enabled)
1228 {
1229     char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1230     snprintf(cmd, PEGASUS_LEN, "U%d:%d", port + 1, enabled ? 1 : 0);
1231     snprintf(expected, PEGASUS_LEN, "U%d:%d", port + 1, enabled ? 1 : 0);
1232     if (sendCommand(cmd, res))
1233     {
1234         return (!strcmp(res, expected));
1235     }
1236 
1237     return false;
1238 }
1239 
1240 //////////////////////////////////////////////////////////////////////
1241 ///
1242 //////////////////////////////////////////////////////////////////////
toggleAutoDewV2()1243 bool PegasusUPB::toggleAutoDewV2()
1244 {
1245     char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1246 
1247     uint8_t value = 0;
1248 
1249     if (IUFindOnSwitchIndex(&AutoDewV2SP) == -1)
1250         value = 0;
1251     else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_B].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1252         value = 1;
1253     else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_B].s == ISS_ON)
1254         value = 5;
1255     else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1256         value = 6;
1257     else if (AutoDewV2S[DEW_PWM_B].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1258         value = 7;
1259     else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON)
1260         value = 2;
1261     else if (AutoDewV2S[DEW_PWM_B].s == ISS_ON)
1262         value = 3;
1263     else if (AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1264         value = 4;
1265 
1266     snprintf(cmd, PEGASUS_LEN, "PD:%d", value);
1267     snprintf(expected, PEGASUS_LEN, "PD:%d", value);
1268     if (sendCommand(cmd, res))
1269     {
1270         return (!strcmp(res, expected));
1271     }
1272 
1273     return false;
1274 }
1275 
1276 //////////////////////////////////////////////////////////////////////
1277 ///
1278 //////////////////////////////////////////////////////////////////////
saveConfigItems(FILE * fp)1279 bool PegasusUPB::saveConfigItems(FILE * fp)
1280 {
1281     // Save CCD Config
1282     INDI::DefaultDevice::saveConfigItems(fp);
1283     FI::saveConfigItems(fp);
1284     WI::saveConfigItems(fp);
1285 
1286     IUSaveConfigSwitch(fp, &PowerLEDSP);
1287     IUSaveConfigSwitch(fp, &AutoDewSP);
1288     if (version == UPB_V2)
1289         IUSaveConfigNumber(fp, &AutoDewAggNP);
1290     IUSaveConfigNumber(fp, &FocuserSettingsNP);
1291     IUSaveConfigText(fp, &PowerControlsLabelsTP);
1292     IUSaveConfigText(fp, &DewControlsLabelsTP);
1293     IUSaveConfigText(fp, &USBControlsLabelsTP);
1294     return true;
1295 }
1296 
1297 //////////////////////////////////////////////////////////////////////
1298 ///
1299 //////////////////////////////////////////////////////////////////////
TimerHit()1300 void PegasusUPB::TimerHit()
1301 {
1302     if (!isConnected() || setupComplete == false)
1303     {
1304         SetTimer(getCurrentPollingPeriod());
1305         return;
1306     }
1307 
1308     if (getSensorData())
1309     {
1310         getPowerData();
1311         getStepperData();
1312 
1313         if (version == UPB_V2)
1314             getDewAggData();
1315     }
1316 
1317     SetTimer(getCurrentPollingPeriod());
1318 }
1319 
1320 //////////////////////////////////////////////////////////////////////
1321 ///
1322 //////////////////////////////////////////////////////////////////////
sendFirmware()1323 bool PegasusUPB::sendFirmware()
1324 {
1325     char res[PEGASUS_LEN] = {0};
1326     if (sendCommand("PV", res))
1327     {
1328         LOGF_INFO("Detected firmware %s", res);
1329         IUSaveText(&FirmwareT[FIRMWARE_VERSION], res);
1330         IDSetText(&FirmwareTP, nullptr);
1331         return true;
1332     }
1333 
1334     return false;
1335 }
1336 
1337 //////////////////////////////////////////////////////////////////////
1338 ///
1339 //////////////////////////////////////////////////////////////////////
sensorUpdated(const std::vector<std::string> & result,uint8_t start,uint8_t end)1340 bool PegasusUPB::sensorUpdated(const std::vector<std::string> &result, uint8_t start, uint8_t end)
1341 {
1342     for (uint8_t index = start; index <= end; index++)
1343         if (result[index] != lastSensorData[index])
1344             return true;
1345 
1346     return false;
1347 }
1348 
1349 //////////////////////////////////////////////////////////////////////
1350 ///
1351 //////////////////////////////////////////////////////////////////////
getSensorData()1352 bool PegasusUPB::getSensorData()
1353 {
1354     char res[PEGASUS_LEN] = {0};
1355     if (sendCommand("PA", res))
1356     {
1357         std::vector<std::string> result = split(res, ":");
1358         if ( (version == UPB_V1 && result.size() != 19) ||
1359                 (version == UPB_V2 && result.size() != 21))
1360         {
1361             LOGF_WARN("Received wrong number (%i) of detailed sensor data (%s). Retrying...", result.size(), res);
1362             return false;
1363         }
1364 
1365         if (result == lastSensorData)
1366             return true;
1367 
1368         // Power Sensors
1369         PowerSensorsN[SENSOR_VOLTAGE].value = std::stod(result[1]);
1370         PowerSensorsN[SENSOR_CURRENT].value = std::stod(result[2]);
1371         PowerSensorsN[SENSOR_POWER].value = std::stod(result[3]);
1372         PowerSensorsNP.s = IPS_OK;
1373         //if (lastSensorData[0] != result[0] || lastSensorData[1] != result[1] || lastSensorData[2] != result[2])
1374         if (sensorUpdated(result, 0, 2))
1375             IDSetNumber(&PowerSensorsNP, nullptr);
1376 
1377         // Environment Sensors
1378         setParameterValue("WEATHER_TEMPERATURE", std::stod(result[4]));
1379         setParameterValue("WEATHER_HUMIDITY", std::stod(result[5]));
1380         setParameterValue("WEATHER_DEWPOINT", std::stod(result[6]));
1381         //if (lastSensorData[4] != result[4] || lastSensorData[5] != result[5] || lastSensorData[6] != result[6])
1382         if (sensorUpdated(result, 4, 6))
1383         {
1384             if (WI::syncCriticalParameters())
1385                 IDSetLight(&critialParametersLP, nullptr);
1386             ParametersNP.s = IPS_OK;
1387             IDSetNumber(&ParametersNP, nullptr);
1388         }
1389 
1390         // Port Status
1391         const char * portStatus = result[7].c_str();
1392         PowerControlS[0].s = (portStatus[0] == '1') ? ISS_ON : ISS_OFF;
1393         PowerControlS[1].s = (portStatus[1] == '1') ? ISS_ON : ISS_OFF;
1394         PowerControlS[2].s = (portStatus[2] == '1') ? ISS_ON : ISS_OFF;
1395         PowerControlS[3].s = (portStatus[3] == '1') ? ISS_ON : ISS_OFF;
1396         //if (lastSensorData[7] != result[7])
1397         if (sensorUpdated(result, 7, 7))
1398             IDSetSwitch(&PowerControlSP, nullptr);
1399 
1400         // Hub Status
1401         const char * usb_status = result[8].c_str();
1402         if (version == UPB_V1)
1403         {
1404             USBControlS[0].s = (usb_status[0] == '0') ? ISS_ON : ISS_OFF;
1405             USBControlS[1].s = (usb_status[0] == '0') ? ISS_OFF : ISS_ON;
1406             USBStatusL[0].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1407             USBStatusL[1].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1408             USBStatusL[2].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1409             USBStatusL[3].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1410             USBStatusL[4].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1411             //if (lastSensorData[8] != result[8])
1412             if (sensorUpdated(result, 8, 8))
1413             {
1414                 USBControlSP.s = (IUFindOnSwitchIndex(&USBControlSP) == 0) ? IPS_OK : IPS_IDLE;
1415                 IDSetSwitch(&USBControlSP, nullptr);
1416                 IDSetLight(&USBStatusLP, nullptr);
1417             }
1418         }
1419         else
1420         {
1421             USBControlV2S[0].s = (usb_status[0] == '1') ? ISS_ON : ISS_OFF;
1422             USBControlV2S[1].s = (usb_status[1] == '1') ? ISS_ON : ISS_OFF;
1423             USBControlV2S[2].s = (usb_status[2] == '1') ? ISS_ON : ISS_OFF;
1424             USBControlV2S[3].s = (usb_status[3] == '1') ? ISS_ON : ISS_OFF;
1425             USBControlV2S[4].s = (usb_status[4] == '1') ? ISS_ON : ISS_OFF;
1426             USBControlV2S[5].s = (usb_status[5] == '1') ? ISS_ON : ISS_OFF;
1427             USBControlV2SP.s = IPS_OK;
1428             //if (lastSensorData[8] != result[8])
1429             if (sensorUpdated(result, 8, 8))
1430             {
1431                 IDSetSwitch(&USBControlV2SP, nullptr);
1432             }
1433         }
1434 
1435         // From here, we get differences between v1 and v2 readings
1436         int index = 9;
1437         // Dew PWM
1438         DewPWMN[DEW_PWM_A].value = std::stod(result[index]) / 255.0 * 100.0;
1439         DewPWMN[DEW_PWM_B].value = std::stod(result[index + 1]) / 255.0 * 100.0;
1440         if (version == UPB_V2)
1441             DewPWMN[DEW_PWM_C].value = std::stod(result[index + 2]) / 255.0 * 100.0;
1442         //        if (lastSensorData[index] != result[index] ||
1443         //                lastSensorData[index + 1] != result[index + 1] ||
1444         //                (version == UPB_V2 && lastSensorData[index +2] != result[index + 2]))
1445         if (sensorUpdated(result, index, version == UPB_V1 ? index + 1 : index + 2))
1446             IDSetNumber(&DewPWMNP, nullptr);
1447 
1448         index = (version == UPB_V1) ? 11 : 12;
1449 
1450         const double ampDivision = (version == UPB_V1) ? 400.0 : 300.0;
1451 
1452         // Current draw
1453         PowerCurrentN[0].value = std::stod(result[index]) / ampDivision;
1454         PowerCurrentN[1].value = std::stod(result[index + 1]) / ampDivision;
1455         PowerCurrentN[2].value = std::stod(result[index + 2]) / ampDivision;
1456         PowerCurrentN[3].value = std::stod(result[index + 3]) / ampDivision;
1457         //        if (lastSensorData[index] != result[index] ||
1458         //                lastSensorData[index + 1] != result[index + 1] ||
1459         //                lastSensorData[index + 2] != result[index + 2] ||
1460         //                lastSensorData[index + 3] != result[index + 3])
1461         if (sensorUpdated(result, index, index + 3))
1462             IDSetNumber(&PowerCurrentNP, nullptr);
1463 
1464         index = (version == UPB_V1) ? 15 : 16;
1465 
1466         DewCurrentDrawN[DEW_PWM_A].value = std::stod(result[index]) / ampDivision;
1467         DewCurrentDrawN[DEW_PWM_B].value = std::stod(result[index + 1]) / ampDivision;
1468         if (version == UPB_V2)
1469             DewCurrentDrawN[DEW_PWM_C].value = std::stod(result[index + 2]) / (ampDivision * 2);
1470         //        if (lastSensorData[index] != result[index] ||
1471         //                lastSensorData[index + 1] != result[index + 1] ||
1472         //                (version == UPB_V2 && lastSensorData[index + 2] != result[index + 2]))
1473         if (sensorUpdated(result, index, version == UPB_V1 ? index + 1 : index + 2))
1474             IDSetNumber(&DewCurrentDrawNP, nullptr);
1475 
1476         index = (version == UPB_V1) ? 17 : 19;
1477 
1478         // Over Current
1479         //if (lastSensorData[index] != result[index])
1480         if (sensorUpdated(result, index, index))
1481         {
1482             const char * over_curent = result[index].c_str();
1483             OverCurrentL[0].s = (over_curent[0] == '0') ? IPS_OK : IPS_ALERT;
1484             OverCurrentL[1].s = (over_curent[1] == '0') ? IPS_OK : IPS_ALERT;
1485             OverCurrentL[2].s = (over_curent[2] == '0') ? IPS_OK : IPS_ALERT;
1486             OverCurrentL[3].s = (over_curent[3] == '0') ? IPS_OK : IPS_ALERT;
1487             if (version == UPB_V2)
1488             {
1489                 OverCurrentL[4].s = (over_curent[4] == '0') ? IPS_OK : IPS_ALERT;
1490                 OverCurrentL[5].s = (over_curent[5] == '0') ? IPS_OK : IPS_ALERT;
1491                 OverCurrentL[6].s = (over_curent[6] == '0') ? IPS_OK : IPS_ALERT;
1492             }
1493 
1494             IDSetLight(&OverCurrentLP, nullptr);
1495         }
1496 
1497         index = (version == UPB_V1) ? 18 : 20;
1498 
1499         // Auto Dew
1500         if (version == UPB_V1)
1501         {
1502             //if (lastSensorData[index] != result[index])
1503             if (sensorUpdated(result, index, index))
1504             {
1505                 AutoDewS[INDI_ENABLED].s  = (std::stoi(result[index]) == 1) ? ISS_ON : ISS_OFF;
1506                 AutoDewS[INDI_DISABLED].s = (std::stoi(result[index]) == 1) ? ISS_OFF : ISS_ON;
1507                 IDSetSwitch(&AutoDewSP, nullptr);
1508             }
1509         }
1510         else
1511         {
1512             //if (lastSensorData[index] != result[index])
1513             if (sensorUpdated(result, index, index))
1514             {
1515                 int value = std::stoi(result[index]);
1516                 IUResetSwitch(&AutoDewV2SP);
1517                 switch (value)
1518                 {
1519                     case 1:
1520                         AutoDewV2S[DEW_PWM_A].s = AutoDewV2S[DEW_PWM_B].s = AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1521                         break;
1522 
1523                     case 2:
1524                         AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1525                         break;
1526 
1527                     case 3:
1528                         AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1529                         break;
1530 
1531                     case 4:
1532                         AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1533                         break;
1534 
1535                     case 5:
1536                         AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1537                         AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1538                         break;
1539 
1540                     case 6:
1541                         AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1542                         AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1543                         break;
1544 
1545                     case 7:
1546                         AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1547                         AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1548                         break;
1549                     default:
1550                         break;
1551                 }
1552                 IDSetSwitch(&AutoDewV2SP, nullptr);
1553             }
1554         }
1555 
1556         lastSensorData = result;
1557         return true;
1558     }
1559 
1560     return false;
1561 }
1562 
1563 //////////////////////////////////////////////////////////////////////
1564 ///
1565 //////////////////////////////////////////////////////////////////////
getPowerData()1566 bool PegasusUPB::getPowerData()
1567 {
1568     char res[PEGASUS_LEN] = {0};
1569     if (sendCommand("PC", res))
1570     {
1571         std::vector<std::string> result = split(res, ":");
1572         if ( (version == UPB_V1 && result.size() != 3) ||
1573                 (version == UPB_V2 && result.size() != 4))
1574         {
1575             LOGF_WARN("Received wrong number (%i) of power sensor data (%s). Retrying...", result.size(), res);
1576             return false;
1577         }
1578 
1579         if (result == lastPowerData)
1580             return true;
1581 
1582         PowerConsumptionN[CONSUMPTION_AVG_AMPS].value = std::stod(result[0]);
1583         PowerConsumptionN[CONSUMPTION_AMP_HOURS].value = std::stod(result[1]);
1584         PowerConsumptionN[CONSUMPTION_WATT_HOURS].value = std::stod(result[2]);
1585         PowerConsumptionNP.s = IPS_OK;
1586         IDSetNumber(&PowerConsumptionNP, nullptr);
1587 
1588         if (version == UPB_V2)
1589         {
1590             try
1591             {
1592                 std::chrono::milliseconds uptime(std::stol(result[3]));
1593                 using dhours = std::chrono::duration<double, std::ratio<3600>>;
1594                 std::stringstream ss;
1595                 ss << std::fixed << std::setprecision(3) << dhours(uptime).count();
1596                 IUSaveText(&FirmwareT[FIRMWARE_UPTIME], ss.str().c_str());
1597             }
1598             catch(...)
1599             {
1600                 IUSaveText(&FirmwareT[FIRMWARE_UPTIME], "NA");
1601                 LOGF_WARN("Failed to process uptime: %s", result[3].c_str());
1602                 return false;
1603             }
1604             IDSetText(&FirmwareTP, nullptr);
1605         }
1606 
1607         lastPowerData = result;
1608         return true;
1609     }
1610 
1611     return false;
1612 }
1613 
1614 //////////////////////////////////////////////////////////////////////
1615 ///
1616 //////////////////////////////////////////////////////////////////////
getStepperData()1617 bool PegasusUPB::getStepperData()
1618 {
1619     char res[PEGASUS_LEN] = {0};
1620     if (sendCommand("SA", res))
1621     {
1622         std::vector<std::string> result = split(res, ":");
1623         if (result.size() != 4)
1624         {
1625             LOGF_WARN("Received wrong number (%i) of stepper sensor data (%s). Retrying...", result.size(), res);
1626             return false;
1627         }
1628 
1629         if (result == lastStepperData)
1630             return true;
1631 
1632         FocusAbsPosN[0].value = std::stoi(result[0]);
1633         focusMotorRunning = (std::stoi(result[1]) == 1);
1634 
1635         if (FocusAbsPosNP.s == IPS_BUSY && focusMotorRunning == false)
1636         {
1637             FocusAbsPosNP.s = IPS_OK;
1638             FocusRelPosNP.s = IPS_OK;
1639             IDSetNumber(&FocusAbsPosNP, nullptr);
1640             IDSetNumber(&FocusRelPosNP, nullptr);
1641         }
1642         else if (result[0] != lastStepperData[0])
1643             IDSetNumber(&FocusAbsPosNP, nullptr);
1644 
1645         FocusReverseS[INDI_ENABLED].s = (std::stoi(result[2]) == 1) ? ISS_ON : ISS_OFF;
1646         FocusReverseS[INDI_DISABLED].s = (std::stoi(result[2]) == 1) ? ISS_OFF : ISS_ON;
1647 
1648         if (result[2] != lastStepperData[2])
1649             IDSetSwitch(&FocusReverseSP, nullptr);
1650 
1651         uint16_t backlash = std::stoi(result[3]);
1652         if (backlash == 0)
1653         {
1654             FocusBacklashN[0].value = backlash;
1655             FocusBacklashS[INDI_ENABLED].s = ISS_OFF;
1656             FocusBacklashS[INDI_DISABLED].s = ISS_ON;
1657             if (result[3] != lastStepperData[3])
1658             {
1659                 IDSetSwitch(&FocusBacklashSP, nullptr);
1660                 IDSetNumber(&FocuserSettingsNP, nullptr);
1661             }
1662         }
1663         else
1664         {
1665             FocusBacklashS[INDI_ENABLED].s = ISS_ON;
1666             FocusBacklashS[INDI_DISABLED].s = ISS_OFF;
1667             FocusBacklashN[0].value = backlash;
1668             if (result[3] != lastStepperData[3])
1669             {
1670                 IDSetSwitch(&FocusBacklashSP, nullptr);
1671                 IDSetNumber(&FocuserSettingsNP, nullptr);
1672             }
1673         }
1674 
1675         lastStepperData = result;
1676         return true;
1677     }
1678 
1679     return false;
1680 }
getDewAggData()1681 bool PegasusUPB::getDewAggData()
1682 {
1683     char res[PEGASUS_LEN] = {0};
1684     if (sendCommand("DA", res))
1685     {
1686         std::vector<std::string> result = split(res, ":");
1687         if (result.size() != 2)
1688         {
1689             LOGF_WARN("Received wrong number (%i) of dew aggresiveness data (%s). Retrying...", result.size(), res);
1690             return false;
1691         }
1692 
1693         if (result == lastDewAggData)
1694             return true;
1695 
1696         AutoDewAggN[0].value = std::stod(result[1]);
1697         AutoDewAggNP.s = IPS_OK;
1698         IDSetNumber(&AutoDewAggNP, nullptr);
1699 
1700         lastDewAggData = result;
1701         return true;
1702     }
1703     return false;
1704 }
1705 //////////////////////////////////////////////////////////////////////
1706 ///
1707 //////////////////////////////////////////////////////////////////////
reboot()1708 bool PegasusUPB::reboot()
1709 {
1710     return sendCommand("PF", nullptr);
1711 }
1712 
1713 //////////////////////////////////////////////////////////////////////
1714 ///
1715 //////////////////////////////////////////////////////////////////////
split(const std::string & input,const std::string & regex)1716 std::vector<std::string> PegasusUPB::split(const std::string &input, const std::string &regex)
1717 {
1718     // passing -1 as the submatch index parameter performs splitting
1719     std::regex re(regex);
1720     std::sregex_token_iterator
1721     first{input.begin(), input.end(), re, -1},
1722           last;
1723     return {first, last};
1724 }
1725 
1726 //////////////////////////////////////////////////////////////////////
1727 ///
1728 //////////////////////////////////////////////////////////////////////
setupParams()1729 bool PegasusUPB::setupParams()
1730 {
1731     if (version == UPB_V2)
1732         getPowerOnBoot();
1733 
1734     sendFirmware();
1735 
1736     // Get Max Focuser Speed
1737     char res[PEGASUS_LEN] = {0};
1738     if (sendCommand("SS", res))
1739     {
1740         try
1741         {
1742             uint32_t value = std::stol(res);
1743             if (value == UINT16_MAX)
1744             {
1745                 LOGF_WARN("Invalid maximum speed detected: %u. Please set maximum speed appropiate for your motor focus type (0-900)",
1746                           value);
1747                 FocuserSettingsNP.s = IPS_ALERT;
1748             }
1749             else
1750             {
1751                 FocuserSettingsN[SETTING_MAX_SPEED].value = value;
1752                 FocuserSettingsNP.s = IPS_OK;
1753             }
1754         }
1755         catch(...)
1756         {
1757             LOGF_WARN("Failed to process focuser max speed: %s", res);
1758             FocuserSettingsNP.s = IPS_ALERT;
1759         }
1760     }
1761 
1762     return false;
1763 }
1764 
1765 //////////////////////////////////////////////////////////////////////
1766 ///
1767 //////////////////////////////////////////////////////////////////////
cleanupResponse(char * response)1768 void PegasusUPB::cleanupResponse(char *response)
1769 {
1770     std::string s(response);
1771     s.erase(std::remove_if(s.begin(), s.end(),
1772                            [](unsigned char x)
1773     {
1774         return std::isspace(x);
1775     }), s.end());
1776     strncpy(response, s.c_str(), PEGASUS_LEN);
1777 }
1778