apcupsd-ups: ignore generated files
[networkupstools/kirr.git] / drivers / mge-utalk.c
blob2935e91d68ea47d3787d12a2bd7d20ab086587e7
1 /* mge-utalk.c - monitor MGE UPS for NUT with UTalk protocol
3 * Copyright (C) 2002 - 2005
4 * Arnaud Quette <arnaud.quette@gmail.com>
5 * Hans Ekkehard Plesser <hans.plesser@itf.nlh.no>
6 * Martin Loyer <martin@degraaf.fr>
7 * Patrick Agrain <patrick.agrain@alcatel.fr>
8 * Nicholas Reilly <nreilly@magma.ca>
9 * Dave Abbott <d.abbott@dcs.shef.ac.uk>
10 * Marek Kralewski <marek@mercy49.de>
12 * This driver is a collaborative effort by the above people,
13 * Sponsored by MGE UPS SYSTEMS
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 * IMPLEMENTATION DETAILS
34 * Not all UTalk models provide all possible information, settings and commands.
35 * mge-utalk checks on startup which variables and commands are available from
36 * the UPS, and re-reads these regularly. Thus, startup is a bit slow, but this
37 * should not matter much.
39 * mge-utalk.h defines a struct array that tells the driver how to read
40 * variables from the UPS and publish them as NUT data.
42 * "ups.status" variable is not included in this array, since it contains
43 * information that requires several calls to the UPS and more advanced analysis
44 * of the reponses. The function get_ups_status does this job.
46 * Note that MGE enumerates the status "bits" from right to left,
47 * i.e., if buf[] contains the reponse to command "Ss" (read system status),
48 * then buf[0] contains "bit" Ss.1.7 (General alarm), while buf[7] contains
49 * "bit" Ss.1.0 (Load unprotected).
51 * enable_ups_comm() is called before each attempt to read/write data
52 * from/to the UPS to re synchronise the communication.
55 #include <ctype.h>
56 #include <sys/ioctl.h>
57 #include "timehead.h"
58 #include "main.h"
59 #include "serial.h"
60 #include "mge-utalk.h"
62 /* --------------------------------------------------------------- */
63 /* Define "technical" constants */
64 /* --------------------------------------------------------------- */
66 #define DRIVER_NAME "MGE UPS SYSTEMS/U-Talk driver"
67 #define DRIVER_VERSION "0.93"
70 /* driver description structure */
71 upsdrv_info_t upsdrv_info = {
72 DRIVER_NAME,
73 DRIVER_VERSION,
74 "Arnaud Quette <ArnaudQuette@gmail.com>\n" \
75 "Hans Ekkehard Plesser <hans.plesser@itf.nlh.no>\n" \
76 "Martin Loyer <martin@degraaf.fr>\n" \
77 "Patrick Agrain <patrick.agrain@alcatel.fr>\n" \
78 "Nicholas Reilly <nreilly@magma.ca>\n" \
79 "Dave Abbott <d.abbott@dcs.shef.ac.uk>\n" \
80 "Marek Kralewski <marek@mercy49.de>",
81 DRV_STABLE,
82 { NULL }
85 /* delay after sending each char to UPS (in MICROSECONDS) */
86 #define MGE_CHAR_DELAY 0
88 /* delay after command, before reading UPS reply (in MICROSECONDS) */
89 #define MGE_REPLY_DELAY 1000
91 /* delay after enable_ups_comm */
92 #define MGE_CONNECT_DELAY 500000
94 #define MGE_COMMAND_ENDCHAR "\r\n" /* some UPS need \r and \n */
95 #define MGE_REPLY_ENDCHAR '\r'
96 #define MGE_REPLY_IGNCHAR "\r\n"
98 #define MAXTRIES 10 /* max number of connect tries */
99 #define BUFFLEN 256
101 #define SD_RETURN 0
102 #define SD_STAYOFF 1
104 int sdtype = SD_RETURN;
105 static time_t lastpoll; /* Timestamp the last polling */
107 /* --------------------------------------------------------------- */
108 /* Structure with information about UPS */
109 /* --------------------------------------------------------------- */
111 static struct {
112 int MultTab;
113 int LowBatt; /* percent */
114 int OnDelay; /* minutes */
115 int OffDelay; /* seconds */
116 } mge_ups = { 0, DEFAULT_LOWBATT, DEFAULT_ONDELAY, DEFAULT_OFFDELAY };
119 /* --------------------------------------------------------------- */
120 /* Declaration of internal functions */
121 /* --------------------------------------------------------------- */
123 static int instcmd(const char *cmdname, const char *extra);
124 static int setvar(const char *varname, const char *val);
125 static void enable_ups_comm(void);
126 static void disable_ups_comm(void);
127 static void extract_info(const char *buf, const mge_info_item_t *mge,
128 char *infostr, int infolen);
129 static const char *info_variable_cmd(const char *type);
130 static bool_t info_variable_ok(const char *type);
131 static int get_ups_status(void);
132 static int mge_command(char *reply, int replylen, const char *fmt, ...);
134 /* --------------------------------------------------------------- */
135 /* UPS Driver Functions */
136 /* --------------------------------------------------------------- */
138 void upsdrv_makevartable(void)
140 char temp[BUFFLEN];
142 snprintf(temp, sizeof(temp),
143 "Low battery level, in %% (default = %3d)",
144 DEFAULT_LOWBATT);
145 addvar (VAR_VALUE, "LowBatt", temp);
147 snprintf(temp, sizeof(temp),
148 "Delay before startup, in minutes (default = %3d)",
149 DEFAULT_ONDELAY);
150 addvar (VAR_VALUE, "OnDelay", temp);
152 snprintf(temp, sizeof(temp),
153 "Delay before shutdown, in seconds (default = %3d)",
154 DEFAULT_OFFDELAY);
155 addvar (VAR_VALUE, "OffDelay", temp);
157 addvar(VAR_FLAG, "oldmac", "Enable Oldworld Apple Macintosh support");
160 /* --------------------------------------------------------------- */
162 void upsdrv_initups(void)
164 char buf[BUFFLEN];
165 int RTS = TIOCM_RTS;
167 upsfd = ser_open(device_path);
168 ser_set_speed(upsfd, device_path, B2400);
170 /* read command line/conf variable that affect comm. */
171 if (testvar ("oldmac"))
172 RTS = ~TIOCM_RTS;
174 /* Init serial line */
175 ioctl(upsfd, TIOCMBIC, &RTS);
176 enable_ups_comm();
178 /* Try to set "Low Battery Level" (if supported and given) */
179 if (getval ("lowbatt"))
181 mge_ups.LowBatt = atoi (getval ("lowbatt"));
182 /* Set the value in the UPS */
183 mge_command(buf, sizeof(buf), "Bl %d", mge_ups.LowBatt);
184 if(!strcmp(buf, "OK"))
185 upsdebugx(1, "Low Battery Level set to %d%%", mge_ups.LowBatt);
186 else
187 upsdebugx(1, "initups: Low Battery Level cannot be set");
190 /* Try to set "ON delay" (if supported and given) */
191 if (getval ("ondelay"))
193 mge_ups.OnDelay = atoi (getval ("ondelay"));
194 /* Set the value in the UPS */
195 mge_command(buf, sizeof(buf), "Sm %d", mge_ups.OnDelay);
196 if(!strcmp(buf, "OK"))
197 upsdebugx(1, "ON delay set to %d min", mge_ups.OnDelay);
198 else
199 upsdebugx(1, "initups: OnDelay unavailable");
202 /* Try to set "OFF delay" (if supported and given) */
203 if (getval ("offdelay"))
205 mge_ups.OffDelay = atoi (getval ("offdelay"));
206 /* Set the value in the UPS */
207 mge_command(buf, sizeof(buf), "Sn %d", mge_ups.OffDelay);
208 if(!strcmp(buf, "OK"))
209 upsdebugx(1, "OFF delay set to %d sec", mge_ups.OffDelay);
210 else
211 upsdebugx(1, "initups: OffDelay unavailable");
215 /* --------------------------------------------------------------- */
217 void upsdrv_initinfo(void)
219 char buf[BUFFLEN];
220 const char *model = NULL;
221 char *firmware = NULL;
222 char *p;
223 char *v = NULL; /* for parsing Si output, get Version ID */
224 int table;
225 int tries;
226 int status_ok = 0;
227 int bytes_rcvd;
228 int si_data1 = 0;
229 int si_data2 = 0;
230 mge_info_item_t *item;
231 models_name_t *model_info;
232 mge_model_info_t *legacy_model;
233 char infostr[32];
234 int chars_rcvd;
236 /* manufacturer -------------------------------------------- */
237 dstate_setinfo("ups.mfr", "MGE UPS SYSTEMS");
239 /* loop until we have at status */
240 tries = 0;
241 do {
242 printf(".");
244 /* get model information in ASCII string form: <Family> <Model> <Firmware> */
245 bytes_rcvd = mge_command(buf, sizeof(buf), "Si 1");
247 if(bytes_rcvd > 0 && buf[0] != '?') {
248 dstate_setinfo("ups.id", "%s", buf); /* raw id */
250 model = buf;
252 p = strrchr(buf, ' ');
253 if ( p != NULL ) {
254 *p = '\0';
255 firmware = p+1;
258 if( firmware && strlen(firmware) < 1 )
259 firmware = NULL; /* no firmware information */
261 /* Parsing model names table */
262 for ( model_info = Si1_models_names ; model_info->basename != NULL ; model_info++ ) {
263 if(!strcasecmp(model_info->basename, model))
265 model = model_info->finalname;
266 upsdebugx(1, "initinfo: UPS model == >%s<", model);
267 break;
271 else
273 upsdebugx(1, "initinfo: 'Si 1' unavailable, switching to 'Si' command");
275 /* get model information, numbered form, : <Model ID> <Version ID> <Firmware> */
276 bytes_rcvd = mge_command(buf, sizeof(buf), "Si");
278 if(bytes_rcvd > 0 && buf[0] != '?') {
279 upsdebugx(1, "initinfo: Si == >%s<", buf);
281 printf("\nCAUTION : This is an older model. It may not support too much polling.\nPlease read man mge-utalk and use pollinterval\n");
283 p = strchr(buf, ' ');
285 if ( p != NULL ) {
286 *p = '\0';
287 si_data1 = atoi(buf);
288 v = p+1;
289 p = strchr(v, ' ');
292 if ( p != NULL ) {
293 *p = '\0';
294 si_data2 = atoi(v);
297 /* Parsing legacy model table in order to find it */
298 for ( legacy_model = mge_model ; legacy_model->name != NULL ; legacy_model++ ) {
299 if(legacy_model->Data1 == si_data1 && legacy_model->Data2 == si_data2){
300 model = legacy_model->name;
301 upsdebugx(1, "initinfo: UPS model == >%s<", model);
302 break;
306 if( model == NULL )
307 printf("No model found by that model and version ID\nPlease contact us with UPS model, name and reminder info\nReminder info : Data1=%i , Data2=%i\n", si_data1, si_data2);
312 if ( model ) {
313 upsdebugx(2, "Got model name: %s", model);
315 /* deal with truncated model names */
316 if (!strncmp(model, "Evolutio", 8)) {
317 dstate_setinfo("ups.model", "Evolution %i", atoi(strchr(model, ' ')));
318 } else {
319 dstate_setinfo("ups.model", "%s", model);
323 if ( firmware && strcmp(firmware, ""))
324 dstate_setinfo("ups.firmware", "%s", firmware);
325 else
326 dstate_setinfo("ups.firmware", "unknown");
328 /* multiplier table */
329 /* <protocol level> <multiplier table> */
330 bytes_rcvd = mge_command(buf, sizeof(buf), "Ai");
332 if (bytes_rcvd > 0 && buf[0] != '?') {
333 p = strchr(buf, ' ');
334 if ( p != NULL ) {
335 table = atoi(p + 1);
336 if ( 0 < table && table < 4 )
337 mge_ups.MultTab = table;
341 /* status --- try only system status, to get the really important
342 * information (OL, OB, LB); all else is added later by updateinfo */
343 status_ok = get_ups_status();
345 } while ( (!status_ok) && (tries++ < MAXTRIES) && (exit_flag != 1) );
347 if ( tries == MAXTRIES && !status_ok )
348 fatalx(EXIT_FAILURE, "Could not get status from UPS.");
350 if ( mge_ups.MultTab == 0 )
351 upslogx(LOG_WARNING, "Could not get multiplier table: using raw readings.");
353 /* all other variables ------------------------------------ */
354 for ( item = mge_info ; item->type != NULL ; item++ ) {
356 /* Check if we are asked to stop (reactivity++) */
357 if (exit_flag != 0)
358 return;
360 /* send request, read answer */
361 chars_rcvd = mge_command(buf, sizeof(buf), item->cmd);
363 if ( chars_rcvd < 1 || buf[0] == '?' ) {
364 item->ok = FALSE;
365 upsdebugx(1, "initinfo: %s unavailable", item->type);
366 } else {
367 item->ok = TRUE;
368 extract_info(buf, item, infostr, sizeof(infostr));
369 dstate_setinfo(item->type, "%s", infostr);
370 dstate_setflags(item->type, item->flags);
371 upsdebugx(1, "initinfo: %s == >%s<", item->type, infostr);
373 /* Set max length for strings */
374 if (item->flags & ST_FLAG_STRING)
375 dstate_setaux(item->type, item->length);
377 } /* for item */
379 /* store timestamp */
380 lastpoll = time(NULL);
382 /* commands ----------------------------------------------- */
383 /* FIXME: check if available before adding! */
384 dstate_addcmd("load.off");
385 dstate_addcmd("load.on");
386 dstate_addcmd("shutdown.return");
387 dstate_addcmd("shutdown.stayoff");
388 dstate_addcmd("test.panel.start");
389 dstate_addcmd("test.battery.start");
390 dstate_addcmd("bypass.start");
391 dstate_addcmd("bypass.stop");
393 /* install handlers */
394 upsh.setvar = setvar;
395 upsh.instcmd = instcmd;
397 printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path);
400 /* --------------------------------------------------------------- */
402 void upsdrv_updateinfo(void)
404 char buf[BUFFLEN];
405 char infostr[32];
406 int status_ok;
407 int bytes_rcvd;
408 mge_info_item_t *item;
410 /* make sure that communication is enabled */
411 enable_ups_comm();
413 /* update status */
414 status_ok = get_ups_status(); /* only sys status is critical */
415 if ( !status_ok )
417 upslogx(LOG_NOTICE, "updateinfo: Cannot update system status");
418 /* try to re enable communication */
419 disable_ups_comm();
420 enable_ups_comm();
422 else
424 dstate_dataok();
427 /* Don't overload old units (at startup) */
428 if ( (unsigned int)time(NULL) <= (unsigned int)(lastpoll + poll_interval) )
429 return;
431 /* update all other ok variables */
432 for ( item = mge_info ; item->type != NULL ; item++ ) {
433 /* Check if we are asked to stop (reactivity++) */
434 if (exit_flag != 0)
435 return;
437 if ( item->ok ) {
438 /* send request, read answer */
439 bytes_rcvd = mge_command(buf, sizeof(buf), item->cmd);
441 if ( bytes_rcvd > 0 && buf[0] != '?' ) {
442 extract_info(buf, item, infostr, sizeof(infostr));
443 dstate_setinfo(item->type, "%s", infostr);
444 upsdebugx(2, "updateinfo: %s == >%s<", item->type, infostr);
445 dstate_dataok();
446 } else
448 upslogx(LOG_NOTICE, "updateinfo: Cannot update %s", item->type);
449 /* try to re enable communication */
450 disable_ups_comm();
451 enable_ups_comm();
453 } /* if item->ok */
456 /* store timestamp */
457 lastpoll = time(NULL);
460 /* --------------------------------------------------------------- */
462 void upsdrv_shutdown(void)
464 char buf[BUFFLEN];
465 /* static time_t lastcmd = 0; */
466 memset(buf, 0, sizeof(buf));
468 if (sdtype == SD_RETURN) {
469 /* enable automatic restart */
470 mge_command(buf, sizeof(buf), "Sx 5");
472 upslogx(LOG_INFO, "UPS response to Automatic Restart was %s", buf);
475 /* Only call the effective shutoff if restart is ok */
476 /* or if we need only a stayoff... */
477 if (!strcmp(buf, "OK") || (sdtype == SD_STAYOFF)) {
478 /* shutdown UPS */
479 mge_command(buf, sizeof(buf), "Sx 0");
481 upslogx(LOG_INFO, "UPS response to Shutdown was %s", buf);
483 /* if(strcmp(buf, "OK")) */
485 /* call the cleanup to disable/close the comm link */
486 upsdrv_cleanup();
489 /* --------------------------------------------------------------- */
491 void upsdrv_help(void)
495 /* --------------------------------------------------------------- */
496 /* Internal Functions */
497 /* --------------------------------------------------------------- */
499 /* handler for commands to be sent to UPS */
500 int instcmd(const char *cmdname, const char *extra)
502 char temp[BUFFLEN];
504 /* Start battery test */
505 if (!strcasecmp(cmdname, "test.battery.start"))
507 mge_command(temp, sizeof(temp), "Bx 1");
508 upsdebugx(2, "UPS response to %s was %s", cmdname, temp);
510 if(strcmp(temp, "OK"))
511 return STAT_INSTCMD_UNKNOWN;
512 else
513 return STAT_INSTCMD_HANDLED;
516 /* Start front panel test */
517 if (!strcasecmp(cmdname, "test.panel.start"))
519 mge_command(temp, sizeof(temp), "Sx 129");
520 upsdebugx(2, "UPS response to %s was %s", cmdname, temp);
522 if(strcmp(temp, "OK"))
523 return STAT_INSTCMD_UNKNOWN;
524 else
525 return STAT_INSTCMD_HANDLED;
528 /* Shutdown UPS */
529 if (!strcasecmp(cmdname, "shutdown.stayoff"))
531 sdtype = SD_STAYOFF;
532 upsdrv_shutdown();
535 if (!strcasecmp(cmdname, "shutdown.return"))
537 sdtype = SD_RETURN;
538 upsdrv_shutdown();
541 /* Power Off [all] plugs */
542 if (!strcasecmp(cmdname, "load.off"))
544 /* TODO: Powershare (per plug) control */
545 mge_command(temp, sizeof(temp), "Wy 65535");
546 upsdebugx(2, "UPS response to Select All Plugs was %s", temp);
548 if(strcmp(temp, "OK"))
549 return STAT_INSTCMD_UNKNOWN;
550 else
552 mge_command(temp, sizeof(temp), "Wx 0");
553 upsdebugx(2, "UPS response to %s was %s", cmdname, temp);
554 if(strcmp(temp, "OK"))
555 return STAT_INSTCMD_UNKNOWN;
556 else
557 return STAT_INSTCMD_HANDLED;
561 /* Power On all plugs */
562 if (!strcasecmp(cmdname, "load.on"))
564 /* TODO: add per plug control */
565 mge_command(temp, sizeof(temp), "Wy 65535");
566 upsdebugx(2, "UPS response to Select All Plugs was %s", temp);
568 if(strcmp(temp, "OK"))
569 return STAT_INSTCMD_UNKNOWN;
570 else
572 mge_command(temp, sizeof(temp), "Wx 1");
573 upsdebugx(2, "UPS response to %s was %s", cmdname, temp);
574 if(strcmp(temp, "OK"))
575 return STAT_INSTCMD_UNKNOWN;
576 else
577 return STAT_INSTCMD_HANDLED;
581 /* Switch on/off Maintenance Bypass */
582 if ((!strcasecmp(cmdname, "bypass.start"))
583 || (!strcasecmp(cmdname, "bypass.stop")))
585 /* TODO: add control on bypass value */
586 /* read maintenance bypass status */
587 if(mge_command(temp, sizeof(temp), "Ps") > 0)
589 if (temp[0] == '1')
591 /* Disable Maintenance Bypass */
592 mge_command(temp, sizeof(temp), "Px 2");
593 upsdebugx(2, "UPS response to Select All Plugs was %s", temp);
594 } else
596 /* Enable Maintenance Bypass */
597 mge_command(temp, sizeof(temp), "Px 3");
600 upsdebugx(2, "UPS response to %s was %s", cmdname, temp);
602 if(strcmp(temp, "OK"))
603 return STAT_INSTCMD_UNKNOWN;
604 else
605 return STAT_INSTCMD_HANDLED;
609 upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
610 return STAT_INSTCMD_UNKNOWN;
613 /* --------------------------------------------------------------- */
615 /* handler for settable variables in UPS*/
616 int setvar(const char *varname, const char *val)
618 char temp[BUFFLEN];
619 char cmd[15];
621 /* TODO : add some controls */
623 if(info_variable_ok(varname))
625 /* format command */
626 snprintf(cmd, sizeof(cmd), "%s", info_variable_cmd(varname));
627 sprintf(strchr(cmd, '?'), "%s", val);
629 /* Execute command */
630 mge_command(temp, sizeof(temp), cmd);
631 upslogx(LOG_INFO, "setvar: UPS response to Set %s to %s was %s", varname, val, temp);
632 } else
633 upsdebugx(1, "setvar: Variable %s not supported by UPS", varname);
635 return STAT_SET_UNKNOWN;
638 /* --------------------------------------------------------------- */
640 /* disable communication with UPS to avoid interference with
641 * kernel serial init at boot time (ie with V24 init) */
642 static void disable_ups_comm(void)
644 upsdebugx(1, "disable_ups_comm()");
645 ser_flush_in(upsfd, "?\r\n", 0);
646 usleep(MGE_CONNECT_DELAY);
647 mge_command(NULL, 0, "Ax 0");
650 /* enable communication with UPS */
651 static void enable_ups_comm(void)
653 char buf[8];
655 /* send Z twice --- speeds up re-connect */
656 mge_command(NULL, 0, "Z");
657 mge_command(NULL, 0, "Z");
658 /* only enable communication if needed! */
659 if ( mge_command(buf, 8, "Si") <= 0)
661 mge_command(NULL, 0, "Ax 1");
662 usleep(MGE_CONNECT_DELAY);
665 ser_flush_in(upsfd, "?\r\n", nut_debug_level);
668 /* --------------------------------------------------------------- */
670 /* extract information from buffer
671 in: buf : reply from UPS
672 item : INFO item queried
673 out: infostr: to be placed in INFO_ variable
674 NOTE: buf="?" must be handled before calling extract_info
675 buf is changed inspite of const !!!!!
677 static void extract_info(const char *buf, const mge_info_item_t *item,
678 char *infostr, int infolen)
680 /* initialize info string */
681 infostr[0] = '\0';
683 /* write into infostr with proper formatting */
684 if ( strpbrk(item->fmt, "feEgG") ) { /* float */
685 snprintf(infostr, infolen, item->fmt,
686 multiplier[mge_ups.MultTab][item->unit] * atof(buf));
687 } else if ( strpbrk(item->fmt, "dioxXuc") ) { /* int */
688 snprintf(infostr, infolen, item->fmt,
689 (int) (multiplier[mge_ups.MultTab][item->unit] * atof(buf)));
690 } else {
691 snprintf(infostr, infolen, item->fmt, buf);
697 /* --------------------------------------------------------------- */
699 /* get system status, at least: OB, OL, LB
700 calls set_status appropriately
701 tries MAXTRIES times
702 returns non-nil if successful
704 NOTE: MGE counts bytes/chars the opposite way as C,
705 see mge-utalk manpage. If status commands send two
706 data items, these are separated by a space, so
707 the elements of the second item are in buf[16..9].
710 static int get_ups_status(void)
712 char buf[BUFFLEN];
713 int rb_set= FALSE; /* has RB flag been set ? */
714 int over_set= FALSE; /* has OVER flag been set ? */
715 int tries = 0;
716 int ok = FALSE;
717 int bytes_rcvd = 0;
719 do {
720 /* Check if we are asked to stop (reactivity++) */
721 if (exit_flag != 0)
722 return FALSE;
724 /* must clear status buffer before each round */
725 status_init();
727 /* system status */
728 /* FIXME: some old units sometimes return "Syst Stat >1<"
729 resulting in an temporary OB status */
730 bytes_rcvd = mge_command(buf, sizeof(buf), "Ss");
731 upsdebugx(1, "Syst Stat >%s<", buf);
732 if ( bytes_rcvd > 0 && strlen(buf) > 7 ) {
733 ok = TRUE;
734 if (buf[6] == '1') {
735 over_set = TRUE;
736 status_set("OVER");
738 if (buf[5] == '1')
739 status_set("OB");
740 else
741 status_set("OL");
743 if (buf[4] == '1')
744 status_set("LB");
746 if (buf[3] == '1') {
747 rb_set = TRUE;
748 status_set("RB");
750 /* buf[2] not used */
751 if (buf[1] == '1')
752 status_set("COMMFAULT"); /* self-invented */
753 /* FIXME: better to call datastale()?! */
754 if (buf[0] == '1')
755 status_set("ALARM"); /* self-invented */
756 /* FIXME: better to use ups.alarm */
757 } /* if strlen */
759 /* battery status */
760 mge_command(buf, sizeof(buf), "Bs");
761 upsdebugx(1, "Batt Stat >%s<", buf);
762 if ( strlen(buf) > 7 ) {
763 if ( !rb_set && ( buf[7] == '1' || buf[3] == '1' ) )
764 status_set("RB");
766 if (buf[1] == '1')
767 status_set("CHRG");
769 if (buf[0] == '1')
770 status_set("DISCHRG");
771 } /* if strlen */
773 /* load status */
774 mge_command(buf, sizeof(buf), "Ls");
775 upsdebugx(1, "Load Stat >%s<", buf);
776 if ( strlen(buf) > 7 ) {
777 if (buf[4] == '1')
778 status_set("BOOST");
780 if ( !over_set && ( buf[3] == '1' ) )
781 status_set("OVER");
783 if (buf[2] == '1')
784 status_set("TRIM");
785 } /* if strlen */
787 if ( strlen(buf) > 15 ) { /* second "byte", skip <SP> */
788 if (buf[16] == '1') {
789 status_set("OB");
790 status_set("LB");
793 /* FIXME: to be checked (MUST be buf[8]) !! */
794 /* if ( !(buf[9] == '1') ) */
795 /* This is not the OFF status!
796 if ( !(buf[8] == '1') )
797 status_set("OFF"); */
798 } /* if strlen */
800 /* Bypass status */
801 mge_command(buf, sizeof(buf), "Ps");
802 upsdebugx(1, "Bypass Stat >%s<", buf);
803 if ( strlen(buf) > 7 ) {
804 /* FIXME: extend ups.status for BYPASS: */
805 /* Manual Bypass */
806 if (buf[7] == '1')
807 status_set("BYPASS");
808 /* Automatic Bypass */
809 if (buf[6] == '1')
810 status_set("BYPASS");
811 } /* if strlen */
813 } while ( !ok && tries++ < MAXTRIES );
815 status_commit();
817 return ok;
820 /* --------------------------------------------------------------- */
822 /* return proper variable "ok" given INFO_ type */
824 static bool_t info_variable_ok(const char *type)
826 mge_info_item_t *item = mge_info ;
828 while ( strcasecmp(item->type, type ))
829 item++;
831 return item->ok;
834 /* --------------------------------------------------------------- */
836 /* return proper variable "cmd" given INFO_ type */
838 static const char *info_variable_cmd(const char *type)
840 mge_info_item_t *item = mge_info ;
842 while ( strcasecmp(item->type, type ))
843 item++;
845 return item->cmd;
848 /* --------------------------------------------------------------- */
850 /* send command to UPS and read reply if requested
852 reply : buffer for reply, NULL if no reply expected
853 replylen: length of buffer reply
854 fmt : format string, followed by optional data for command
856 returns : no of chars received, -1 if error
858 static int mge_command(char *reply, int replylen, const char *fmt, ...)
860 const char *p;
861 char command[BUFFLEN];
862 int bytes_sent = 0;
863 int bytes_rcvd = 0;
864 int ret;
865 va_list ap;
867 /* build command string */
868 va_start(ap, fmt);
870 ret = vsnprintf(command, sizeof(command), fmt, ap);
872 if ((ret < 1) || (ret >= (int) sizeof(command)))
873 upsdebugx(4, "mge_command: command truncated");
875 va_end(ap);
877 /* Delay a bit to avoid overlap of a previous answer (500 ms), as per
878 * http://old.networkupstools.org/protocols/mge/9261zwfa.pdf ยง 6.1. Timings */
879 usleep(500000);
881 /* flush received, unread data */
882 tcflush(upsfd, TCIFLUSH);
884 /* send command */
885 for (p = command; *p; p++) {
886 if ( isprint(*p & 0xFF) )
887 upsdebugx(4, "mge_command: sending [%c]", *p);
888 else
889 upsdebugx(4, "mge_command: sending [%02X]", *p);
891 if (write(upsfd, p, 1) != 1)
892 return -1;
894 bytes_sent++;
895 usleep(MGE_CHAR_DELAY);
898 /* send terminating string */
899 if (MGE_COMMAND_ENDCHAR) {
900 for (p = MGE_COMMAND_ENDCHAR; *p; p++) {
901 if ( isprint(*p & 0xFF) )
902 upsdebugx(4, "mge_command: sending [%c]", *p);
903 else
904 upsdebugx(4, "mge_command: sending [%02X]", *p);
906 if (write(upsfd, p, 1) != 1)
907 return -1;
909 bytes_sent++;
910 usleep(MGE_CHAR_DELAY);
914 if ( !reply )
915 return bytes_rcvd;
916 else
917 usleep(MGE_REPLY_DELAY);
919 bytes_rcvd = ser_get_line(upsfd, reply, replylen,
920 MGE_REPLY_ENDCHAR, MGE_REPLY_IGNCHAR, 3, 0);
922 upsdebugx(4, "mge_command: received %d byte(s)", bytes_rcvd);
924 return bytes_rcvd;
927 void upsdrv_cleanup(void)
929 upsdebugx(1, "cleaning up");
930 disable_ups_comm();
931 ser_close(upsfd, device_path);