1 /*
2 bcmxcp.c - driver for powerware UPS
3
4 Total rewrite of bcmxcp.c (nut ver-1.4.3)
5 * Copyright (c) 2002, Martin Schroeder *
6 * emes -at- geomer.de *
7 * All rights reserved.*
8
9 Copyright (C)
10 2004 Kjell Claesson <kjell.claesson-at-epost.tidanet.se>
11 2004 Tore Ørpetveit <tore-at-orpetveit.net>
12 2011 - 2015 Arnaud Quette <ArnaudQuette@Eaton.com>
13
14 Thanks to Tore Ørpetveit <tore-at-orpetveit.net> that sent me the
15 manuals for bcm/xcp.
16
17 And to Fabio Di Niro <fabio.diniro@email.it> and his metasys module.
18 It influenced the layout of this driver.
19
20 Modified for USB by Wolfgang Ocker <weo@weo1.de>
21
22 ojw0000 2007Apr5 Oliver Wilcock - modified to control individual load segments (outlet.2.shutdown.return) on Powerware PW5125.
23
24 Modified to support setvar for outlet.n.delay.start by Rich Wrenn (RFW) 9-3-11.
25 Modified to support setvar for outlet.n.delay.shutdown by Arnaud Quette, 9-12-11
26
27 This program is free software; you can redistribute it and/or modify
28 it under the terms of the GNU General Public License as published by
29 the Free Software Foundation; either version 2 of the License, or
30 (at your option) any later version.
31
32 This program is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
36
37 You should have received a copy of the GNU General Public License
38 along with this program; if not, write to the Free Software
39 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
40
41 TODO List:
42
43 Extend the parsing of the Standard ID Block, to read:
44
45 Config Block Length: (High priority)
46 Give information if config block is
47 present, and how long it is, if it exist.
48 If config block exist, read the config block and parse the
49 'Length of the Extended Limits Configuration Block' for
50 extended configuration commands
51
52 Statistic map Size: (Low priority)
53 May be used to se if there is a Statistic Map.
54 It holds data on the utility power quality for
55 the past month and since last reset. Number of
56 times on battery and how long. Up time and utility
57 frequency deviation. (Only larger ups'es)
58
59 Size of Alarm History Log: (Low priority)
60 See if it have any alarm history block and enable
61 command to dump it.
62
63 Maximum Supported Command Length: ( Med. to High priority)
64 Give info about the ups receive buffer size.
65
66 Size of Alarm Block: ( Med. to High priority)
67 Make a smarter handling of the Active alarm's if we know the length
68 of the Active Alarm Block. Don't need the long loop to parse the
69 alarm's. Maybe use another way to set up the alarm struct in the
70 'init_alarm_map'.
71
72 Parse 'Communication Capabilities Block' ( Low priority)
73 Get info of the connected ports ID, number of baud rates,
74 command and respnse length.
75
76 Parse 'Communication Port List Block': ( Low priority)
77 This block gives info about the communication ports. Some ups'es
78 have multiple comport's, and use one port for eatch load segment.
79 In this block it is possible to get:
80 Number of ports. (In this List)
81 This Comport id (Which Comm Port is reporting this block.)
82 Comport id (Id for eatch port listed. The first comport ID=1)
83 Baudrate of the listed port.
84 Serial config.
85 Port usage:
86 What this Comm Port is being used for:
87 0 = Unknown usage, No communication occurring.
88 1 = Undefined / Unknown communication occurring
89 2 = Waiting to communicate with a UPS
90 3 = Communication established with a UPS
91 4 = Waiting to communicate with software or adapter
92 5 = Communication established software (e.g., LanSafe)
93 or adapter (e.g., ConnectUPS)
94 6 = Communicating with a Display Device
95 7 = Multi-drop Serial channel
96 8 = Communicating with an Outlet Controller
97 Number of outlets. (Number of Outlets "assigned to" (controlled by) this Comm Port)
98 Outlet number. (Each assigned Outlet is listed (1-64))
99
100 'Set outlet parameter command (0x97)' to alter the delay
101 settings or turn the outlet on or off with a delay (0 - 32767 seconds)
102
103
104 Rewrite some parts of the driver, to minimise code duplication. (Like the instant commands)
105
106 Implement support for Password Authorization (XCP spec, §4.3.2)
107
108 Complete support for settable variables (upsh.setvar)
109 */
110
111
112 #include "main.h"
113 #include <math.h> /* For ldexp() */
114 #include <float.h> /*for FLT_MAX */
115 #include "nut_stdint.h" /* for uint8_t, uint16_t, uint32_t, ... */
116 #include "bcmxcp_io.h"
117 #include "bcmxcp.h"
118
119 #define DRIVER_NAME "BCMXCP UPS driver"
120 #define DRIVER_VERSION "0.31"
121
122 #define MAX_NUT_NAME_LENGTH 128
123 #define NUT_OUTLET_POSITION 7
124
125 /* driver description structure */
126 upsdrv_info_t upsdrv_info = {
127 DRIVER_NAME,
128 DRIVER_VERSION,
129 "Martin Schroeder <emes@geomer.de>\n" \
130 "Kjell Claesson <kjell.claesson@epost.tidanet.se>\n" \
131 "Tore Ørpetveit <tore@orpetveit.net>\n" \
132 "Arnaud Quette <ArnaudQuette@Eaton.com>\n" \
133 "Wolfgang Ocker <weo@weo1.de>\n" \
134 "Oliver Wilcock\n" \
135 "Prachi Gandhi <prachisgandhi@eaton.com>\n" \
136 "Alf Høgemark <alf@i100>\n" \
137 "Gavrilov Igor",
138 DRV_STABLE,
139 { &comm_upsdrv_info, NULL }
140 };
141
142 static int get_word(const unsigned char*);
143 static long int get_long(const unsigned char*);
144 static float get_float(const unsigned char *data);
145 static void init_command_map(void);
146 static void init_meter_map(void);
147 static void init_alarm_map(void);
148 static bool_t init_command(int size);
149 static void init_config(void);
150 static void init_limit(void);
151 static void init_ext_vars(void);
152 static void init_topology(void);
153 static void init_ups_meter_map(const unsigned char *map, unsigned char len);
154 static void init_ups_alarm_map(const unsigned char *map, unsigned char len);
155 static bool_t set_alarm_support_in_alarm_map(const unsigned char *map, const int mapIndex, const int bitmask, const int alarmMapIndex, const int alarmBlockIndex);
156 static void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value);
157 static int init_outlet(unsigned char len);
158 static void init_system_test_capabilities(void);
159 static int instcmd(const char *cmdname, const char *extra);
160 static int setvar(const char *varname, const char *val);
161 static int decode_instcmd_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg);
162 static int decode_setvar_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg);
163 static float calculate_ups_load(const unsigned char *data);
164
165 static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting);
166
167 const char *FreqTol[3] = {"+/-2%", "+/-5%", "+/-7"};
168 const char *ABMStatus[4] = {"charging", "discharging", "floating", "resting"};
169 const char *OutletStatus[9] = {"unknown","on/closed","off/open","on with pending","off with pending","unknown","unknown","failed and closed","failed and open"};
170 /* Standard Authorization Block */
171 unsigned char AUTHOR[4] = {0xCF, 0x69, 0xE8, 0xD5};
172 int nphases = 0;
173 int outlet_block_len = 0;
174 const char *cpu_name[5] = {"Cont:", "Inve:", "Rect:", "Netw:", "Disp:"};
175 const char *horn_stat[3] = {"disabled", "enabled", "muted"};
176
177
178 /* Battery test results */
179 info_lkp_t batt_test_info[] = {
180 { 0, "No test initiated", NULL },
181 { 1, "In progress", NULL },
182 { 2, "Done and passed", NULL },
183 { 3, "Aborted", NULL },
184 { 4, "Done and error", NULL },
185 { 5, "Test scheduled", NULL },
186 /* Not sure about the meaning of the below ones! */
187 { 6, NULL, NULL }, /* The string was present but it has now been removed */
188 { 7, NULL, NULL }, /* The string was not installed at the last power up */
189 { 0, NULL, NULL }
190 };
191
192 /* Topology map results */
193 info_lkp_t topology_info[] = {
194 { BCMXCP_TOPOLOGY_OFFLINE_SWITCHER_1P, "Off-line switcher, Single Phase", NULL },
195 { BCMXCP_TOPOLOGY_LINEINT_UPS_1P, "Line-Interactive UPS, Single Phase", NULL },
196 { BCMXCP_TOPOLOGY_LINEINT_UPS_2P, "Line-Interactive UPS, Two Phase", NULL },
197 { BCMXCP_TOPOLOGY_LINEINT_UPS_3P, "Line-Interactive UPS, Three Phase", NULL },
198 { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_1P, "Dual AC Input, On-Line UPS, Single Phase", NULL },
199 { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_2P, "Dual AC Input, On-Line UPS, Two Phase", NULL },
200 { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_3P, "Dual AC Input, On-Line UPS, Three Phase", NULL },
201 { BCMXCP_TOPOLOGY_ONLINE_UPS_1P, "On-Line UPS, Single Phase", NULL },
202 { BCMXCP_TOPOLOGY_ONLINE_UPS_2P, "On-Line UPS, Two Phase", NULL },
203 { BCMXCP_TOPOLOGY_ONLINE_UPS_3P, "On-Line UPS, Three Phase", NULL },
204 { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_1P, "Parallel Redundant On-Line UPS, Single Phase", NULL },
205 { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_2P, "Parallel Redundant On-Line UPS, Two Phase", NULL },
206 { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_3P, "Parallel Redundant On-Line UPS, Three Phase", NULL },
207 { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_1P, "Parallel for Capacity On-Line UPS, Single Phase", NULL },
208 { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_2P, "Parallel for Capacity On-Line UPS, Two Phase", NULL },
209 { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_3P, "Parallel for Capacity On-Line UPS, Three Phase", NULL },
210 { BCMXCP_TOPOLOGY_SYSTEM_BYPASS_MODULE_3P, "System Bypass Module, Three Phase", NULL },
211 { BCMXCP_TOPOLOGY_HOT_TIE_CABINET_3P, "Hot-Tie Cabinet, Three Phase", NULL },
212 { BCMXCP_TOPOLOGY_OUTLET_CONTROLLER_1P, "Outlet Controller, Single Phase", NULL },
213 { BCMXCP_TOPOLOGY_DUAL_AC_STATIC_SWITCH_3P, "Dual AC Input Static Switch Module, 3 Phase", NULL },
214 { 0, NULL, NULL }
215 };
216
217 /* Command map results */
218 info_lkp_t command_map_info[] = {
219 { PW_INIT_BAT_TEST, "test.battery.start", NULL },
220 { PW_LOAD_OFF_RESTART, "shutdown.return", NULL },
221 { PW_UPS_OFF, "shutdown.stayoff", NULL },
222 { PW_UPS_ON, "load.on", NULL},
223 { PW_GO_TO_BYPASS, "bypass.start", NULL},
224 { 0, NULL, NULL }
225 };
226
227 /* System test capabilities results */
228 info_lkp_t system_test_info[] = {
229 { PW_SYS_TEST_GENERAL, "test.system.start", NULL },
230 /* { PW_SYS_TEST_SCHEDULE_BATTERY_COMMISSION, "test.battery.start.delayed", NULL }, */
231 /* { PW_SYS_TEST_ALTERNATE_AC_INPUT, "test.alternate_acinput.start", NULL }, */
232 { PW_SYS_TEST_FLASH_LIGHTS, "test.panel.start", NULL },
233 { 0, NULL, NULL }
234 };
235
236 /* allocate storage for shared variables (extern in bcmxcp.h) */
237 BCMXCP_COMMAND_MAP_ENTRY_t
238 bcmxcp_command_map[BCMXCP_COMMAND_MAP_MAX];
239 BCMXCP_METER_MAP_ENTRY_t
240 bcmxcp_meter_map[BCMXCP_METER_MAP_MAX];
241 BCMXCP_ALARM_MAP_ENTRY_t
242 bcmxcp_alarm_map[BCMXCP_ALARM_MAP_MAX];
243 BCMXCP_STATUS_t
244 bcmxcp_status;
245
246
247 /* get_word function from nut driver metasys.c */
get_word(const unsigned char * buffer)248 int get_word(const unsigned char *buffer) /* return an integer reading a word in the supplied buffer */
249 {
250 unsigned char a, b;
251 int result;
252
253 a = buffer[0];
254 b = buffer[1];
255 result = b*256 + a;
256
257 return result;
258 }
259
260 /* get_long function from nut driver metasys.c for meter readings*/
get_long(const unsigned char * buffer)261 long int get_long(const unsigned char *buffer) /* return a long integer reading 4 bytes in the supplied buffer.*/
262 {
263 unsigned char a, b, c, d;
264 long int result;
265
266 a = buffer[0];
267 b = buffer[1];
268 c = buffer[2];
269 d = buffer[3];
270 result = (256*256*256*d) + (256*256*c) + (256*b) + a;
271
272 return result;
273 }
274
275 /* get_float funktion for convering IEEE-754 to float */
get_float(const unsigned char * data)276 float get_float(const unsigned char *data)
277 {
278 int s, e;
279 unsigned long src;
280 long f;
281
282 src = ((unsigned long)data[3] << 24) |
283 ((unsigned long)data[2] << 16) |
284 ((unsigned long)data[1] << 8) |
285 ((unsigned long)data[0]);
286
287 s = (src & 0x80000000UL) >> 31;
288 e = (src & 0x7F800000UL) >> 23;
289 f = (src & 0x007FFFFFUL);
290
291 if (e == 255 && f != 0)
292 {
293 /* NaN (Not a Number) */
294 return FLT_MAX;
295 }
296
297 if (e == 255 && f == 0 && s == 1)
298 {
299 /* Negative infinity */
300 return -FLT_MAX;
301 }
302
303 if (e == 255 && f == 0 && s == 0)
304 {
305 /* Positive infinity */
306 return FLT_MAX;
307 }
308
309 if (e > 0 && e < 255)
310 {
311 /* Normal number */
312 f += 0x00800000UL;
313 if (s) f = -f;
314 return ldexp(f, e - 150);
315 }
316
317 if (e == 0 && f != 0)
318 {
319 /* Denormal number */
320 if (s) f = -f;
321 return ldexp(f, -149);
322 }
323
324 if (e == 0 && f == 0 && (s == 1 || s == 0))
325 {
326 /* Zero */
327 return 0;
328 }
329
330 /* Never happens */
331 upslogx(LOG_ERR, "s = %d, e = %d, f = %lu\n", s, e, f);
332 return 0;
333 }
334
335 /* lightweight function to calculate the 8-bit
336 * two's complement checksum of buf, using XCP data length (including header)
337 * the result must be 0 for the sequence data to be valid */
checksum_test(const unsigned char * buf)338 int checksum_test(const unsigned char *buf)
339 {
340 unsigned char checksum = 0;
341 int i, length;
342
343 /* buf[2] is the length of the XCP frame ; add 5 for the header */
344 length = (int)(buf[2]) + 5;
345
346 for (i = 0; i < length; i++) {
347 checksum += buf[i];
348 }
349 /* Compute the 8-bit, Two's Complement checksum now and return it */
350 checksum = ((0x100 - checksum) & 0xFF);
351 return (checksum == 0);
352 }
353
calc_checksum(const unsigned char * buf)354 unsigned char calc_checksum(const unsigned char *buf)
355 {
356 unsigned char c;
357 int i;
358
359 c = 0;
360 for(i = 0; i < 2 + buf[1]; i++)
361 c -= buf[i];
362
363 return c;
364 }
365
init_command_map()366 void init_command_map()
367 {
368 int i = 0;
369
370 /* Clean entire map */
371 memset(&bcmxcp_command_map, 0, sizeof(BCMXCP_COMMAND_MAP_ENTRY_t) * BCMXCP_COMMAND_MAP_MAX);
372
373 /* Set all command descriptions */
374 bcmxcp_command_map[PW_ID_BLOCK_REQ].command_desc = "PW_ID_BLOCK_REQ";
375 bcmxcp_command_map[PW_EVENT_HISTORY_LOG_REQ].command_desc = "PW_EVENT_HISTORY_LOG_REQ";
376 bcmxcp_command_map[PW_STATUS_REQ].command_desc = "PW_STATUS_REQ";
377 bcmxcp_command_map[PW_METER_BLOCK_REQ].command_desc = "PW_METER_BLOCK_REQ";
378 bcmxcp_command_map[PW_CUR_ALARM_REQ].command_desc = "PW_CUR_ALARM_REQ";
379 bcmxcp_command_map[PW_CONFIG_BLOCK_REQ].command_desc = "PW_CONFIG_BLOCK_REQ";
380 bcmxcp_command_map[PW_UTILITY_STATISTICS_BLOCK_REQ].command_desc = "PW_UTILITY_STATISTICS_BLOCK_REQ";
381 bcmxcp_command_map[PW_WAVEFORM_BLOCK_REQ].command_desc = "PW_WAVEFORM_BLOCK_REQ";
382 bcmxcp_command_map[PW_BATTERY_REQ].command_desc = "PW_BATTERY_REQ";
383 bcmxcp_command_map[PW_LIMIT_BLOCK_REQ].command_desc = "PW_LIMIT_BLOCK_REQ";
384 bcmxcp_command_map[PW_TEST_RESULT_REQ].command_desc = "PW_TEST_RESULT_REQ";
385 bcmxcp_command_map[PW_COMMAND_LIST_REQ].command_desc = "PW_COMMAND_LIST_REQ";
386 bcmxcp_command_map[PW_OUT_MON_BLOCK_REQ].command_desc = "PW_OUT_MON_BLOCK_REQ";
387 bcmxcp_command_map[PW_COM_CAP_REQ].command_desc = "PW_COM_CAP_REQ";
388 bcmxcp_command_map[PW_UPS_TOP_DATA_REQ].command_desc = "PW_UPS_TOP_DATA_REQ";
389 bcmxcp_command_map[PW_COM_PORT_LIST_BLOCK_REQ].command_desc = "PW_COM_PORT_LIST_BLOCK_REQ";
390 bcmxcp_command_map[PW_REQUEST_SCRATCHPAD_DATA_REQ].command_desc = "PW_REQUEST_SCRATCHPAD_DATA_REQ";
391 bcmxcp_command_map[PW_GO_TO_BYPASS].command_desc = "PW_GO_TO_BYPASS";
392 bcmxcp_command_map[PW_UPS_ON].command_desc = "PW_UPS_ON";
393 bcmxcp_command_map[PW_LOAD_OFF_RESTART].command_desc = "PW_LOAD_OFF_RESTART";
394 bcmxcp_command_map[PW_UPS_OFF].command_desc = "PW_UPS_OFF";
395 bcmxcp_command_map[PW_DECREMENT_OUTPUT_VOLTAGE].command_desc = "PW_DECREMENT_OUTPUT_VOLTAGE";
396 bcmxcp_command_map[PW_INCREMENT_OUTPUT_VOLTAGE].command_desc = "PW_INCREMENT_OUTPUT_VOLTAGE";
397 bcmxcp_command_map[PW_SET_TIME_AND_DATE].command_desc = "PW_SET_TIME_AND_DATE";
398 bcmxcp_command_map[PW_UPS_ON_TIME].command_desc = "PW_UPS_ON_TIME";
399 bcmxcp_command_map[PW_UPS_ON_AT_TIME].command_desc = "PW_UPS_ON_AT_TIME";
400 bcmxcp_command_map[PW_UPS_OFF_TIME].command_desc = "PW_UPS_OFF_TIME";
401 bcmxcp_command_map[PW_UPS_OFF_AT_TIME].command_desc = "PW_UPS_OFF_AT_TIME";
402 bcmxcp_command_map[PW_SET_CONF_COMMAND].command_desc = "PW_SET_CONF_COMMAND";
403 bcmxcp_command_map[PW_SET_OUTLET_COMMAND].command_desc = "PW_SET_OUTLET_COMMAND";
404 bcmxcp_command_map[PW_SET_COM_COMMAND].command_desc = "PW_SET_COM_COMMAND";
405 bcmxcp_command_map[PW_SET_SCRATHPAD_SECTOR].command_desc = "PW_SET_SCRATHPAD_SECTOR";
406 bcmxcp_command_map[PW_SET_POWER_STRATEGY].command_desc = "PW_SET_POWER_STRATEGY";
407 bcmxcp_command_map[PW_SET_REQ_ONLY_MODE].command_desc = "PW_SET_REQ_ONLY_MODE";
408 bcmxcp_command_map[PW_SET_UNREQUESTED_MODE].command_desc = "PW_SET_UNREQUESTED_MODE";
409 bcmxcp_command_map[PW_INIT_BAT_TEST].command_desc = "PW_INIT_BAT_TEST";
410 bcmxcp_command_map[PW_INIT_SYS_TEST].command_desc = "PW_INIT_SYS_TEST";
411 bcmxcp_command_map[PW_SELECT_SUBMODULE].command_desc = "PW_SELECT_SUBMODULE";
412 bcmxcp_command_map[PW_AUTHORIZATION_CODE].command_desc = "PW_AUTHORIZATION_CODE";
413
414 for(i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) {
415 bcmxcp_command_map[i].command_byte = 0;
416 }
417 }
418
init_meter_map()419 void init_meter_map()
420 {
421 /* Clean entire map */
422 memset(&bcmxcp_meter_map, 0, sizeof(BCMXCP_METER_MAP_ENTRY_t) * BCMXCP_METER_MAP_MAX);
423
424 /* Set all corresponding mappings NUT <-> BCM/XCP */
425 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_AB].nut_entity = "output.L1-L2.voltage";
426 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_BC].nut_entity = "output.L2-L3.voltage";
427 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_CA].nut_entity = "output.L3-L1.voltage";
428 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_AB].nut_entity = "input.L1-L2.voltage";
429 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_BC].nut_entity = "input.L2-L3.voltage";
430 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_CA].nut_entity = "input.L3-L1.voltage";
431 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_B].nut_entity = "input.L2.current";
432 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_C].nut_entity = "input.L3.current";
433 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_WATTS].nut_entity = "input.realpower";
434 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].nut_entity = "ups.power";
435 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VA].nut_entity = "input.power";
436 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_POWER_FACTOR].nut_entity = "output.powerfactor";
437 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_POWER_FACTOR].nut_entity = "input.powerfactor";
438
439 if (nphases == 1) {
440 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.current";
441 bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "ups.load"; /* TODO: Decide on corresponding three-phase variable mapping. */
442 bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.voltage";
443 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.voltage";
444 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.current";
445 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.current.nominal";
446 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.voltage";
447 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS].nut_entity = "ups.realpower";
448 } else {
449 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.L1.current";
450 bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "output.L1.power.percent";
451 bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_B].nut_entity = "output.L2.power.percent";
452 bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_C].nut_entity = "output.L3.power.percent";
453 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_A].nut_entity = "output.L1.power";
454 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_B].nut_entity = "output.L2.power";
455 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_C].nut_entity = "output.L3.power";
456 bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.L1-N.voltage";
457 bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_B].nut_entity = "input.bypass.L2-N.voltage";
458 bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_C].nut_entity = "input.bypass.L3-N.voltage";
459 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.L1-N.voltage";
460 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.L1.current";
461 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.L1.current.nominal";
462 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.L1-N.voltage";
463 }
464 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_FREQUENCY].nut_entity = "output.frequency";
465 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_FREQUENCY].nut_entity = "input.frequency";
466 bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_FREQUENCY].nut_entity = "input.bypass.frequency";
467 bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_CURRENT].nut_entity = "battery.current";
468 bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_VOLTAGE].nut_entity = "battery.voltage";
469 bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_BATTERY_LEFT].nut_entity = "battery.charge";
470 bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TIME_REMAINING].nut_entity = "battery.runtime";
471 bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DCUV_BAR_CHART].nut_entity = "battery.voltage.low";
472 bcmxcp_meter_map[BCMXCP_METER_MAP_LOW_BATTERY_WARNING_V_BAR_CHART].nut_entity = "battery.charge.low";
473 bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DISCHARGING_CURRENT_BAR_CHART].nut_entity = "battery.current.total";
474 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_B].nut_entity = "input.L2-N.voltage";
475 bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_C].nut_entity = "input.L3-N.voltage";
476 bcmxcp_meter_map[BCMXCP_METER_MAP_AMBIENT_TEMPERATURE].nut_entity = "ambient.temperature";
477 bcmxcp_meter_map[BCMXCP_METER_MAP_HEATSINK_TEMPERATURE].nut_entity = "ups.temperature";
478 bcmxcp_meter_map[BCMXCP_METER_MAP_POWER_SUPPLY_TEMPERATURE].nut_entity = "ambient.1.temperature";
479 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B].nut_entity = "output.L2.current";
480 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C].nut_entity = "output.L3.current";
481 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B_BAR_CHART].nut_entity = "output.L2.current.nominal";
482 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C_BAR_CHART].nut_entity = "output.L3.current.nominal";
483 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].nut_entity = "ups.power.nominal";
484 bcmxcp_meter_map[BCMXCP_METER_MAP_DATE].nut_entity = "ups.date";
485 bcmxcp_meter_map[BCMXCP_METER_MAP_TIME].nut_entity = "ups.time";
486 bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TEMPERATURE].nut_entity = "battery.temperature";
487 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_B].nut_entity = "output.L2-N.voltage";
488 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_C].nut_entity = "output.L3-N.voltage";
489 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A].nut_entity = "ups.L1-N.realpower";
490 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_B].nut_entity = "ups.L2-N.realpower";
491 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_C].nut_entity = "ups.L3-N.realpower";
492 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A_B_C_BAR_CHART].nut_entity = "ups.realpower.nominal";
493 bcmxcp_meter_map[BCMXCP_METER_MAP_LINE_EVENT_COUNTER].nut_entity = "input.quality";
494 }
495
init_alarm_map()496 void init_alarm_map()
497 {
498 /* Clean entire map */
499 memset(&bcmxcp_alarm_map, 0, sizeof(BCMXCP_ALARM_MAP_ENTRY_t) * BCMXCP_ALARM_MAP_MAX);
500
501 /* Set all alarm descriptions */
502 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_OVER_VOLTAGE].alarm_desc = "INVERTER_AC_OVER_VOLTAGE";
503 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_UNDER_VOLTAGE].alarm_desc = "INVERTER_AC_UNDER_VOLTAGE";
504 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_OR_UNDER_FREQ].alarm_desc = "INVERTER_OVER_OR_UNDER_FREQ";
505 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_AC_OVER_VOLTAGE].alarm_desc = "BYPASS_AC_OVER_VOLTAGE";
506 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_AC_UNDER_VOLTAGE].alarm_desc = "BYPASS_AC_UNDER_VOLTAGE";
507 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_OVER_OR_UNDER_FREQ].alarm_desc = "BYPASS_OVER_OR_UNDER_FREQ";
508 bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_AC_OVER_VOLTAGE].alarm_desc = "INPUT_AC_OVER_VOLTAGE";
509 bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_AC_UNDER_VOLTAGE].alarm_desc = "INPUT_AC_UNDER_VOLTAGE";
510 bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_UNDER_OR_OVER_FREQ].alarm_desc = "INPUT_UNDER_OR_OVER_FREQ";
511 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_OVER_VOLTAGE].alarm_desc = "OUTPUT_OVER_VOLTAGE";
512 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNDER_VOLTAGE].alarm_desc = "OUTPUT_UNDER_VOLTAGE";
513 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNDER_OR_OVER_FREQ].alarm_desc = "OUTPUT_UNDER_OR_OVER_FREQ";
514 bcmxcp_alarm_map[BCMXCP_ALARM_REMOTE_EMERGENCY_PWR_OFF].alarm_desc = "REMOTE_EMERGENCY_PWR_OFF";
515 bcmxcp_alarm_map[BCMXCP_ALARM_REMOTE_GO_TO_BYPASS].alarm_desc = "REMOTE_GO_TO_BYPASS";
516 bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_6].alarm_desc = "BUILDING_ALARM_6";
517 bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_5].alarm_desc = "BUILDING_ALARM_5";
518 bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_4].alarm_desc = "BUILDING_ALARM_4";
519 bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_3].alarm_desc = "BUILDING_ALARM_3";
520 bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_2].alarm_desc = "BUILDING_ALARM_2";
521 bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_1].alarm_desc = "BUILDING_ALARM_1";
522 bcmxcp_alarm_map[BCMXCP_ALARM_STATIC_SWITCH_OVER_TEMP].alarm_desc = "STATIC_SWITCH_OVER_TEMP";
523 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OVER_TEMP].alarm_desc = "CHARGER_OVER_TEMP";
524 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_LOGIC_PWR_FAIL].alarm_desc = "CHARGER_LOGIC_PWR_FAIL";
525 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OVER_VOLTAGE_OR_CURRENT].alarm_desc = "CHARGER_OVER_VOLTAGE_OR_CURRENT";
526 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_TEMP].alarm_desc = "INVERTER_OVER_TEMP";
527 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_OVERLOAD].alarm_desc = "OUTPUT_OVERLOAD";
528 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_INPUT_OVER_CURRENT].alarm_desc = "RECTIFIER_INPUT_OVER_CURRENT";
529 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OUTPUT_OVER_CURRENT].alarm_desc = "INVERTER_OUTPUT_OVER_CURRENT";
530 bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_OVER_VOLTAGE].alarm_desc = "DC_LINK_OVER_VOLTAGE";
531 bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_UNDER_VOLTAGE].alarm_desc = "DC_LINK_UNDER_VOLTAGE";
532 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_FAILED].alarm_desc = "RECTIFIER_FAILED";
533 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_FAULT].alarm_desc = "INVERTER_FAULT";
534 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CONNECTOR_FAIL].alarm_desc = "BATTERY_CONNECTOR_FAIL";
535 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_BREAKER_FAIL].alarm_desc = "BYPASS_BREAKER_FAIL";
536 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_FAIL].alarm_desc = "CHARGER_FAIL";
537 bcmxcp_alarm_map[BCMXCP_ALARM_RAMP_UP_FAILED].alarm_desc = "RAMP_UP_FAILED";
538 bcmxcp_alarm_map[BCMXCP_ALARM_STATIC_SWITCH_FAILED].alarm_desc = "STATIC_SWITCH_FAILED";
539 bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_AD_REF_FAIL].alarm_desc = "ANALOG_AD_REF_FAIL";
540 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_UNCALIBRATED].alarm_desc = "BYPASS_UNCALIBRATED";
541 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_UNCALIBRATED].alarm_desc = "RECTIFIER_UNCALIBRATED";
542 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNCALIBRATED].alarm_desc = "OUTPUT_UNCALIBRATED";
543 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_UNCALIBRATED].alarm_desc = "INVERTER_UNCALIBRATED";
544 bcmxcp_alarm_map[BCMXCP_ALARM_DC_VOLT_UNCALIBRATED].alarm_desc = "DC_VOLT_UNCALIBRATED";
545 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_CURRENT_UNCALIBRATED].alarm_desc = "OUTPUT_CURRENT_UNCALIBRATED";
546 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_CURRENT_UNCALIBRATED].alarm_desc = "RECTIFIER_CURRENT_UNCALIBRATED";
547 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CURRENT_UNCALIBRATED].alarm_desc = "BATTERY_CURRENT_UNCALIBRATED";
548 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_OFF_STAT_FAIL].alarm_desc = "INVERTER_ON_OFF_STAT_FAIL";
549 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CURRENT_LIMIT].alarm_desc = "BATTERY_CURRENT_LIMIT";
550 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_STARTUP_FAIL].alarm_desc = "INVERTER_STARTUP_FAIL";
551 bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_BOARD_AD_STAT_FAIL].alarm_desc = "ANALOG_BOARD_AD_STAT_FAIL";
552 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_CURRENT_OVER_100].alarm_desc = "OUTPUT_CURRENT_OVER_100";
553 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_GROUND_FAULT].alarm_desc = "BATTERY_GROUND_FAULT";
554 bcmxcp_alarm_map[BCMXCP_ALARM_WAITING_FOR_CHARGER_SYNC].alarm_desc = "WAITING_FOR_CHARGER_SYNC";
555 bcmxcp_alarm_map[BCMXCP_ALARM_NV_RAM_FAIL].alarm_desc = "NV_RAM_FAIL";
556 bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_BOARD_AD_TIMEOUT].alarm_desc = "ANALOG_BOARD_AD_TIMEOUT";
557 bcmxcp_alarm_map[BCMXCP_ALARM_SHUTDOWN_IMMINENT].alarm_desc = "SHUTDOWN_IMMINENT";
558 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_LOW].alarm_desc = "BATTERY_LOW";
559 bcmxcp_alarm_map[BCMXCP_ALARM_UTILITY_FAIL].alarm_desc = "UTILITY_FAIL";
560 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_SHORT_CIRCUIT].alarm_desc = "OUTPUT_SHORT_CIRCUIT";
561 bcmxcp_alarm_map[BCMXCP_ALARM_UTILITY_NOT_PRESENT].alarm_desc = "UTILITY_NOT_PRESENT";
562 bcmxcp_alarm_map[BCMXCP_ALARM_FULL_TIME_CHARGING].alarm_desc = "FULL_TIME_CHARGING";
563 bcmxcp_alarm_map[BCMXCP_ALARM_FAST_BYPASS_COMMAND].alarm_desc = "FAST_BYPASS_COMMAND";
564 bcmxcp_alarm_map[BCMXCP_ALARM_AD_ERROR].alarm_desc = "AD_ERROR";
565 bcmxcp_alarm_map[BCMXCP_ALARM_INTERNAL_COM_FAIL].alarm_desc = "INTERNAL_COM_FAIL";
566 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_SELFTEST_FAIL].alarm_desc = "RECTIFIER_SELFTEST_FAIL";
567 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_EEPROM_FAIL].alarm_desc = "RECTIFIER_EEPROM_FAIL";
568 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_EPROM_FAIL].alarm_desc = "RECTIFIER_EPROM_FAIL";
569 bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_LINE_VOLTAGE_LOSS].alarm_desc = "INPUT_LINE_VOLTAGE_LOSS";
570 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_DC_OVER_VOLTAGE].alarm_desc = "BATTERY_DC_OVER_VOLTAGE";
571 bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_OVER_TEMP].alarm_desc = "POWER_SUPPLY_OVER_TEMP";
572 bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_FAIL].alarm_desc = "POWER_SUPPLY_FAIL";
573 bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_5V_FAIL].alarm_desc = "POWER_SUPPLY_5V_FAIL";
574 bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_12V_FAIL].alarm_desc = "POWER_SUPPLY_12V_FAIL";
575 bcmxcp_alarm_map[BCMXCP_ALARM_HEATSINK_OVER_TEMP].alarm_desc = "HEATSINK_OVER_TEMP";
576 bcmxcp_alarm_map[BCMXCP_ALARM_HEATSINK_TEMP_SENSOR_FAIL].alarm_desc = "HEATSINK_TEMP_SENSOR_FAIL";
577 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_CURRENT_OVER_125].alarm_desc = "RECTIFIER_CURRENT_OVER_125";
578 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_FAULT_INTERRUPT_FAIL].alarm_desc = "RECTIFIER_FAULT_INTERRUPT_FAIL";
579 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_POWER_CAPASITOR_FAIL].alarm_desc = "RECTIFIER_POWER_CAPASITOR_FAIL";
580 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PROGRAM_STACK_ERROR].alarm_desc = "INVERTER_PROGRAM_STACK_ERROR";
581 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_BOARD_SELFTEST_FAIL].alarm_desc = "INVERTER_BOARD_SELFTEST_FAIL";
582 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AD_SELFTEST_FAIL].alarm_desc = "INVERTER_AD_SELFTEST_FAIL";
583 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_RAM_SELFTEST_FAIL].alarm_desc = "INVERTER_RAM_SELFTEST_FAIL";
584 bcmxcp_alarm_map[BCMXCP_ALARM_NV_MEMORY_CHECKSUM_FAIL].alarm_desc = "NV_MEMORY_CHECKSUM_FAIL";
585 bcmxcp_alarm_map[BCMXCP_ALARM_PROGRAM_CHECKSUM_FAIL].alarm_desc = "PROGRAM_CHECKSUM_FAIL";
586 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_CPU_SELFTEST_FAIL].alarm_desc = "INVERTER_CPU_SELFTEST_FAIL";
587 bcmxcp_alarm_map[BCMXCP_ALARM_NETWORK_NOT_RESPONDING].alarm_desc = "NETWORK_NOT_RESPONDING";
588 bcmxcp_alarm_map[BCMXCP_ALARM_FRONT_PANEL_SELFTEST_FAIL].alarm_desc = "FRONT_PANEL_SELFTEST_FAIL";
589 bcmxcp_alarm_map[BCMXCP_ALARM_NODE_EEPROM_VERIFICATION_ERROR].alarm_desc = "NODE_EEPROM_VERIFICATION_ERROR";
590 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_AC_OVER_VOLT_TEST_FAIL].alarm_desc = "OUTPUT_AC_OVER_VOLT_TEST_FAIL";
591 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_DC_OVER_VOLTAGE].alarm_desc = "OUTPUT_DC_OVER_VOLTAGE";
592 bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_PHASE_ROTATION_ERROR].alarm_desc = "INPUT_PHASE_ROTATION_ERROR";
593 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_RAMP_UP_TEST_FAILED].alarm_desc = "INVERTER_RAMP_UP_TEST_FAILED";
594 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OFF_COMMAND].alarm_desc = "INVERTER_OFF_COMMAND";
595 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_COMMAND].alarm_desc = "INVERTER_ON_COMMAND";
596 bcmxcp_alarm_map[BCMXCP_ALARM_TO_BYPASS_COMMAND].alarm_desc = "TO_BYPASS_COMMAND";
597 bcmxcp_alarm_map[BCMXCP_ALARM_FROM_BYPASS_COMMAND].alarm_desc = "FROM_BYPASS_COMMAND";
598 bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_MODE_COMMAND].alarm_desc = "AUTO_MODE_COMMAND";
599 bcmxcp_alarm_map[BCMXCP_ALARM_EMERGENCY_SHUTDOWN_COMMAND].alarm_desc = "EMERGENCY_SHUTDOWN_COMMAND";
600 bcmxcp_alarm_map[BCMXCP_ALARM_SETUP_SWITCH_OPEN].alarm_desc = "SETUP_SWITCH_OPEN";
601 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_VOLT_INT].alarm_desc = "INVERTER_OVER_VOLT_INT";
602 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_UNDER_VOLT_INT].alarm_desc = "INVERTER_UNDER_VOLT_INT";
603 bcmxcp_alarm_map[BCMXCP_ALARM_ABSOLUTE_DCOV_ACOV].alarm_desc = "ABSOLUTE_DCOV_ACOV";
604 bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_A_CURRENT_LIMIT].alarm_desc = "PHASE_A_CURRENT_LIMIT";
605 bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_B_CURRENT_LIMIT].alarm_desc = "PHASE_B_CURRENT_LIMIT";
606 bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_C_CURRENT_LIMIT].alarm_desc = "PHASE_C_CURRENT_LIMIT";
607 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_NOT_AVAILABLE].alarm_desc = "BYPASS_NOT_AVAILABLE";
608 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_BREAKER_OPEN].alarm_desc = "RECTIFIER_BREAKER_OPEN";
609 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CONTACTOR_OPEN].alarm_desc = "BATTERY_CONTACTOR_OPEN";
610 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_CONTACTOR_OPEN].alarm_desc = "INVERTER_CONTACTOR_OPEN";
611 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_BREAKER_OPEN].alarm_desc = "BYPASS_BREAKER_OPEN";
612 bcmxcp_alarm_map[BCMXCP_ALARM_INV_BOARD_ACOV_INT_TEST_FAIL].alarm_desc = "INV_BOARD_ACOV_INT_TEST_FAIL";
613 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_TEMP_TRIP].alarm_desc = "INVERTER_OVER_TEMP_TRIP";
614 bcmxcp_alarm_map[BCMXCP_ALARM_INV_BOARD_ACUV_INT_TEST_FAIL].alarm_desc = "INV_BOARD_ACUV_INT_TEST_FAIL";
615 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_FEEDBACK_ERROR].alarm_desc = "INVERTER_VOLTAGE_FEEDBACK_ERROR";
616 bcmxcp_alarm_map[BCMXCP_ALARM_DC_UNDER_VOLTAGE_TIMEOUT].alarm_desc = "DC_UNDER_VOLTAGE_TIMEOUT";
617 bcmxcp_alarm_map[BCMXCP_ALARM_AC_UNDER_VOLTAGE_TIMEOUT].alarm_desc = "AC_UNDER_VOLTAGE_TIMEOUT";
618 bcmxcp_alarm_map[BCMXCP_ALARM_DC_UNDER_VOLTAGE_WHILE_CHARGE].alarm_desc = "DC_UNDER_VOLTAGE_WHILE_CHARGE";
619 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR].alarm_desc = "INVERTER_VOLTAGE_BIAS_ERROR";
620 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_PHASE_ROTATION].alarm_desc = "RECTIFIER_PHASE_ROTATION";
621 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_PHASER_ROTATION].alarm_desc = "BYPASS_PHASER_ROTATION";
622 bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_INTERFACE_BOARD_FAIL].alarm_desc = "SYSTEM_INTERFACE_BOARD_FAIL";
623 bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_BOARD_FAIL].alarm_desc = "PARALLEL_BOARD_FAIL";
624 bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_A].alarm_desc = "LOST_LOAD_SHARING_PHASE_A";
625 bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_B].alarm_desc = "LOST_LOAD_SHARING_PHASE_B";
626 bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_C].alarm_desc = "LOST_LOAD_SHARING_PHASE_C";
627 bcmxcp_alarm_map[BCMXCP_ALARM_DC_OVER_VOLTAGE_TIMEOUT].alarm_desc = "DC_OVER_VOLTAGE_TIMEOUT";
628 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TOTALLY_DISCHARGED].alarm_desc = "BATTERY_TOTALLY_DISCHARGED";
629 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PHASE_BIAS_ERROR].alarm_desc = "INVERTER_PHASE_BIAS_ERROR";
630 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR_2].alarm_desc = "INVERTER_VOLTAGE_BIAS_ERROR_2";
631 bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_BLEED_COMPLETE].alarm_desc = "DC_LINK_BLEED_COMPLETE";
632 bcmxcp_alarm_map[BCMXCP_ALARM_LARGE_CHARGER_INPUT_CURRENT].alarm_desc = "LARGE_CHARGER_INPUT_CURRENT";
633 bcmxcp_alarm_map[BCMXCP_ALARM_INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL].alarm_desc = "INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL";
634 bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_REDUNDANCY].alarm_desc = "LOSS_OF_REDUNDANCY";
635 bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_SYNC_BUS].alarm_desc = "LOSS_OF_SYNC_BUS";
636 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_BREAKER_SHUNT_TRIP].alarm_desc = "RECTIFIER_BREAKER_SHUNT_TRIP";
637 bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_CHARGER_SYNC].alarm_desc = "LOSS_OF_CHARGER_SYNC";
638 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_LOW_LEVEL_TEST_TIMEOUT].alarm_desc = "INVERTER_LOW_LEVEL_TEST_TIMEOUT";
639 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_BREAKER_OPEN].alarm_desc = "OUTPUT_BREAKER_OPEN";
640 bcmxcp_alarm_map[BCMXCP_ALARM_CONTROL_POWER_ON].alarm_desc = "CONTROL_POWER_ON";
641 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON].alarm_desc = "INVERTER_ON";
642 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON].alarm_desc = "CHARGER_ON";
643 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_ON].alarm_desc = "BYPASS_ON";
644 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_POWER_LOSS].alarm_desc = "BYPASS_POWER_LOSS";
645 bcmxcp_alarm_map[BCMXCP_ALARM_ON_MANUAL_BYPASS].alarm_desc = "ON_MANUAL_BYPASS";
646 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_MANUAL_TURN_OFF].alarm_desc = "BYPASS_MANUAL_TURN_OFF";
647 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_BLEEDING_DC_LINK_VOLT].alarm_desc = "INVERTER_BLEEDING_DC_LINK_VOLT";
648 bcmxcp_alarm_map[BCMXCP_ALARM_CPU_ISR_ERROR].alarm_desc = "CPU_ISR_ERROR";
649 bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_ISR_RESTART].alarm_desc = "SYSTEM_ISR_RESTART";
650 bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_DC].alarm_desc = "PARALLEL_DC";
651 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_NEEDS_SERVICE].alarm_desc = "BATTERY_NEEDS_SERVICE";
652 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CHARGING].alarm_desc = "BATTERY_CHARGING";
653 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_NOT_CHARGED].alarm_desc = "BATTERY_NOT_CHARGED";
654 bcmxcp_alarm_map[BCMXCP_ALARM_DISABLED_BATTERY_TIME].alarm_desc = "DISABLED_BATTERY_TIME";
655 bcmxcp_alarm_map[BCMXCP_ALARM_SERIES_7000_ENABLE].alarm_desc = "SERIES_7000_ENABLE";
656 bcmxcp_alarm_map[BCMXCP_ALARM_OTHER_UPS_ON].alarm_desc = "OTHER_UPS_ON";
657 bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_INVERTER].alarm_desc = "PARALLEL_INVERTER";
658 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_IN_PARALLEL].alarm_desc = "UPS_IN_PARALLEL";
659 bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_BREAKER_REALY_FAIL].alarm_desc = "OUTPUT_BREAKER_REALY_FAIL";
660 bcmxcp_alarm_map[BCMXCP_ALARM_CONTROL_POWER_OFF].alarm_desc = "CONTROL_POWER_OFF";
661 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_A";
662 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_B";
663 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_C";
664 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_A";
665 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_B";
666 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_C";
667 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_A";
668 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_B";
669 bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_C";
670 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_BATTERY].alarm_desc = "UPS_ON_BATTERY";
671 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_BYPASS].alarm_desc = "UPS_ON_BYPASS";
672 bcmxcp_alarm_map[BCMXCP_ALARM_LOAD_DUMPED].alarm_desc = "LOAD_DUMPED";
673 bcmxcp_alarm_map[BCMXCP_ALARM_LOAD_ON_INVERTER].alarm_desc = "LOAD_ON_INVERTER";
674 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_COMMAND].alarm_desc = "UPS_ON_COMMAND";
675 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_OFF_COMMAND].alarm_desc = "UPS_OFF_COMMAND";
676 bcmxcp_alarm_map[BCMXCP_ALARM_LOW_BATTERY_SHUTDOWN].alarm_desc = "LOW_BATTERY_SHUTDOWN";
677 bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_ON_ENABLED].alarm_desc = "AUTO_ON_ENABLED";
678 bcmxcp_alarm_map[BCMXCP_ALARM_SOFTWARE_INCOMPABILITY_DETECTED].alarm_desc = "SOFTWARE_INCOMPABILITY_DETECTED";
679 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_TEMP_SENSOR_FAILED].alarm_desc = "INVERTER_TEMP_SENSOR_FAILED";
680 bcmxcp_alarm_map[BCMXCP_ALARM_DC_START_OCCURED].alarm_desc = "DC_START_OCCURED";
681 bcmxcp_alarm_map[BCMXCP_ALARM_IN_PARALLEL_OPERATION].alarm_desc = "IN_PARALLEL_OPERATION";
682 bcmxcp_alarm_map[BCMXCP_ALARM_SYNCING_TO_BYPASS].alarm_desc = "SYNCING_TO_BYPASS";
683 bcmxcp_alarm_map[BCMXCP_ALARM_RAMPING_UPS_UP].alarm_desc = "RAMPING_UPS_UP";
684 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_DELAY].alarm_desc = "INVERTER_ON_DELAY";
685 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON_DELAY].alarm_desc = "CHARGER_ON_DELAY";
686 bcmxcp_alarm_map[BCMXCP_ALARM_WAITING_FOR_UTIL_INPUT].alarm_desc = "WAITING_FOR_UTIL_INPUT";
687 bcmxcp_alarm_map[BCMXCP_ALARM_CLOSE_BYPASS_BREAKER].alarm_desc = "CLOSE_BYPASS_BREAKER";
688 bcmxcp_alarm_map[BCMXCP_ALARM_TEMPORARY_BYPASS_OPERATION].alarm_desc = "TEMPORARY_BYPASS_OPERATION";
689 bcmxcp_alarm_map[BCMXCP_ALARM_SYNCING_TO_OUTPUT].alarm_desc = "SYNCING_TO_OUTPUT";
690 bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_FAILURE].alarm_desc = "BYPASS_FAILURE";
691 bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_OFF_COMMAND_EXECUTED].alarm_desc = "AUTO_OFF_COMMAND_EXECUTED";
692 bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_ON_COMMAND_EXECUTED].alarm_desc = "AUTO_ON_COMMAND_EXECUTED";
693 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_FAILED].alarm_desc = "BATTERY_TEST_FAILED";
694 bcmxcp_alarm_map[BCMXCP_ALARM_FUSE_FAIL].alarm_desc = "FUSE_FAIL";
695 bcmxcp_alarm_map[BCMXCP_ALARM_FAN_FAIL].alarm_desc = "FAN_FAIL";
696 bcmxcp_alarm_map[BCMXCP_ALARM_SITE_WIRING_FAULT].alarm_desc = "SITE_WIRING_FAULT";
697 bcmxcp_alarm_map[BCMXCP_ALARM_BACKFEED_CONTACTOR_FAIL].alarm_desc = "BACKFEED_CONTACTOR_FAIL";
698 bcmxcp_alarm_map[BCMXCP_ALARM_ON_BUCK].alarm_desc = "ON_BUCK";
699 bcmxcp_alarm_map[BCMXCP_ALARM_ON_BOOST].alarm_desc = "ON_BOOST";
700 bcmxcp_alarm_map[BCMXCP_ALARM_ON_DOUBLE_BOOST].alarm_desc = "ON_DOUBLE_BOOST";
701 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERIES_DISCONNECTED].alarm_desc = "BATTERIES_DISCONNECTED";
702 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_CABINET_OVER_TEMP].alarm_desc = "UPS_CABINET_OVER_TEMP";
703 bcmxcp_alarm_map[BCMXCP_ALARM_TRANSFORMER_OVER_TEMP].alarm_desc = "TRANSFORMER_OVER_TEMP";
704 bcmxcp_alarm_map[BCMXCP_ALARM_AMBIENT_UNDER_TEMP].alarm_desc = "AMBIENT_UNDER_TEMP";
705 bcmxcp_alarm_map[BCMXCP_ALARM_AMBIENT_OVER_TEMP].alarm_desc = "AMBIENT_OVER_TEMP";
706 bcmxcp_alarm_map[BCMXCP_ALARM_CABINET_DOOR_OPEN].alarm_desc = "CABINET_DOOR_OPEN";
707 bcmxcp_alarm_map[BCMXCP_ALARM_CABINET_DOOR_OPEN_VOLT_PRESENT].alarm_desc = "CABINET_DOOR_OPEN_VOLT_PRESENT";
708 bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_SHUTDOWN_PENDING].alarm_desc = "AUTO_SHUTDOWN_PENDING";
709 bcmxcp_alarm_map[BCMXCP_ALARM_TAP_SWITCHING_REALY_PENDING].alarm_desc = "TAP_SWITCHING_REALY_PENDING";
710 bcmxcp_alarm_map[BCMXCP_ALARM_UNABLE_TO_CHARGE_BATTERIES].alarm_desc = "UNABLE_TO_CHARGE_BATTERIES";
711 bcmxcp_alarm_map[BCMXCP_ALARM_STARTUP_FAILURE_CHECK_EPO].alarm_desc = "STARTUP_FAILURE_CHECK_EPO";
712 bcmxcp_alarm_map[BCMXCP_ALARM_AUTOMATIC_STARTUP_PENDING].alarm_desc = "AUTOMATIC_STARTUP_PENDING";
713 bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_FAILED].alarm_desc = "MODEM_FAILED";
714 bcmxcp_alarm_map[BCMXCP_ALARM_INCOMING_MODEM_CALL_STARTED].alarm_desc = "INCOMING_MODEM_CALL_STARTED";
715 bcmxcp_alarm_map[BCMXCP_ALARM_OUTGOING_MODEM_CALL_STARTED].alarm_desc = "OUTGOING_MODEM_CALL_STARTED";
716 bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CONNECTION_ESTABLISHED].alarm_desc = "MODEM_CONNECTION_ESTABLISHED";
717 bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CALL_COMPLETED_SUCCESS].alarm_desc = "MODEM_CALL_COMPLETED_SUCCESS";
718 bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CALL_COMPLETED_FAIL].alarm_desc = "MODEM_CALL_COMPLETED_FAIL";
719 bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_BREAKER_FAIL].alarm_desc = "INPUT_BREAKER_FAIL";
720 bcmxcp_alarm_map[BCMXCP_ALARM_SYSINIT_IN_PROGRESS].alarm_desc = "SYSINIT_IN_PROGRESS";
721 bcmxcp_alarm_map[BCMXCP_ALARM_AUTOCALIBRATION_FAIL].alarm_desc = "AUTOCALIBRATION_FAIL";
722 bcmxcp_alarm_map[BCMXCP_ALARM_SELECTIVE_TRIP_OF_MODULE].alarm_desc = "SELECTIVE_TRIP_OF_MODULE";
723 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OUTPUT_FAILURE].alarm_desc = "INVERTER_OUTPUT_FAILURE";
724 bcmxcp_alarm_map[BCMXCP_ALARM_ABNORMAL_OUTPUT_VOLT_AT_STARTUP].alarm_desc = "ABNORMAL_OUTPUT_VOLT_AT_STARTUP";
725 bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_OVER_TEMP].alarm_desc = "RECTIFIER_OVER_TEMP";
726 bcmxcp_alarm_map[BCMXCP_ALARM_CONFIG_ERROR].alarm_desc = "CONFIG_ERROR";
727 bcmxcp_alarm_map[BCMXCP_ALARM_REDUNDANCY_LOSS_DUE_TO_OVERLOAD].alarm_desc = "REDUNDANCY_LOSS_DUE_TO_OVERLOAD";
728 bcmxcp_alarm_map[BCMXCP_ALARM_ON_ALTERNATE_AC_SOURCE].alarm_desc = "ON_ALTERNATE_AC_SOURCE";
729 bcmxcp_alarm_map[BCMXCP_ALARM_IN_HIGH_EFFICIENCY_MODE].alarm_desc = "IN_HIGH_EFFICIENCY_MODE";
730 bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_NOTICE_ACTIVE].alarm_desc = "SYSTEM_NOTICE_ACTIVE";
731 bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_ALARM_ACTIVE].alarm_desc = "SYSTEM_ALARM_ACTIVE";
732 bcmxcp_alarm_map[BCMXCP_ALARM_ALTERNATE_POWER_SOURCE_NOT_AVAILABLE].alarm_desc = "ALTERNATE_POWER_SOURCE_NOT_AVAILABLE";
733 bcmxcp_alarm_map[BCMXCP_ALARM_CURRENT_BALANCE_FAILURE].alarm_desc = "CURRENT_BALANCE_FAILURE";
734 bcmxcp_alarm_map[BCMXCP_ALARM_CHECK_AIR_FILTER].alarm_desc = "CHECK_AIR_FILTER";
735 bcmxcp_alarm_map[BCMXCP_ALARM_SUBSYSTEM_NOTICE_ACTIVE].alarm_desc = "SUBSYSTEM_NOTICE_ACTIVE";
736 bcmxcp_alarm_map[BCMXCP_ALARM_SUBSYSTEM_ALARM_ACTIVE].alarm_desc = "SUBSYSTEM_ALARM_ACTIVE";
737 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON_COMMAND].alarm_desc = "CHARGER_ON_COMMAND";
738 bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OFF_COMMAND].alarm_desc = "CHARGER_OFF_COMMAND";
739 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_NORMAL].alarm_desc = "UPS_NORMAL";
740 bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PHASE_ROTATION].alarm_desc = "INVERTER_PHASE_ROTATION";
741 bcmxcp_alarm_map[BCMXCP_ALARM_UPS_OFF].alarm_desc = "UPS_OFF";
742 bcmxcp_alarm_map[BCMXCP_ALARM_EXTERNAL_COMMUNICATION_FAILURE].alarm_desc = "EXTERNAL_COMMUNICATION_FAILURE";
743 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_INPROGRESS].alarm_desc = "BATTERY_TEST_INPROGRESS";
744 bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_TEST_INPROGRESS].alarm_desc = "SYSTEM_TEST_INPROGRESS";
745 bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_ABORTED].alarm_desc = "BATTERY_TEST_ABORTED";
746 }
747
748 /* Get information on UPS commands */
init_command(int size)749 bool_t init_command(int size)
750 {
751 unsigned char answer[PW_ANSWER_MAX_SIZE];
752 unsigned char commandByte;
753 const char* nutvalue;
754 int res, iIndex = 0, ncounter, NumComms = 0, i;
755
756 upsdebugx(1, "entering init_command(%i)", size);
757
758 res = command_read_sequence(PW_COMMAND_LIST_REQ, answer);
759 if (res <= 0)
760 {
761 upsdebugx(2, "No command list block.");
762 return FALSE;
763 }
764 else
765 {
766 upsdebugx(2, "Command list block supported.");
767
768 res = answer[iIndex];
769 NumComms = (int)res; /* Number of commands implemented in this UPS */
770 upsdebugx(3, "Number of commands implemented in ups %d", res);
771 iIndex++;
772 res = answer[iIndex]; /* Entry length - bytes reported for each command */
773 iIndex++;
774 upsdebugx(5, "bytes per command %d", res);
775
776 /* In case of debug - make explanation of values */
777 upsdebugx(2, "Index\tCmd byte\tDescription");
778
779 /* Get command bytes if size of command block matches with size from standard ID block */
780 if (NumComms + 2 == size)
781 {
782 for (ncounter = 0; ncounter < NumComms; ncounter++)
783 {
784 commandByte = answer[iIndex];
785 if(commandByte >= 0 && commandByte < BCMXCP_COMMAND_MAP_MAX) {
786 upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, bcmxcp_command_map[commandByte].command_desc);
787 bcmxcp_command_map[commandByte].command_byte = commandByte;
788 }
789 else {
790 upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, "Unknown command, the commandByte is not mapped");
791 }
792
793 iIndex++;
794 }
795
796 /* Map supported commands to instcmd */
797 for(i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) {
798 if(bcmxcp_command_map[i].command_desc != NULL) {
799 if(bcmxcp_command_map[i].command_byte > 0) {
800 if ((nutvalue = nut_find_infoval(command_map_info, bcmxcp_command_map[i].command_byte, FALSE)) != NULL) {
801 dstate_addcmd(nutvalue);
802 upsdebugx(2, "Added support for instcmd %s", nutvalue);
803 }
804 }
805 }
806 }
807
808 return TRUE;
809 }
810 else {
811 upsdebugx(1, "Invalid response received from Command List block");
812 return FALSE;
813 }
814 }
815 }
816
init_ups_meter_map(const unsigned char * map,unsigned char len)817 void init_ups_meter_map(const unsigned char *map, unsigned char len)
818 {
819 unsigned int iIndex, iOffset = 0;
820
821 /* In case of debug - make explanation of values */
822 upsdebugx(2, "Index\tOffset\tFormat\tNUT");
823
824 /* Loop thru map */
825 for (iIndex = 0; iIndex < len && iIndex < BCMXCP_METER_MAP_MAX; iIndex++)
826 {
827 bcmxcp_meter_map[iIndex].format = map[iIndex];
828 if (map[iIndex] != 0)
829 {
830 /* Set meter map entry offset */
831 bcmxcp_meter_map[iIndex].meter_block_index = iOffset;
832
833 /* Debug info */
834 upsdebugx(2, "%04d\t%04d\t%2x\t%s", iIndex, iOffset, bcmxcp_meter_map[iIndex].format,
835 (bcmxcp_meter_map[iIndex].nut_entity == NULL ? "None" :bcmxcp_meter_map[iIndex].nut_entity));
836
837 iOffset += 4;
838 }
839 }
840 upsdebugx(2, "\n");
841 }
842
decode_meter_map_entry(const unsigned char * entry,const unsigned char format,char * value)843 void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value)
844 {
845 long lValue = 0;
846 char sFormat[32];
847 float fValue;
848 unsigned char dd, mm, yy, cc, hh, ss;
849
850 /* Paranoid input sanity checks */
851 if (value == NULL)
852 return;
853 *value = '\0';
854 if (entry == (unsigned char *)NULL || format == 0x00)
855 return;
856
857 /* Get data based on format */
858 if (format == 0xf0) {
859 /* Long integer */
860 lValue = get_long(entry);
861 snprintf(value, 127, "%d", (int)lValue);
862 }
863 else if ((format & 0xf0) == 0xf0) {
864 /* Fixed point integer */
865 fValue = get_long(entry) / ldexp(1, format & 0x0f);
866 snprintf(value, 127, "%.2f", fValue);
867 }
868 else if (format <= 0x97) {
869 /* Floating point */
870 fValue = get_float(entry);
871 /* Format is packed BCD */
872 snprintf(sFormat, 31, "%%%d.%df", ((format & 0xf0) >> 4), (format & 0x0f));
873 snprintf(value, 127, sFormat, fValue);
874 }
875 else if (format == 0xe2) {
876 /* Seconds */
877 lValue = get_long(entry);
878 snprintf(value, 127, "%d", (int)lValue);
879 }
880 else if (format == 0xe0) {
881 /* Date */
882 /* Format is packed BCD for each byte, and cc uses most signifcant bit to signal date format */
883 dd = entry[0];
884 mm = entry[1];
885 yy = entry[2];
886 cc = entry[3];
887
888 /* Check format type */
889 if (cc & 0x80) {
890 /* Month:Day format */
891 snprintf(value, 127, "%d%d/%d%d/%d%d%d%d", ((dd & 0xf0) >> 4), (dd & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f));
892 }
893 else {
894 /* Julian format */
895 /* TODO test this, unsure if the day part is correct, i.e. how we use the two bytes mm and dd to calculate the number of julian days */
896 snprintf(value, 127, "%d%d%d%d:%d%d%d", (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f), (mm & 0x0f), ((dd & 0xf0) >> 4), (dd & 0x0f));
897 }
898 }
899 else if (format == 0xe1) {
900 /* Time */
901 /* Format is packed BCD for each byte */
902 cc = entry[0];
903 ss = entry[1];
904 mm = entry[2];
905 hh = entry[3];
906
907 snprintf(value, 127, "%d%d:%d%d:%d%d.%d%d", ((hh & 0xf0) >> 4), (hh & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), ((ss & 0xf0) >> 4), (ss & 0x0f), ((cc & 0xf0) >> 4), (cc & 0x0f));
908 }
909 else {
910 /* Unknown format */
911 snprintf(value, 127, "???");
912 return;
913 }
914 return;
915 }
916
917
init_ups_alarm_map(const unsigned char * map,unsigned char len)918 void init_ups_alarm_map(const unsigned char *map, unsigned char len)
919 {
920 unsigned int iIndex = 0;
921 int alarm = 0;
922
923 /* In case of debug - make explanation of values */
924 upsdebugx(2, "Index\tAlarm\tSupported");
925
926 /* Loop thru map */
927 for (iIndex = 0; iIndex < len && iIndex < BCMXCP_ALARM_MAP_MAX / 8; iIndex++)
928 {
929 /* Bit 0 */
930 if(set_alarm_support_in_alarm_map(map, iIndex, 0x01, iIndex * 8, alarm) == TRUE)
931 alarm++;
932 /* Bit 1 */
933 if(set_alarm_support_in_alarm_map(map, iIndex, 0x02, iIndex * 8 + 1, alarm) == TRUE)
934 alarm++;
935 /* Bit 2 */
936 if(set_alarm_support_in_alarm_map(map, iIndex, 0x04, iIndex * 8 + 2, alarm) == TRUE)
937 alarm++;
938 /* Bit 3 */
939 if(set_alarm_support_in_alarm_map(map, iIndex, 0x08, iIndex * 8 + 3, alarm) == TRUE)
940 alarm++;
941 /* Bit 4 */
942 if(set_alarm_support_in_alarm_map(map, iIndex, 0x10, iIndex * 8 + 4, alarm) == TRUE)
943 alarm++;
944 /* Bit 5 */
945 if(set_alarm_support_in_alarm_map(map, iIndex, 0x20, iIndex * 8 + 5, alarm) == TRUE)
946 alarm++;
947 /* Bit 6 */
948 if(set_alarm_support_in_alarm_map(map, iIndex, 0x40, iIndex * 8 + 6, alarm) == TRUE)
949 alarm++;
950 /* Bit 7 */
951 if(set_alarm_support_in_alarm_map(map, iIndex, 0x80, iIndex * 8 + 7, alarm) == TRUE)
952 alarm++;
953 }
954 upsdebugx(2, "\n");
955 }
956
set_alarm_support_in_alarm_map(const unsigned char * map,const int mapIndex,const int bitmask,const int alarmMapIndex,const int alarmBlockIndex)957 bool_t set_alarm_support_in_alarm_map(const unsigned char *map, const int mapIndex, const int bitmask, const int alarmMapIndex, const int alarmBlockIndex) {
958 /* Check what the alarm block tells about the support for the alarm */
959 if (map[mapIndex] & bitmask)
960 {
961 /* Set alarm active */
962 bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = alarmBlockIndex;
963 }
964 else
965 {
966 /* Set alarm inactive */
967 bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = -1;
968 }
969
970 /* Return if the alarm was supported or not */
971 if(bcmxcp_alarm_map[alarmMapIndex].alarm_block_index >= 0) {
972 /* Debug info */
973 upsdebugx(2, "%04d\t%s\tYes", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc);
974 return TRUE;
975 }
976 else {
977 /* Debug info */
978 upsdebugx(3, "%04d\t%s\tNo", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc);
979 return FALSE;
980 }
981 }
982
init_outlet(unsigned char len)983 int init_outlet(unsigned char len)
984 {
985 unsigned char answer[PW_ANSWER_MAX_SIZE];
986 int iIndex = 0, res, num;
987 int num_outlet, size_outlet;
988 int outlet_num, outlet_state;
989 short auto_dly_off, auto_dly_on;
990 char outlet_name[25];
991
992 res = command_read_sequence(PW_OUT_MON_BLOCK_REQ, answer);
993 if (res <= 0)
994 fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
995 else
996 upsdebugx(1, "init_outlet(%i), res=%i", len, res);
997
998 num_outlet = answer[iIndex++];
999 upsdebugx(2, "Number of outlets: %d", num_outlet);
1000
1001 size_outlet = answer[iIndex++];
1002 upsdebugx(2, "Number of bytes: %d", size_outlet);
1003
1004 for(num = 1 ; num <= num_outlet ; num++) {
1005 outlet_num = answer[iIndex++];
1006 upsdebugx(2, "Outlet number: %d", outlet_num);
1007 snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.id", num);
1008 dstate_setinfo(outlet_name, "%d", outlet_num);
1009
1010 outlet_state = answer[iIndex++];
1011 upsdebugx(2, "Outlet state: %d", outlet_state);
1012 snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.status", num);
1013 if (outlet_state>0 && outlet_state <9 )
1014 dstate_setinfo(outlet_name, "%s", OutletStatus[outlet_state] );
1015
1016 auto_dly_off = get_word(answer+iIndex);
1017 iIndex += 2;
1018 upsdebugx(2, "Auto delay off: %d", auto_dly_off);
1019 snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.shutdown", num);
1020 dstate_setinfo(outlet_name, "%d", auto_dly_off);
1021 dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING);
1022 dstate_setaux(outlet_name, 5);
1023
1024 auto_dly_on = get_word(answer+iIndex);
1025 iIndex += 2;
1026 upsdebugx(2, "Auto delay on: %d", auto_dly_on);
1027 snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.start", num);
1028 dstate_setinfo(outlet_name, "%d", auto_dly_on);
1029 dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING);
1030 dstate_setaux(outlet_name, 5);
1031 }
1032
1033 return num_outlet;
1034 }
1035
init_ext_vars(void)1036 void init_ext_vars(void)
1037 {
1038 unsigned char answer[PW_ANSWER_MAX_SIZE],cbuf[5];
1039 int length=0,index=0;
1040
1041 send_write_command(AUTHOR, 4);
1042
1043 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1044
1045 cbuf[0] = PW_SET_CONF_COMMAND;
1046 cbuf[1] = PW_CONF_REQ;
1047 cbuf[2] = 0x0;
1048 cbuf[3] = 0x0;
1049
1050 length=command_write_sequence(cbuf,4,answer);
1051 if (length <= 0)
1052 fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1053 if (length < 4) //UPS dont have configurable vars
1054 return;
1055 for( index=3; index < length; index++) {
1056 switch(answer[index]){
1057 case PW_CONF_LOW_DEV_LIMIT: dstate_setinfo("input.transfer.boost.high","%d",0);
1058 dstate_setflags("input.transfer.boost.high", ST_FLAG_RW | ST_FLAG_STRING);
1059 dstate_setaux("input.transfer.boost.high", 3);
1060 break;
1061
1062 case PW_CONF_HIGH_DEV_LIMIT: dstate_setinfo("input.transfer.trim.low","%d",0);
1063 dstate_setflags("input.transfer.trim.low", ST_FLAG_RW | ST_FLAG_STRING);
1064 dstate_setaux("input.transfer.trim.low", 3);
1065 break;
1066
1067 case PW_CONF_LOW_BATT: dstate_setinfo("battery.runtime.low","%d",0);
1068 dstate_setflags("battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING);
1069 dstate_setaux("battery.runtime.low", 2);
1070 break;
1071
1072 case PW_CONF_BEEPER: dstate_addcmd("beeper.disable");
1073 dstate_addcmd("beeper.enable");
1074 dstate_addcmd("beeper.mute");
1075 break;
1076
1077 case PW_CONF_RETURN_DELAY: dstate_setinfo("input.transfer.delay","%d",0);
1078 dstate_setflags("input.transfer.delay", ST_FLAG_RW | ST_FLAG_STRING);
1079 dstate_setaux("input.transfer.delay", 5);
1080 break;
1081
1082 case PW_CONF_RETURN_CAP: dstate_setinfo("battery.charge.restart","%d",0);
1083 dstate_setflags("battery.charge.restart", ST_FLAG_RW | ST_FLAG_STRING);
1084 dstate_setaux("battery.charge.restart", 3);
1085 break;
1086
1087 case PW_CONF_MAX_TEMP: dstate_setinfo("ambient.temperature.high","%d",0);
1088 dstate_setflags("ambient.temperature.high", ST_FLAG_RW | ST_FLAG_STRING);
1089 dstate_setaux("ambient.temperature.high", 3);
1090 break;
1091
1092 case PW_CONF_NOMINAL_OUT_VOLTAGE: dstate_setinfo("output.voltage.nominal","%d",0);
1093 dstate_setflags("output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING);
1094 dstate_setaux("output.voltage.nominal", 3);
1095 break;
1096
1097 case PW_CONF_SLEEP_TH_LOAD: dstate_setinfo("battery.energysave.load","%d",0);
1098 dstate_setflags("battery.energysave.load", ST_FLAG_RW | ST_FLAG_STRING);
1099 dstate_setaux("battery.energysave.load", 3);
1100 break;
1101
1102 case PW_CONF_SLEEP_DELAY: dstate_setinfo("battery.energysave.delay","%d",0);
1103 dstate_setflags("battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING);
1104 dstate_setaux("battery.energysave.delay", 3);
1105 break;
1106
1107 case PW_CONF_BATT_STRINGS: dstate_setinfo("battery.packs","%d",0);
1108 dstate_setflags("battery.packs", ST_FLAG_RW | ST_FLAG_STRING);
1109 dstate_setaux("battery.packs", 1);
1110 break;
1111
1112 }
1113 }
1114 }
1115
1116
init_config(void)1117 void init_config(void)
1118 {
1119 unsigned char answer[PW_ANSWER_MAX_SIZE];
1120 int voltage = 0, frequency = 0, res, tmp=0;
1121 char sValue[17];
1122 char sPartNumber[17];
1123
1124 res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer);
1125 if (res <= 0)
1126 fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1127
1128 /* Get validation mask for status bitmap */
1129 bcmxcp_status.topology_mask = answer[BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_BYTE3];
1130
1131 /* Nominal output voltage of ups */
1132 voltage = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE));
1133 if (voltage != 0)
1134 dstate_setinfo("output.voltage.nominal", "%d", voltage);
1135
1136 /* Nominal Output Frequency */
1137 frequency = get_word((answer+BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_FREQ));
1138 if (frequency != 0)
1139 dstate_setinfo("output.frequency.nominal", "%d", frequency);
1140
1141 /*Number of EBM*/
1142 tmp = (int) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3);
1143 if (tmp != 0)
1144 dstate_setinfo("battery.packs", "%d", tmp);
1145
1146 /* UPS serial number */
1147 snprintf(sValue, sizeof(sValue), "%s", answer + BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER);
1148 if(sValue[0] != '\0')
1149 dstate_setinfo("ups.serial", "%s", sValue);
1150
1151 /* UPS Part Number*/
1152 snprintf(sPartNumber, sizeof(sPartNumber), "%s", answer + BCMXCP_CONFIG_BLOCK_PART_NUMBER);
1153 if(sPartNumber[0] != '\0')
1154 dstate_setinfo("device.part", "%s", sPartNumber);
1155 }
1156
init_limit(void)1157 void init_limit(void)
1158 {
1159 unsigned char answer[PW_ANSWER_MAX_SIZE];
1160 int value, res;
1161
1162 res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer);
1163 if (res <= 0) {
1164 fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1165 }
1166
1167 /* Nominal input voltage */
1168 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE));
1169 if (value != 0) {
1170 dstate_setinfo("input.voltage.nominal", "%d", value);
1171 }
1172
1173 /* Nominal input frequency */
1174 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ));
1175 if (value != 0) {
1176 int fnom = value;
1177 dstate_setinfo("input.frequency.nominal", "%d", value);
1178
1179 /* Input frequency deviation */
1180 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_FREQ_DEV_LIMIT));
1181
1182 if (value != 0) {
1183 value /= 100;
1184 dstate_setinfo("input.frequency.low", "%d", fnom - value);
1185 dstate_setinfo("input.frequency.high", "%d", fnom + value);
1186 }
1187 }
1188
1189 /* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */
1190 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT));
1191 if (value != 0) {
1192 dstate_setinfo("input.transfer.boost.high", "%d", value);
1193 }
1194
1195 /* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */
1196 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT));
1197 if (value != 0) {
1198 dstate_setinfo("input.transfer.trim.low", "%d", value);
1199 }
1200
1201 /* Low battery warning */
1202 bcmxcp_status.lowbatt = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING] * 60;
1203
1204 /* Check if we should warn the user that her shutdown delay is to long? */
1205 if (bcmxcp_status.shutdowndelay > bcmxcp_status.lowbatt)
1206 upslogx(LOG_WARNING, "Shutdown delay longer than battery capacity when Low Battery warning is given. (max %d seconds)", bcmxcp_status.lowbatt);
1207
1208 /* Horn Status: */
1209 value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS];
1210 if (value >= 0 && value <= 2) {
1211 dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]);
1212 }
1213
1214 /* Minimum Supported Input Voltage */
1215 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE));
1216 if (value != 0) {
1217 dstate_setinfo("input.transfer.low", "%d", value);
1218 }
1219
1220 /* Maximum Supported Input Voltage */
1221 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE));
1222 if (value != 0) {
1223 dstate_setinfo("input.transfer.high", "%d", value);
1224 }
1225
1226 /* Ambient Temperature Lower Alarm Limit */
1227 value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW];
1228 if (value != 0) {
1229 dstate_setinfo("ambient.temperature.low", "%d", value);
1230 }
1231
1232 /* Ambient Temperature Upper Alarm Limit */
1233 value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE];
1234 if (value != 0) {
1235 dstate_setinfo("ambient.temperature.high", "%d", value);
1236 }
1237
1238 /*Sleep minimum load*/
1239 value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD];
1240 if (value != 0) {
1241 dstate_setinfo("battery.energysave.load", "%d", value);
1242 }
1243
1244 /* Sleep delay*/
1245 value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY];
1246 if (value != 0) {
1247 dstate_setinfo("battery.energysave.delay", "%d", value);
1248 }
1249
1250 /* Low batt minutes warning*/
1251 value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING];
1252 if (value != 0) {
1253 dstate_setinfo("battery.runtime.low", "%d", value);
1254 }
1255
1256 /* Return to mains delay */
1257 value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY);
1258 if (value != 0) {
1259 dstate_setinfo("input.transfer.delay","%d",value);
1260 }
1261
1262 /* Minimum return capacity*/
1263 value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN];
1264 if (value != 0) {
1265 dstate_setinfo("battery.charge.restart","%d",value);
1266 }
1267
1268 }
1269
init_topology(void)1270 void init_topology(void)
1271 {
1272 unsigned char answer[PW_ANSWER_MAX_SIZE];
1273 const char* nutvalue;
1274 int res, value;
1275
1276 res = command_read_sequence(PW_UPS_TOP_DATA_REQ, answer);
1277 if (res <= 0)
1278 fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1279
1280 value = get_word(answer);
1281
1282 if ((nutvalue = nut_find_infoval(topology_info, value, TRUE)) != NULL) {
1283 dstate_setinfo("ups.description", "%s", nutvalue);
1284 }
1285 }
1286
init_system_test_capabilities(void)1287 void init_system_test_capabilities(void)
1288 {
1289 unsigned char answer[PW_ANSWER_MAX_SIZE], cbuf[5];
1290 const char* nutvalue;
1291 int res, value, i;
1292
1293 /* Query what system test capabilities are supported */
1294 send_write_command(AUTHOR, 4);
1295
1296 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1297
1298 cbuf[0] = PW_INIT_SYS_TEST;
1299 cbuf[1] = PW_SYS_TEST_REPORT_CAPABILITIES;
1300 res = command_write_sequence(cbuf, 2, answer);
1301 if (res <= 0) {
1302 upslogx(LOG_ERR, "Short read from UPS");
1303 return;
1304 }
1305
1306 if((unsigned char)answer[0] != BCMXCP_RETURN_ACCEPTED) {
1307 upsdebugx(2, "System test capabilities list not supported");
1308 return;
1309 }
1310
1311 /* Add instcmd for system test capabilities */
1312 for(i = 3; i < res; i++) {
1313 value = answer[i];
1314 if ((nutvalue = nut_find_infoval(system_test_info, value, TRUE)) != NULL) {
1315 upsdebugx(2, "Added support for instcmd %s", nutvalue);
1316 dstate_addcmd(nutvalue);
1317 }
1318 }
1319 }
1320
upsdrv_initinfo(void)1321 void upsdrv_initinfo(void)
1322 {
1323 unsigned char answer[PW_ANSWER_MAX_SIZE];
1324 char *pTmp;
1325 char outlet_name[27];
1326 char power_rating[10];
1327 int iRating = 0, iIndex = 0, res, len;
1328 int ncpu = 0, buf;
1329 int conf_block_len = 0, alarm_block_len = 0, cmd_list_len = 0, topology_block_len = 0;
1330 bool_t got_cmd_list = FALSE;
1331
1332 /* Init BCM/XCP command descriptions */
1333 init_command_map();
1334
1335 /* Init BCM/XCP alarm descriptions */
1336 init_alarm_map();
1337
1338 /* Get vars from ups.conf */
1339 if (getval("shutdown_delay") != NULL)
1340 bcmxcp_status.shutdowndelay = atoi(getval("shutdown_delay"));
1341 else
1342 bcmxcp_status.shutdowndelay = 120;
1343
1344 /* Get information on UPS from UPS ID block */
1345 res = command_read_sequence(PW_ID_BLOCK_REQ, answer);
1346 if (res <= 0)
1347 fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1348
1349 /* Get number of CPU's in ID block */
1350 len = answer[iIndex++];
1351
1352 buf = len * 11;
1353 pTmp = xmalloc(buf+1);
1354
1355 pTmp[0] = 0;
1356 /* If there is one or more CPU number, get it */
1357 if (len > 0) {
1358 do {
1359 if ((answer[iIndex] != 0x00) || (answer[iIndex+1] != 0x00)) {
1360 /* Get the ups firmware. The major number is in the last byte, the minor is in the first */
1361 snprintfcat(pTmp, buf+1, "%s%02x.%02x ", cpu_name[ncpu], answer[iIndex+1], answer[iIndex]);
1362 }
1363 iIndex += 2;
1364 len--;
1365 ncpu++;
1366
1367 } while ((len > 0) && (ncpu <= 5));
1368
1369 dstate_setinfo("ups.firmware", "%s", pTmp);
1370
1371 /* Increment index to point at end of CPU bytes. */
1372 iIndex += len * 2;
1373 }
1374
1375 free(pTmp);
1376
1377 /* Get rating in kVA, if present */
1378 if ((iRating = answer[iIndex++]) > 0)
1379 iRating *= 1000;
1380 else
1381 {
1382 /* The rating is given as 2 byte VA */
1383 iRating = get_word(answer+iIndex) * 50;
1384 iIndex += 2;
1385 }
1386 dstate_setinfo("ups.power.nominal", "%d", iRating);
1387
1388 /* Get information on Phases from UPS */
1389 nphases = (answer[iIndex++]);
1390 dstate_setinfo("output.phases", "%d", nphases);
1391
1392 /* Init BCM/XCP <-> NUT meter map */
1393 init_meter_map();
1394
1395 /* Skip UPS' phase angle, as NUT do not care */
1396 iIndex += 1;
1397
1398 /* Set manufacturer name */
1399 dstate_setinfo("ups.mfr", "Eaton");
1400
1401 /* Get length of UPS description */
1402 len = answer[iIndex++];
1403
1404 /* Extract and reformat the model string */
1405 pTmp = xmalloc(len+15);
1406 snprintf(pTmp, len + 1, "%s", answer + iIndex);
1407 pTmp[len+1] = 0;
1408 iIndex += len;
1409 /* power rating in the model name is in the form "<rating>i"
1410 * ie "1500i", "500i", ...
1411 * some models already includes it, so check to avoid duplication */
1412 snprintf(power_rating, sizeof(power_rating), "%ii", iRating);
1413 if (strstr(pTmp, power_rating) == NULL) {
1414 snprintfcat(pTmp, len+10, " %s", power_rating);
1415 }
1416 dstate_setinfo("ups.model", "%s", str_rtrim(pTmp, ' '));
1417 free(pTmp);
1418
1419 /* Get meter map info from ups, and init our map */
1420 len = answer[iIndex++];
1421 upsdebugx(2, "Length of meter map: %d\n", len);
1422 init_ups_meter_map(answer+iIndex, len);
1423 iIndex += len;
1424
1425 /* Next is alarm map */
1426 len = answer[iIndex++];
1427 upsdebugx(2, "Length of alarm map: %d\n", len);
1428 init_ups_alarm_map(answer+iIndex, len);
1429 iIndex += len;
1430
1431 /* Then the Config_block_length */
1432 conf_block_len = get_word(answer+iIndex);
1433 upsdebugx(2, "Length of Config_block: %d\n", conf_block_len);
1434 iIndex += 2;
1435
1436 /* Next is statistics map */
1437 len = answer[iIndex++];
1438 upsdebugx(2, "Length of statistics map: %d\n", len);
1439 /* init_statistics_map(answer+iIndex, len); */
1440 iIndex += len;
1441
1442 /* Size of the alarm history log */
1443 len = get_word(answer+iIndex);
1444 upsdebugx(2, "Length of alarm history log: %d\n", len);
1445 iIndex += 2;
1446
1447 /* Size of custom event log, always 0 according to spec */
1448 iIndex += 2;
1449 /* Size of topology block */
1450 topology_block_len = get_word(answer+iIndex);
1451 upsdebugx(2, "Length of topology block: %d\n", topology_block_len);
1452 iIndex += 2;
1453
1454 /* Maximum supported command length */
1455 len = answer[iIndex++];
1456 upsdebugx(2, "Length of max supported command length: %d\n", len);
1457
1458 /* Size of command list block */
1459 if (iIndex < res)
1460 cmd_list_len = get_word(answer+iIndex);
1461 upsdebugx(2, "Length of command list: %d\n", cmd_list_len);
1462 iIndex += 2;
1463
1464 /* Size of outlet monitoring block */
1465 if (iIndex < res)
1466 outlet_block_len = get_word(answer+iIndex);
1467 upsdebugx(2, "Length of outlet_block: %d\n", outlet_block_len);
1468 iIndex += 2;
1469
1470 /* Size of the alarm block */
1471 if (iIndex < res)
1472 alarm_block_len = get_word(answer+iIndex);
1473 upsdebugx(2, "Length of alarm_block: %d\n", alarm_block_len);
1474 /* End of UPS ID block request */
1475
1476 /* Due to a bug in PW5115 firmware, we need to use blocklength > 8.
1477 The protocol state that outlet block is only implemented if there is
1478 at least 2 outlet block. 5115 has only one outlet, but has outlet block! */
1479 if (outlet_block_len > 8) {
1480 len = init_outlet(outlet_block_len);
1481
1482 for(res = 1 ; res <= len ; res++) {
1483 snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.shutdown.return", res);
1484 dstate_addcmd(outlet_name);
1485 snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.load.on", res);
1486 dstate_addcmd(outlet_name);
1487 snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.load.off", res);
1488 dstate_addcmd(outlet_name);
1489 }
1490 }
1491
1492 /* Get information on UPS configuration */
1493 init_config();
1494
1495 /* Get information on UPS extended limits */
1496 init_limit();
1497
1498 /* Get information on UPS commands */
1499 if (cmd_list_len)
1500 got_cmd_list = init_command(cmd_list_len);
1501 /* Add default commands if we were not able to query UPS for support */
1502 if(got_cmd_list == FALSE) {
1503 dstate_addcmd("shutdown.return");
1504 dstate_addcmd("shutdown.stayoff");
1505 dstate_addcmd("test.battery.start");
1506 }
1507 /* Get information on UPS topology */
1508 if (topology_block_len)
1509 init_topology();
1510 /* Get information on system test capabilities */
1511 if (bcmxcp_command_map[PW_INIT_SYS_TEST].command_byte > 0) {
1512 init_system_test_capabilities();
1513 }
1514 /* Get information about configurable external variables*/
1515 init_ext_vars();
1516
1517 upsh.instcmd = instcmd;
1518 upsh.setvar = setvar;
1519 }
1520
upsdrv_updateinfo(void)1521 void upsdrv_updateinfo(void)
1522 {
1523 unsigned char answer[PW_ANSWER_MAX_SIZE];
1524 unsigned char status, topology;
1525 char sValue[128];
1526 int iIndex, res,value;
1527 bool_t has_ups_load = FALSE;
1528 int batt_status = 0;
1529 const char *nutvalue;
1530 float calculated_load;
1531
1532 /* Get info from UPS */
1533 res = command_read_sequence(PW_METER_BLOCK_REQ, answer);
1534
1535 if (res <= 0){
1536 upslogx(LOG_ERR, "Short read from UPS");
1537 dstate_datastale();
1538 return;
1539 }
1540
1541 /* Loop thru meter map, get all data UPS is willing to offer */
1542 for (iIndex = 0; iIndex < BCMXCP_METER_MAP_MAX; iIndex++){
1543 if (bcmxcp_meter_map[iIndex].format != 0 && bcmxcp_meter_map[iIndex].nut_entity != NULL) {
1544 decode_meter_map_entry(answer + bcmxcp_meter_map[iIndex].meter_block_index,
1545 bcmxcp_meter_map[iIndex].format, sValue);
1546
1547 /* Set result */
1548 dstate_setinfo(bcmxcp_meter_map[iIndex].nut_entity, "%s", sValue);
1549
1550 /* Check if we read ups.load */
1551 if(has_ups_load == FALSE && !strcasecmp(bcmxcp_meter_map[iIndex].nut_entity, "ups.load")) {
1552 has_ups_load = TRUE;
1553 }
1554 }
1555 }
1556
1557 /* Calculate ups.load if UPS does not report it directly */
1558 if(has_ups_load == FALSE) {
1559 calculated_load = calculate_ups_load(answer);
1560 if(calculated_load >= 0.0f) {
1561 dstate_setinfo("ups.load", "%5.1f", calculated_load);
1562 }
1563 }
1564
1565 /* Due to a bug in PW5115 firmware, we need to use blocklength > 8.
1566 The protocol state that outlet block is only implemented if there is
1567 at least 2 outlet block. 5115 has only one outlet, but has outlet block. */
1568 if (outlet_block_len > 8) {
1569 init_outlet(outlet_block_len);
1570 }
1571
1572 /* Get alarm info from UPS */
1573 res = command_read_sequence(PW_CUR_ALARM_REQ, answer);
1574 if (res <= 0){
1575 upslogx(LOG_ERR, "Short read from UPS");
1576 dstate_datastale();
1577 return;
1578 }
1579 else
1580 {
1581 bcmxcp_status.alarm_on_battery = 0;
1582 bcmxcp_status.alarm_low_battery = 0;
1583
1584 /* Set alarms */
1585 alarm_init();
1586
1587 /* Loop thru alarm map, get all alarms UPS is willing to offer */
1588 for (iIndex = 0; iIndex < BCMXCP_ALARM_MAP_MAX; iIndex++){
1589 if (bcmxcp_alarm_map[iIndex].alarm_block_index >= 0 && bcmxcp_alarm_map[iIndex].alarm_desc != NULL) {
1590 if (answer[bcmxcp_alarm_map[iIndex].alarm_block_index] > 0) {
1591 alarm_set(bcmxcp_alarm_map[iIndex].alarm_desc);
1592
1593 if (iIndex == BCMXCP_ALARM_UPS_ON_BATTERY) {
1594 bcmxcp_status.alarm_on_battery = 1;
1595 }
1596 else if (iIndex == BCMXCP_ALARM_BATTERY_LOW) {
1597 bcmxcp_status.alarm_low_battery = 1;
1598 }
1599 else if (iIndex == BCMXCP_ALARM_BATTERY_TEST_FAILED) {
1600 bcmxcp_status.alarm_replace_battery = 1;
1601 }
1602 else if (iIndex == BCMXCP_ALARM_BATTERY_NEEDS_SERVICE) {
1603 bcmxcp_status.alarm_replace_battery = 1;
1604 }
1605 }
1606 }
1607 }
1608
1609 /* Confirm alarms */
1610 alarm_commit();
1611 }
1612
1613 /* Get status info from UPS */
1614 res = command_read_sequence(PW_STATUS_REQ, answer);
1615 if (res <= 0)
1616 {
1617 upslogx(LOG_ERR, "Short read from UPS");
1618 dstate_datastale();
1619 return;
1620 }
1621 else
1622 {
1623
1624 /* Get overall status */
1625 memcpy(&status, answer, sizeof(status));
1626
1627 /* Get topology status bitmap, validate */
1628 memcpy(&topology, answer+1, sizeof(topology));
1629 topology &= bcmxcp_status.topology_mask;
1630
1631 /* Set status */
1632 status_init();
1633
1634 switch (status) {
1635 case BCMXCP_STATUS_ONLINE: /* On line, everything is fine */
1636 status_set("OL");
1637 break;
1638 case BCMXCP_STATUS_ONBATTERY: /* Off line */
1639 if (bcmxcp_status.alarm_on_battery == 0)
1640 status_set("OB");
1641 break;
1642 case BCMXCP_STATUS_OVERLOAD: /* Overload */
1643 status_set("OL");
1644 status_set("OVER");
1645 break;
1646 case BCMXCP_STATUS_TRIM: /* Trim */
1647 status_set("OL");
1648 status_set("TRIM");
1649 break;
1650 case BCMXCP_STATUS_BOOST1:
1651 case BCMXCP_STATUS_BOOST2: /* Boost */
1652 status_set("OL");
1653 status_set("BOOST");
1654 break;
1655 case BCMXCP_STATUS_BYPASS: /* Bypass */
1656 status_set("OL");
1657 status_set("BYPASS");
1658 break;
1659 case BCMXCP_STATUS_OFF: /* Mostly off */
1660 status_set("OFF");
1661 break;
1662 default: /* Unknown, assume it is OK... */
1663 status_set("OL");
1664 break;
1665 }
1666
1667 /* We might have to modify status based on topology status */
1668 if ((topology & 0x20) && bcmxcp_status.alarm_low_battery == 0)
1669 status_set("LB");
1670
1671 /* And finally, we might need to modify status based on alarms - the most correct way */
1672 if (bcmxcp_status.alarm_on_battery)
1673 status_set("OB");
1674 if (bcmxcp_status.alarm_low_battery)
1675 status_set("LB");
1676 if (bcmxcp_status.alarm_replace_battery)
1677 status_set("RB");
1678
1679 status_commit();
1680 }
1681
1682 /* Get battery info from UPS, if exist */
1683 res = command_read_sequence(PW_BATTERY_REQ, answer);
1684 if (res <= 0)
1685 {
1686 upsdebugx(1, "Failed to read Battery Status from UPS");
1687 }
1688 else
1689 {
1690 /* Only parse the status (first byte)
1691 * Powerware 5115 RM output:
1692 * 02 00 78 1d 42 00 e0 17 42 1e 00 00 00 00 00 00 00 00 00 01 03
1693 * Powerware 9130 output:
1694 * 03 0a d7 25 42 0a d7 25 42 00 9a 19 6d 43 cd cc 4c 3e 01 00 01 03
1695 */
1696 upsdebug_hex(2, "Battery Status", answer, res);
1697 batt_status = answer[BCMXCP_BATTDATA_BLOCK_BATT_TEST_STATUS];
1698
1699 if ((nutvalue = nut_find_infoval(batt_test_info, batt_status, TRUE)) != NULL) {
1700 dstate_setinfo("ups.test.result", "%s", nutvalue);
1701 upsdebugx(2, "Battery Status = %s (%i)", nutvalue, batt_status);
1702 }
1703 else {
1704 upsdebugx(1, "Failed to extract Battery Status from answer");
1705 }
1706
1707 /*Extracting internal batteries ABM status*/
1708 /*Placed first in ABM statuses list. For examples above - on position BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS (18):
1709 PW5115RM - 0 - no external strings, no status bytes,
1710 so next byte (19) - number of ABM statuses, next (20) - first ABM Status for internal batteries.
1711
1712 PW9130 - 1 - one external string, so one additional status byte (#19 - 00 - no test run), next(20) - number of ABM statuses,
1713 next (21) - ABM Status for internal batteries.
1714 */
1715 value=*(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS + *(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS)*1+2 );
1716 upsdebugx(2, "ABM Status = %d ",value);
1717 if (value > 0 && value < 5)
1718 dstate_setinfo("battery.charger.status","%s",ABMStatus[value-1]);
1719 }
1720
1721
1722 res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer);
1723 if (res <= 0) {
1724 upsdebugx(1, "Failed to read EXT LIMITs from UPS");
1725 } else
1726 {
1727 /* Nominal input voltage */
1728 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE));
1729
1730 if (value != 0) {
1731 dstate_setinfo("input.voltage.nominal", "%d", value);
1732 }
1733
1734 /* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */
1735 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT));
1736
1737 if (value != 0) {
1738 dstate_setinfo("input.transfer.boost.high", "%d", value);
1739 }
1740
1741 /* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */
1742 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT));
1743
1744 if (value != 0) {
1745 dstate_setinfo("input.transfer.trim.low", "%d", value);
1746 }
1747
1748 /* Minimum Supported Input Voltage */
1749 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE));
1750
1751 if (value != 0) {
1752 dstate_setinfo("input.transfer.low", "%d", value);
1753 }
1754
1755 /* Maximum Supported Input Voltage */
1756 value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE));
1757
1758 if (value != 0) {
1759 dstate_setinfo("input.transfer.high", "%d", value);
1760 }
1761
1762 /* Horn Status: */
1763 value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS];
1764
1765 if (value >= 0 && value <= 2) {
1766 dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]);
1767 }
1768 /* AAmbient Temperature Upper Alarm Limit */
1769 value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE];
1770
1771 if (value != 0) {
1772 dstate_setinfo("ambient.temperature.high", "%d", value);
1773 }
1774
1775 /*Sleep minimum load*/
1776 value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD];
1777 if (value != 0) {
1778 dstate_setinfo("battery.energysave.load", "%d", value);
1779 }
1780
1781 /* Sleep delay*/
1782 value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY];
1783 if (value != 0) {
1784 dstate_setinfo("battery.energysave.delay", "%d", value);
1785 }
1786
1787 /* Low batt minutes warning*/
1788 value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING];
1789 if (value != 0) {
1790 dstate_setinfo("battery.runtime.low", "%d", value);
1791 }
1792
1793 /* Return to mains delay */
1794 value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY);
1795 if (value != 0) {
1796 dstate_setinfo("input.transfer.delay","%d",value);
1797 }
1798
1799 /* Minimum return capacity*/
1800 value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN];
1801 if (value != 0) {
1802 dstate_setinfo("battery.charge.restart","%d",value);
1803 }
1804 };
1805
1806 res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer);
1807 if (res <= 0) {
1808 upsdebugx(1, "Failed to read CONF BLOCK from UPS");
1809 }
1810 else
1811 {
1812 /*Nominal output voltage*/
1813 value = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE));
1814
1815 if (value != 0)
1816 dstate_setinfo("output.voltage.nominal", "%d", value);
1817 /*Number of EBM*/
1818 value = (int) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3);
1819 if (value != 0)
1820 dstate_setinfo("battery.packs", "%d", value);
1821
1822 }
1823
1824
1825 dstate_dataok();
1826 }
1827
calculate_ups_load(const unsigned char * answer)1828 float calculate_ups_load(const unsigned char *answer)
1829 {
1830 char sValue[128];
1831 float output = 0, max_output = -FLT_MAX, fValue = -FLT_MAX;
1832
1833 if (bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format != 0 && /* Output VA */
1834 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format != 0) /* Max output VA */
1835 {
1836 decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].meter_block_index,
1837 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format, sValue);
1838 output = atof(sValue);
1839 decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].meter_block_index,
1840 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format, sValue);
1841 max_output = atof(sValue);
1842 }
1843 else if (bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format != 0 && /* Output A */
1844 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format != 0) /* Max output A */
1845 {
1846 decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].meter_block_index,
1847 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format, sValue);
1848 output = atof(sValue);
1849 decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].meter_block_index,
1850 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format, sValue);
1851 max_output = atof(sValue);
1852 }
1853 if (max_output > 0.0)
1854 fValue = 100 * (output / max_output);
1855
1856 return fValue;
1857 }
1858
upsdrv_shutdown(void)1859 void upsdrv_shutdown(void)
1860 {
1861 upsdebugx(1, "upsdrv_shutdown...");
1862
1863 /* Try to shutdown with delay */
1864 if (instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) {
1865 /* Shutdown successful */
1866 return;
1867 }
1868
1869 /* If the above doesn't work, try shutdown.stayoff */
1870 if (instcmd("shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) {
1871 /* Shutdown successful */
1872 return;
1873 }
1874
1875 fatalx(EXIT_FAILURE, "Shutdown failed!");
1876 }
1877
1878
instcmd(const char * cmdname,const char * extra)1879 static int instcmd(const char *cmdname, const char *extra)
1880 {
1881 unsigned char answer[128], cbuf[6];
1882 char success_msg[40];
1883 char namebuf[MAX_NUT_NAME_LENGTH];
1884 char varname[32];
1885 const char *varvalue = NULL;
1886 int res, sec, outlet_num;
1887 int sddelay = 0x03; /* outlet off in 3 seconds, by default */
1888
1889 upsdebugx(1, "entering instcmd(%s)", cmdname);
1890
1891 if (!strcasecmp(cmdname, "shutdown.return")) {
1892 send_write_command(AUTHOR, 4);
1893
1894 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1895
1896 cbuf[0] = PW_LOAD_OFF_RESTART;
1897 cbuf[1] = (unsigned char)(bcmxcp_status.shutdowndelay & 0x00ff); /* "delay" sec delay for shutdown, */
1898 cbuf[2] = (unsigned char)(bcmxcp_status.shutdowndelay >> 8); /* high byte sec. From ups.conf. */
1899
1900 res = command_write_sequence(cbuf, 3, answer);
1901
1902 sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2];
1903 snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec);
1904
1905 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg);
1906 }
1907
1908 if (!strcasecmp(cmdname, "shutdown.stayoff")) {
1909 send_write_command(AUTHOR, 4);
1910
1911 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1912
1913 res = command_read_sequence(PW_UPS_OFF, answer);
1914
1915 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Going down NOW");
1916 }
1917
1918 if (!strcasecmp(cmdname, "load.on")) {
1919 send_write_command(AUTHOR, 4);
1920
1921 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1922
1923 res = command_read_sequence(PW_UPS_ON, answer);
1924
1925 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Enabling");
1926 }
1927
1928 if (!strcasecmp(cmdname, "bypass.start")) {
1929 send_write_command(AUTHOR, 4);
1930
1931 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1932
1933 res = command_read_sequence(PW_GO_TO_BYPASS, answer);
1934
1935 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Bypass enabled");
1936 }
1937
1938 /* Note: test result will be parsed from Battery status block,
1939 * part of the update loop, and published into ups.test.result
1940 */
1941 if (!strcasecmp(cmdname, "test.battery.start")) {
1942 send_write_command(AUTHOR, 4);
1943
1944 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1945
1946 cbuf[0] = PW_INIT_BAT_TEST;
1947 cbuf[1] = 0x0A; /* 10 sec start delay for test.*/
1948 cbuf[2] = 0x1E; /* 30 sec test duration.*/
1949
1950 res = command_write_sequence(cbuf, 3, answer);
1951
1952 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing battery now");
1953 /* Get test info from UPS ?
1954 Should we wait for 50 sec and get the
1955 answer from the test.
1956 Or return, as we may lose line power
1957 and need to do a shutdown.*/
1958 }
1959
1960 if (!strcasecmp(cmdname, "test.system.start")) {
1961 send_write_command(AUTHOR, 4);
1962
1963 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1964
1965 cbuf[0] = PW_INIT_SYS_TEST;
1966 cbuf[1] = PW_SYS_TEST_GENERAL;
1967 res = command_write_sequence(cbuf, 2, answer);
1968
1969 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing system now");
1970 }
1971
1972 if (!strcasecmp(cmdname, "test.panel.start")) {
1973 send_write_command(AUTHOR, 4);
1974
1975 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1976
1977 cbuf[0] = PW_INIT_SYS_TEST;
1978 cbuf[1] = PW_SYS_TEST_FLASH_LIGHTS;
1979 cbuf[2] = 0x0A; /* Flash and beep 10 times */
1980 res = command_write_sequence(cbuf, 3, answer);
1981
1982 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing panel now");
1983 }
1984
1985 if (!strcasecmp(cmdname, "beeper.disable") || !strcasecmp(cmdname, "beeper.enable") || !strcasecmp(cmdname, "beeper.mute")) {
1986 send_write_command(AUTHOR, 4);
1987
1988 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1989
1990 cbuf[0] = PW_SET_CONF_COMMAND;
1991 cbuf[1] = PW_CONF_BEEPER;
1992 switch (cmdname[7]){
1993
1994 case 'd':
1995 case 'D': {
1996 cbuf[2] = 0x0; /*disable beeper*/
1997 break;
1998 }
1999 case 'e':
2000 case 'E': {
2001 cbuf[2] = 0x1; /*enable beeper*/
2002 break;
2003 }
2004 case 'm':
2005 case 'M': {
2006 cbuf[2] = 0x2;
2007 break; /*mute beeper*/
2008 }
2009 }
2010 cbuf[3] = 0x0; /*padding*/
2011
2012 res = command_write_sequence(cbuf, 4, answer);
2013 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Beeper status changed");
2014 }
2015
2016 strncpy(namebuf, cmdname, sizeof(namebuf));
2017 namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */
2018
2019 if (!strcasecmp(namebuf, "outlet.n.shutdown.return")) {
2020 send_write_command(AUTHOR, 4);
2021
2022 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2023
2024 /* Get the shutdown delay, if any */
2025 snprintf(varname, sizeof(varname)-1, "outlet.%c.delay.shutdown", cmdname[NUT_OUTLET_POSITION]);
2026 if ((varvalue = dstate_getinfo(varname)) != NULL) {
2027 sddelay = atoi(varvalue);
2028 }
2029
2030 /*if -1 then use global shutdown_delay from ups.conf*/
2031 if (sddelay == -1) sddelay=bcmxcp_status.shutdowndelay;
2032
2033 outlet_num = cmdname[NUT_OUTLET_POSITION] - '0';
2034 if (outlet_num < 1 || outlet_num > 9)
2035 return STAT_INSTCMD_FAILED;
2036
2037 cbuf[0] = PW_LOAD_OFF_RESTART;
2038 cbuf[1] = sddelay & 0xff;
2039 cbuf[2] = sddelay >> 8; /* high byte of the 2 byte time argument */
2040 cbuf[3] = outlet_num; /* which outlet load segment? Assumes outlet number at position 8 of the command string. */
2041
2042 res = command_write_sequence(cbuf, 4, answer);
2043
2044 sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2];
2045 snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec);
2046
2047 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg);
2048 }
2049
2050 if (!strcasecmp(namebuf,"outlet.n.load.on") || !strcasecmp(namebuf,"outlet.n.load.off")){
2051 send_write_command(AUTHOR, 4);
2052
2053 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2054
2055 outlet_num = cmdname[NUT_OUTLET_POSITION] - '0';
2056 if (outlet_num < 1 || outlet_num > 9)
2057 return STAT_INSTCMD_FAILED;
2058
2059
2060 cbuf[0] = (cmdname[NUT_OUTLET_POSITION+8] == 'n')?PW_UPS_ON:PW_UPS_OFF; /* Cmd oN or not*/
2061 cbuf[1] = outlet_num; /* Outlet number */
2062
2063 res = command_write_sequence(cbuf, 2, answer);
2064 snprintf(success_msg, sizeof(success_msg)-1, "Outlet %d is %s",outlet_num, (cmdname[NUT_OUTLET_POSITION+8] == 'n')?"On":"Off");
2065
2066 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg);
2067
2068
2069
2070 }
2071
2072 upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
2073 return STAT_INSTCMD_UNKNOWN;
2074 }
2075
decode_instcmd_exec(const int res,const unsigned char exec_status,const char * cmdname,const char * success_msg)2076 static int decode_instcmd_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg)
2077 {
2078 if (res <= 0) {
2079 upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname);
2080 dstate_datastale();
2081 return STAT_INSTCMD_FAILED;
2082 }
2083
2084 /* Decode the status code from command execution */
2085 switch (exec_status) {
2086 case BCMXCP_RETURN_ACCEPTED: {
2087 upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2088 upsdrv_comm_good();
2089 return STAT_INSTCMD_HANDLED;
2090 break;
2091 }
2092 case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: {
2093 upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname);
2094 upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2095 upsdrv_comm_good();
2096 return STAT_INSTCMD_HANDLED;
2097 break;
2098 }
2099 case BCMXCP_RETURN_BUSY: {
2100 upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname);
2101 return STAT_INSTCMD_FAILED;
2102 break;
2103 }
2104 case BCMXCP_RETURN_UNRECOGNISED: {
2105 upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname);
2106 return STAT_INSTCMD_FAILED;
2107 break;
2108 }
2109 case BCMXCP_RETURN_INVALID_PARAMETER: {
2110 upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname);
2111 return STAT_INSTCMD_INVALID;
2112 break;
2113 }
2114 case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: {
2115 upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname);
2116 return STAT_INSTCMD_INVALID;
2117 break;
2118 }
2119 default: {
2120 upslogx(LOG_NOTICE, "[%s] Not supported", cmdname);
2121 return STAT_INSTCMD_INVALID;
2122 break;
2123 }
2124 }
2125 }
2126
upsdrv_help(void)2127 void upsdrv_help(void)
2128 {
2129 }
2130
2131 /* list flags and values that you want to receive via -x */
upsdrv_makevartable(void)2132 void upsdrv_makevartable(void)
2133 {
2134 addvar(VAR_VALUE, "shutdown_delay", "Specify shutdown delay (seconds)");
2135 addvar(VAR_VALUE, "baud_rate", "Specify communication speed (ex: 9600)");
2136 }
2137
setvar(const char * varname,const char * val)2138 int setvar (const char *varname, const char *val)
2139 {
2140 unsigned char answer[128], cbuf[5];
2141 char namebuf[MAX_NUT_NAME_LENGTH];
2142 char success_msg[50];
2143 int res, sec, outlet_num,tmp;
2144 int onOff_setting = PW_AUTO_OFF_DELAY;
2145
2146 upsdebugx(1, "entering setvar(%s, %s)", varname, val);
2147
2148 if (!strcasecmp(varname, "input.transfer.boost.high")) {
2149
2150 send_write_command(AUTHOR, 4);
2151 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2152
2153 tmp=atoi(val);
2154 if (tmp < 0 || tmp > 460) {
2155 return STAT_SET_INVALID;
2156 }
2157
2158 cbuf[0]=PW_SET_CONF_COMMAND;
2159 cbuf[1]=PW_CONF_LOW_DEV_LIMIT;
2160 cbuf[2]=tmp&0xff;
2161 cbuf[3]=tmp>>8;
2162
2163 res = command_write_sequence(cbuf, 4, answer);
2164 snprintf(success_msg, sizeof(success_msg)-1, " BOOST threshold volage set to %d V", tmp);
2165
2166 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2167
2168
2169 }
2170
2171 if (!strcasecmp(varname, "input.transfer.trim.low")) {
2172
2173 send_write_command(AUTHOR, 4);
2174 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2175
2176 tmp=atoi(val);
2177 if (tmp < 110 || tmp > 540) {
2178 return STAT_SET_INVALID;
2179 }
2180
2181 cbuf[0]=PW_SET_CONF_COMMAND;
2182 cbuf[1]=PW_CONF_HIGH_DEV_LIMIT;
2183 cbuf[2]=tmp&0xff;
2184 cbuf[3]=tmp>>8;
2185
2186 res = command_write_sequence(cbuf, 4, answer);
2187 snprintf(success_msg, sizeof(success_msg)-1, " TRIM threshold volage set to %d V", tmp);
2188
2189 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2190
2191 }
2192
2193 if (!strcasecmp(varname, "battery.runtime.low")) {
2194
2195 send_write_command(AUTHOR, 4);
2196 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2197
2198 tmp=atoi(val);
2199 if (tmp < 0 || tmp > 30) {
2200 return STAT_SET_INVALID;
2201 }
2202
2203 cbuf[0]=PW_SET_CONF_COMMAND;
2204 cbuf[1]=PW_CONF_LOW_BATT;
2205 cbuf[2]=tmp;
2206 cbuf[3]=0x0;
2207
2208 res = command_write_sequence(cbuf, 4, answer);
2209 snprintf(success_msg, sizeof(success_msg)-1, " Low battery warning time set to %d min", tmp);
2210
2211 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2212
2213 }
2214
2215 if (!strcasecmp(varname, "input.transfer.delay")) {
2216
2217 send_write_command(AUTHOR, 4);
2218 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2219
2220 tmp=atoi(val);
2221 if (tmp < 1 || tmp > 18000 ) {
2222 return STAT_SET_INVALID;
2223 }
2224
2225 cbuf[0]=PW_SET_CONF_COMMAND;
2226 cbuf[1]=PW_CONF_RETURN_DELAY;
2227 cbuf[2]=tmp&0xff;
2228 cbuf[3]=tmp>>8;
2229
2230 res = command_write_sequence(cbuf, 4, answer);
2231 snprintf(success_msg, sizeof(success_msg)-1, " Mains return delay set to %d sec", tmp);
2232
2233 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2234
2235 }
2236
2237 if (!strcasecmp(varname, "battery.charge.restart")) {
2238
2239 send_write_command(AUTHOR, 4);
2240 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2241
2242 tmp=atoi(val);
2243 if (tmp < 0 || tmp > 100 ) {
2244 return STAT_SET_INVALID;
2245 }
2246
2247 cbuf[0]=PW_SET_CONF_COMMAND;
2248 cbuf[1]=PW_CONF_RETURN_CAP;
2249 cbuf[2]=tmp&0xff;
2250 cbuf[3]=0x0;
2251
2252 res = command_write_sequence(cbuf, 4, answer);
2253 snprintf(success_msg, sizeof(success_msg)-1, " Mains return minimum battery capacity set to %d %%", tmp);
2254
2255 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2256
2257 }
2258
2259
2260 if (!strcasecmp(varname, "ambient.temperature.high")) {
2261
2262 send_write_command(AUTHOR, 4);
2263 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2264
2265 tmp=atoi(val);
2266 if (tmp < 0 || tmp > 100 ) {
2267 return STAT_SET_INVALID;
2268 }
2269
2270 cbuf[0]=PW_SET_CONF_COMMAND;
2271 cbuf[1]=PW_CONF_MAX_TEMP;
2272 cbuf[2]=tmp&0xff;
2273 cbuf[3]=0x0;
2274
2275 res = command_write_sequence(cbuf, 4, answer);
2276 snprintf(success_msg, sizeof(success_msg)-1, " Maximum temperature set to %d C", tmp);
2277
2278 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2279
2280 }
2281
2282 if (!strcasecmp(varname, "output.voltage.nominal")) {
2283
2284 send_write_command(AUTHOR, 4);
2285 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2286
2287 tmp=atoi(val);
2288 if (tmp < 0 || tmp > 460) {
2289 return STAT_SET_INVALID;
2290 }
2291
2292 cbuf[0]=PW_SET_CONF_COMMAND;
2293 cbuf[1]=PW_CONF_NOMINAL_OUT_VOLTAGE;
2294 cbuf[2]=tmp&0xff;
2295 cbuf[3]=tmp>>8;
2296
2297 res = command_write_sequence(cbuf, 4, answer);
2298 snprintf(success_msg, sizeof(success_msg)-1, " Nominal output voltage set to %d V", tmp);
2299
2300 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2301
2302 }
2303
2304 if (!strcasecmp(varname, "battery.energysave.load")) {
2305
2306 send_write_command(AUTHOR, 4);
2307 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2308
2309 tmp=atoi(val);
2310 if (tmp < 0 || tmp > 100) {
2311 return STAT_SET_INVALID;
2312 }
2313
2314 cbuf[0]=PW_SET_CONF_COMMAND;
2315 cbuf[1]=PW_CONF_SLEEP_TH_LOAD;
2316 cbuf[2]=tmp&0xff;
2317 cbuf[3]=0x0;
2318
2319 res = command_write_sequence(cbuf, 4, answer);
2320 snprintf(success_msg, sizeof(success_msg)-1, " Minimum load before sleep countdown set to %d %%", tmp);
2321
2322 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2323
2324
2325 }
2326
2327
2328 if (!strcasecmp(varname, "battery.energysave.delay")) {
2329
2330 send_write_command(AUTHOR, 4);
2331 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2332
2333 tmp=atoi(val);
2334 if (tmp < 0 || tmp > 255) {
2335 return STAT_SET_INVALID;
2336 }
2337
2338 cbuf[0]=PW_SET_CONF_COMMAND;
2339 cbuf[1]=PW_CONF_SLEEP_DELAY;
2340 cbuf[2]=tmp&0xff;
2341 cbuf[3]=0x0;
2342
2343 res = command_write_sequence(cbuf, 4, answer);
2344 snprintf(success_msg, sizeof(success_msg)-1, " Delay before sleep shutdown set to %d min", tmp);
2345
2346 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2347
2348
2349 }
2350
2351 if (!strcasecmp(varname, "battery.packs")) {
2352
2353 send_write_command(AUTHOR, 4);
2354 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2355
2356 tmp=atoi(val);
2357 if (tmp < 0 || tmp > 5) {
2358 return STAT_SET_INVALID;
2359 }
2360
2361 cbuf[0]=PW_SET_CONF_COMMAND;
2362 cbuf[1]=PW_CONF_BATT_STRINGS;
2363 cbuf[2]=tmp;
2364 cbuf[3]=0x0;
2365
2366 res = command_write_sequence(cbuf, 4, answer);
2367 snprintf(success_msg, sizeof(success_msg)-1, "EBM Count set to %d ", tmp);
2368
2369 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2370
2371
2372 }
2373
2374 strncpy(namebuf, varname, sizeof(namebuf));
2375 namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */
2376
2377 if ( (!strcasecmp(namebuf, "outlet.n.delay.start")) ||
2378 (!strcasecmp(namebuf, "outlet.n.delay.shutdown")) ) {
2379
2380
2381 if (outlet_block_len <= 8) {
2382 return STAT_SET_INVALID;
2383 }
2384
2385 if (!strcasecmp(namebuf, "outlet.n.delay.start")) {
2386 onOff_setting = PW_AUTO_ON_DELAY;
2387 }
2388
2389 send_write_command(AUTHOR, 4);
2390 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2391
2392 outlet_num = varname[NUT_OUTLET_POSITION] - '0';
2393 if (outlet_num < 1 || outlet_num > 9) {
2394 return STAT_SET_INVALID;
2395 }
2396
2397 sec = atoi(val);
2398 /* Check value:
2399 * 0-32767 are valid values
2400 * -1 means no Automatic off or restart
2401 * for Auto Off Delay:
2402 * 0-30 are valid but ill-advised */
2403 if (sec < -1 || sec > 0x7FFF) {
2404 return STAT_SET_INVALID;
2405 }
2406
2407 cbuf[0] = PW_SET_OUTLET_COMMAND; /* Cmd */
2408 cbuf[1] = onOff_setting; /* Set Auto Off (1) or On (2) Delay */
2409 cbuf[2] = outlet_num; /* Outlet number */
2410 cbuf[3] = sec&0xff; /* Delay in seconds LSB */
2411 cbuf[4] = sec>>8; /* Delay in seconds MSB */
2412
2413 res = command_write_sequence(cbuf, 5, answer);
2414 snprintf(success_msg, sizeof(success_msg)-1, "Outlet %d %s delay set to %d sec",
2415 outlet_num, (onOff_setting == PW_AUTO_ON_DELAY)?"start":"shutdown", sec);
2416
2417 return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2418
2419 }
2420
2421 return STAT_SET_INVALID;
2422 }
2423
decode_setvar_exec(const int res,const unsigned char exec_status,const char * cmdname,const char * success_msg)2424 static int decode_setvar_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg)
2425 {
2426 if (res <= 0) {
2427 upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname);
2428 dstate_datastale();
2429 return STAT_SET_FAILED;
2430 }
2431
2432 /* Decode the status code from command execution */
2433 switch (exec_status) {
2434 case BCMXCP_RETURN_ACCEPTED: {
2435 upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2436 upsdrv_comm_good();
2437 return STAT_SET_HANDLED;
2438 break;
2439 }
2440 case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: {
2441 upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname);
2442 upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2443 upsdrv_comm_good();
2444 return STAT_SET_HANDLED;
2445 break;
2446 }
2447 case BCMXCP_RETURN_BUSY: {
2448 upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname);
2449 return STAT_SET_FAILED;
2450 break;
2451 }
2452 case BCMXCP_RETURN_UNRECOGNISED: {
2453 upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname);
2454 return STAT_SET_FAILED;
2455 break;
2456 }
2457 case BCMXCP_RETURN_INVALID_PARAMETER: {
2458 upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname);
2459 return STAT_SET_INVALID;
2460 break;
2461 }
2462 case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: {
2463 upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname);
2464 return STAT_SET_INVALID;
2465 break;
2466 }
2467 default: {
2468 upslogx(LOG_NOTICE, "[%s] Not supported", cmdname);
2469 return STAT_SET_INVALID;
2470 break;
2471 }
2472 }
2473 }
2474
2475 /*******************************
2476 * Extracted from usbhid-ups.c *
2477 *******************************/
2478
2479 /* find the NUT value matching that XCP Item value */
nut_find_infoval(info_lkp_t * xcp2info,const double value,const bool_t debug_output_nonexisting)2480 static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting)
2481 {
2482 info_lkp_t *info_lkp;
2483
2484 /* if a conversion function is defined, use 'value' as argument for it */
2485 if (xcp2info->fun != NULL) {
2486 return xcp2info->fun(value);
2487 }
2488
2489 /* use 'value' as an index for a lookup in an array */
2490 for (info_lkp = xcp2info; info_lkp->nut_value != NULL; info_lkp++) {
2491 if (info_lkp->xcp_value == (long)value) {
2492 upsdebugx(5, "nut_find_infoval: found %s (value: %ld)", info_lkp->nut_value, (long)value);
2493 return info_lkp->nut_value;
2494 }
2495 }
2496 if(debug_output_nonexisting == TRUE) {
2497 upsdebugx(3, "nut_find_infoval: no matching INFO_* value for this XCP value (%g)", value);
2498 }
2499 return NULL;
2500 }
2501