~ubuntu-branches/ubuntu/saucy/nut/saucy

« back to all changes in this revision

Viewing changes to drivers/bestfcom.c

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler
  • Date: 2005-07-20 19:48:50 UTC
  • mto: (16.1.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20050720194850-oo61wjr33rrx2mre
Tags: upstream-2.0.2
ImportĀ upstreamĀ versionĀ 2.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
   bestfcom.c - model specific routines for Best Power F-Command ups models
 
3
 
 
4
   This module is yet another rewritten mangle of the bestuferrups
 
5
   driver.  This driver was written in an attempt to consolidate
 
6
   the various Best Fortress/FERRUPS modules that support the
 
7
   'f'-command set and provide support for more of these models.
 
8
 
 
9
   Models tested with this new version:
 
10
   FortressII   LI720
 
11
   FERRUPS      FE2.1K
 
12
   FERRUPS      FE4.3K
 
13
   FERRUPS      FE18K
 
14
   FERRUPS      FD4.3K
 
15
 
 
16
   From bestuferrups.c :
 
17
 
 
18
   This module is a 40% rewritten mangle of the bestfort module by
 
19
   Grant, which is a 75% rewritten mangle of the bestups module by
 
20
   Russell.   It has no test battery command since my ME3100 does this
 
21
   by itself. (same as Grant's driver in this respect)
 
22
 
 
23
   Copyright (C) 2002  Andreas Wrede  <andreas@planix.com>
 
24
   Copyright (C) 2000  John Stone  <johns@megapixel.com>
 
25
   Copyright (C) 2000  Grant Taylor <gtaylor@picante.com>
 
26
   Copyright (C) 1999  Russell Kroll <rkroll@exploits.org>
 
27
 
 
28
   This program is free software; you can redistribute it and/or modify
 
29
   it under the terms of the GNU General Public License as published by
 
30
   the Free Software Foundation; either version 2 of the License, or
 
31
   (at your option) any later version.
 
32
 
 
33
   This program is distributed in the hope that it will be useful,
 
34
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
35
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
36
   GNU General Public License for more details.
 
37
 
 
38
   You should have received a copy of the GNU General Public License
 
39
   along with this program; if not, write to the Free Software
 
40
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
41
 
 
42
*/
 
43
 
 
44
#define DRV_VERSION "0.11"
 
45
 
 
46
#include "main.h"
 
47
#include "serial.h"
 
48
 
 
49
#define ENDCHAR                 '\r'
 
50
#define IGNCHARS                "\012"
 
51
#define UPSDELAY                1
 
52
 
 
53
/* BEST Factory UPS Model Codes */
 
54
#define FORTRESS                00
 
55
#define PATRIOT                 01
 
56
#define FORTRESSII              02
 
57
#define FERRUPS                 03
 
58
#define UNITY1                  04
 
59
 
 
60
/* Internal driver UPS Model Codes */
 
61
#define UNKNOWN                 000
 
62
#define FDxxxx                  100
 
63
#define FExxxx                  200
 
64
#define LIxxxx                  300
 
65
#define MExxxx                  400
 
66
#define MDxxxx                  500
 
67
 
 
68
#include <stdio.h>
 
69
#include <string.h>
 
70
#include <unistd.h>
 
71
 
 
72
/* Blob of UPS configuration data from the formatconfig string */
 
73
struct {
 
74
        int valid;                      /* set to 1 when this is filled in */
 
75
 
 
76
        float  idealbvolts;             /* various interesting battery voltages */
 
77
        float  fullvolts;
 
78
        float  lowvolts;
 
79
        float  emptyvolts;
 
80
 
 
81
        int va;                         /* capacity of UPS in Volt-Amps */
 
82
        int watts;                      /* capacity of UPS in watts */
 
83
        int model;                      /* enumerated model type */
 
84
        int type;                       /* enumerated ups type*/
 
85
 
 
86
        char name[16];                  /* ups type name*/
 
87
 
 
88
} fc;
 
89
 
 
90
static int inverter_status;
 
91
 
 
92
/* Forward decls */
 
93
 
 
94
/* Set up all the funky shared memory stuff used to communicate with upsd */
 
95
void  upsdrv_initinfo (void)
 
96
{
 
97
        /* now set up room for all future variables that are supported */
 
98
 
 
99
        /*
 
100
        dstate_setinfo("driver.name", "%s", "bestfcom");
 
101
        */
 
102
        dstate_setinfo("ups.mfr", "%s", "Best Power");
 
103
        switch(fc.model) {
 
104
          case MExxxx:
 
105
                dstate_setinfo("ups.model", "%s ME%d", fc.name, fc.va);
 
106
                break;
 
107
          case MDxxxx:
 
108
                dstate_setinfo("ups.model", "%s MD%d", fc.name, fc.va);
 
109
                break;
 
110
          case FDxxxx:
 
111
                dstate_setinfo("ups.model", "%s FD%d", fc.name, fc.va);
 
112
                break;
 
113
          case FExxxx:
 
114
                dstate_setinfo("ups.model", "%s FE%d", fc.name, fc.va);
 
115
                break;
 
116
          case LIxxxx:
 
117
                dstate_setinfo("ups.model", "%s LI%d", fc.name, fc.va);
 
118
                break;
 
119
          default:
 
120
                exit(EXIT_FAILURE); /* Will never get here, upsdrv_initups() will catch */
 
121
        } 
 
122
 
 
123
        dstate_setinfo("battery.voltage.nominal", "%05.2f", (double)fc.idealbvolts);
 
124
 
 
125
        /* Do we really need to waste time on this? */
 
126
        /*
 
127
        if (fc.model != FDxxxx) {
 
128
                if (execute("d 00\r", tmp, sizeof(tmp)) > 0)
 
129
                                sscanf(tmp, "00 Time %8s", time);
 
130
 
 
131
                if (execute("d 10\r", tmp, sizeof(tmp)) > 0)
 
132
                                sscanf(tmp, "10 Date %8s", date);
 
133
 
 
134
                dstate_setinfo("ups.time", "%s", time);
 
135
                dstate_setinfo("ups.date", "%s", date);
 
136
                }
 
137
        */
 
138
 
 
139
        upsdebugx(1, "Best Power %s detected", dstate_getinfo("ups.model"));
 
140
        upsdebugx(1, "Battery voltages: %5.2f nominal, %5.2f full, %5.2f low, %5.2f empty",
 
141
                fc.idealbvolts,
 
142
                fc.fullvolts,
 
143
                fc.lowvolts,
 
144
                fc.emptyvolts);
 
145
}
 
146
 
 
147
 
 
148
/* atoi() without the freebie octal conversion */
 
149
int bcd2i (const char *bcdstring, const int bcdlen)
 
150
{
 
151
        int i, digit, total = 0, factor = 1;
 
152
        for (i = 1; i < bcdlen; i++)
 
153
                factor *= 10;
 
154
        for (i = 0; i < bcdlen; i++) {
 
155
                digit = bcdstring[i] - '0';
 
156
                if (digit > 9) {
 
157
                        digit = 0;
 
158
                }
 
159
                total += digit * factor;
 
160
                factor /= 10;
 
161
        }
 
162
        return total;
 
163
}
 
164
 
 
165
#define POLL_ALERT "{"
 
166
static void alert_handler(char ch)
 
167
{
 
168
        char buf[256];
 
169
 
 
170
        /* Received an Inverter status alarm :
 
171
         * "\r\n{Inverter:     On}\r\n=>"
 
172
         * Try to flush the message
 
173
         */
 
174
        ser_get_line(upsfd, buf, sizeof(buf), '\012', "", 0, 20);
 
175
}
 
176
 
 
177
/* Debugging display from kermit:
 
178
----------------------------------------------------
 
179
time^M^M^JFeb 20, 22:13:32^M^J^M^J=>id^M^JUnit ID "ME3.1K12345"^M^J^M^J=>
 
180
----------------------------------------------------
 
181
*/
 
182
static int execute(const char *cmd, char *result, int resultsize)
 
183
{
 
184
        int ret;
 
185
        char ch, buf[256];
 
186
 
 
187
        /* Check for the Inverter status alarm if pending :
 
188
         * "\r\n{Inverter:     On}\r\n=>"
 
189
         */
 
190
        ser_get_line_alert(upsfd, buf, sizeof(buf), '\012', "",
 
191
                POLL_ALERT, alert_handler, 0, 20);
 
192
 
 
193
        ser_send(upsfd, cmd);
 
194
 
 
195
        /* delete command echo up to \012 but no further */
 
196
        for (ch = '\0'; ch != '\012'; ser_get_char(upsfd, &ch, 0, 10));
 
197
 
 
198
        /* get command response */
 
199
        ret = ser_get_line(upsfd, result, resultsize, '\015', "\012", 3, 0);
 
200
 
 
201
        return ret;
 
202
}
 
203
 
 
204
 
 
205
/*
 
206
format command response -> 80 chars
 
207
                 chrg                                               line status
 
208
                  ||alrm                                                ||
 
209
Date          Invtr |12|                                             error
 
210
|  ||Time|      ||      |Vi||Vo|    |Io|| VA |    |Vb||Hz||rt|        ||  |vr|CS
 
211
011314581801000000010000011601160000002300026600000265600000190000000000E00106E6\r
 
212
01161706430100010001000002040121000000980011890000057959980001002200000064080727\r
 
213
011800364801000100010000021301200000003100037100001343599803060024000000680807A0\r
 
214
0121022719010001000100000208011900190000000000000005676001082200350000000006101A\r
 
215
00000000000100000000000002370236000000220005190000026850000009002600000000030161\r
 
216
0    0    1    1    2    2    3    3    4    4    5    5    6    6    7    7    8
 
217
0    5    0    5    0    5    0    5    0    5    0    5    0    5    0    5    0
 
218
 
 
219
Above f-responses listed in this order:
 
220
 FortressII     LI720
 
221
 FERRUPS        FE4.3K
 
222
 FERRUPS        FE18K
 
223
 FERRUPS        FD4.3K
 
224
 Fortress       ?????? (from Holger's old Best Fortress notes)
 
225
*/
 
226
 
 
227
void upsdrv_updateinfo(void)
 
228
{
 
229
        char fstring[512];
 
230
 
 
231
        if (! fc.valid) {
 
232
                upsdebugx(1, "upsupdate run before ups_ident() read ups config");
 
233
                assert(0);
 
234
        }
 
235
 
 
236
        if (execute("f\r", fstring, sizeof(fstring)) >= 80) {
 
237
                int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, 
 
238
                        alstat=0, vaout=0;
 
239
 
 
240
                double ampsout=0.0, vbatt=0.0, battpercent=0.0, loadpercent=0.0,
 
241
                        upstemp=0.0, acfreq=0.0;
 
242
 
 
243
                char date[9], time[9], tmp[32];
 
244
 
 
245
                upsdebugx(3, "f response: %d %s", strlen(fstring), fstring);
 
246
 
 
247
                date[0]='\0';
 
248
                time[0]='\0';
 
249
 
 
250
                /* Inverter status.      0=off 1=on */
 
251
                inverter = bcd2i(&fstring[16], 2);
 
252
 
 
253
                /* Charger status.      0=off 1=on */
 
254
                charger = bcd2i(&fstring[18], 2);
 
255
                
 
256
                /* Input Voltage. integer number */
 
257
                vin      = bcd2i(&fstring[24], 4);
 
258
 
 
259
                /* Output Voltage. integer number */
 
260
                vout = bcd2i(&fstring[28], 4);
 
261
 
 
262
                /* Battery voltage.      int times 10 */
 
263
                vbatt = ((double)bcd2i(&fstring[50], 4) / 10.0);
 
264
 
 
265
                /* Alarm status reg 1.  Bitmask */
 
266
                alstat = bcd2i(&fstring[20], 2);
 
267
 
 
268
                /* Alarm status reg 2.  Bitmask */
 
269
                alstat = alstat | (bcd2i(&fstring[22], 2) << 8);
 
270
 
 
271
                /* AC line frequency */
 
272
                acfreq = ((double)bcd2i(&fstring[54], 4) / 100.0);
 
273
 
 
274
                /* Runtime remaining */
 
275
                btimeleft = bcd2i(&fstring[58], 4);
 
276
 
 
277
                if (fc.model != FDxxxx) {
 
278
                        /* Iout.  int times 10 */
 
279
                        ampsout = ((double)bcd2i(&fstring[36], 4) / 10.0);
 
280
 
 
281
                        /* Volt-amps out.  int  */
 
282
                        vaout = ((double)bcd2i(&fstring[40], 6) / 10);
 
283
 
 
284
                        /* Line status.  Bitmask */
 
285
                        linestat = bcd2i(&fstring[72], 2);
 
286
 
 
287
                }
 
288
 
 
289
                if (fc.model != LIxxxx) {
 
290
                        upstemp = (double) bcd2i(&fstring[62], 4);
 
291
                }
 
292
 
 
293
                /* Percent Load */
 
294
                switch(fc.model) {
 
295
                  case LIxxxx:
 
296
                  case FDxxxx:
 
297
                  case FExxxx:
 
298
                  case MExxxx:
 
299
                        if (execute("d 16\r", tmp, sizeof(tmp)) > 0) {
 
300
                                int l;
 
301
                                sscanf(tmp, "16 FullLoad%% %d", &l);
 
302
                                loadpercent = (double) l;
 
303
                        }
 
304
                        break;
 
305
                  case MDxxxx:
 
306
                        if (execute("d 22\r", tmp, sizeof(tmp)) > 0) {
 
307
                                int l;
 
308
                                sscanf(tmp, "22 FullLoad%% %d", &l);
 
309
                                loadpercent = (double) l;
 
310
                        }
 
311
                        break;
 
312
                  default: /* Will never happen, caught in upsdrv_initups() */
 
313
                        upsdebugx(1, "Uknown model in upsdrv_updateinfo()");
 
314
                        exit(EXIT_FAILURE);
 
315
                }
 
316
 
 
317
                /* Compute battery percent left based on battery voltages. */
 
318
                battpercent = ((vbatt - fc.emptyvolts) 
 
319
                                           / (fc.fullvolts - fc.emptyvolts) * 100.0);
 
320
                if (battpercent < 0.0) 
 
321
                        battpercent = 0.0;
 
322
                else if (battpercent > 100.0)
 
323
                        battpercent = 100.0;
 
324
                
 
325
                /* Compute status string */
 
326
                {
 
327
                        int lowbatt, lowvolts, overload, replacebatt, boosting, trimming;
 
328
 
 
329
                        lowbatt = alstat & (1<<1);
 
330
                        overload = alstat & (1<<6);
 
331
                        replacebatt = alstat & (1<<10);
 
332
 
 
333
                        boosting = inverter && (linestat & (1<<2)) && (vin < 115);
 
334
                        trimming = inverter && (linestat & (1<<2)) && (vin > 115);
 
335
 
 
336
                        /* status bits can be unreliable, so try to help it out */
 
337
                        lowvolts = (vbatt <= fc.lowvolts);
 
338
 
 
339
                        status_init();
 
340
                         
 
341
                        if (inverter) {
 
342
                                if (inverter_status < 1) {
 
343
                                        upsdebugx(1, "Inverter On, charger: %d battery time left: %d",
 
344
                                                charger, btimeleft);
 
345
                                }
 
346
                                inverter_status = 1;
 
347
                                status_set("OB");
 
348
                        } else {
 
349
                                if (inverter_status) {
 
350
                                        upsdebugx(1, "Inverter Off, charger: %d battery time left: %d",
 
351
                                                charger, btimeleft);
 
352
                                }
 
353
                                inverter_status = 0;
 
354
                                status_set("OL");
 
355
                        }
 
356
 
 
357
                        if (lowbatt | lowvolts)
 
358
                                status_set("LB");
 
359
 
 
360
                        if (trimming)
 
361
                                status_set("TRIM");
 
362
 
 
363
                        if (boosting)
 
364
                                status_set("BOOST");
 
365
 
 
366
                        if (replacebatt)
 
367
                                status_set("RB");
 
368
 
 
369
                        if (overload)
 
370
                                status_set("OVER");
 
371
 
 
372
                        status_commit();
 
373
                }
 
374
 
 
375
                upsdebugx(2,
 
376
                        "Poll: inverter %d charger %d vin %d vout %d vaout %d btimeleft %d",
 
377
                                inverter, charger, vin, vout, vaout, btimeleft);
 
378
                upsdebugx(2,
 
379
                        "      vbatt %5.1f batpcnt %5.1f loadpcnt %5.1f upstemp %5.1f ampsout %5.1f acfreq %5.2f",
 
380
                                vbatt, battpercent, loadpercent, upstemp, ampsout, acfreq);
 
381
 
 
382
                /* Stuff information into info structures */
 
383
                dstate_setinfo("input.voltage", "%05.1f", (double)vin);
 
384
                dstate_setinfo("input.frequency", "%05.2f", acfreq);
 
385
                dstate_setinfo("output.voltage", "%05.1f", (double)vout);
 
386
                dstate_setinfo("output.current", "%04.1f", ampsout);
 
387
                dstate_setinfo("battery.charge", "%02.1f", battpercent);
 
388
                dstate_setinfo("battery.voltage", "%02.1f", vbatt);
 
389
                dstate_setinfo("battery.runtime", "%d", btimeleft);
 
390
                dstate_setinfo("ups.load", "%02.1f", loadpercent);
 
391
                if (upstemp)
 
392
                        dstate_setinfo("ups.temperature", "%05.1f", (double)upstemp);
 
393
 
 
394
                dstate_dataok();
 
395
 
 
396
        } else {
 
397
 
 
398
                upsdebugx(1, "failed f response. strlen: %d", strlen(fstring));
 
399
                dstate_datastale();
 
400
 
 
401
        } /* if (execute("f\r", fstring, sizeof(fstring)) >= 80) */
 
402
 
 
403
        return;
 
404
}
 
405
 
 
406
 
 
407
static void ups_sync(void)
 
408
{
 
409
        char buf[256];
 
410
 
 
411
        /* A bit better sanity might be good here. As is, we expect the
 
412
         human to observe the time being totally not a time. */
 
413
 
 
414
        if (execute("time\r", buf, sizeof(buf)) > 0) {
 
415
                upsdebugx(1, "UPS Time: %s", buf);
 
416
        } else {
 
417
                upsdebugx(1, "Error connecting to UPS.");
 
418
                exit(EXIT_FAILURE);
 
419
        }
 
420
}
 
421
 
 
422
/* power down the attached load immediately */
 
423
void upsdrv_shutdown(void)
 
424
{
 
425
        /* NB: hard-wired password */
 
426
        ser_send(upsfd, "pw377\r");
 
427
        ser_send(upsfd, "off 1 a\r");   /* power off in 1 second and restart when line power returns */
 
428
}
 
429
 
 
430
/* list flags and values that you want to receive via -x */
 
431
void upsdrv_makevartable(void)
 
432
{
 
433
}
 
434
 
 
435
void upsdrv_help(void)
 
436
{
 
437
}
 
438
 
 
439
void upsdrv_banner(void)
 
440
{
 
441
        printf("Network UPS Tools - Best Ferrups/Fortress %s (%s)\n",
 
442
                DRV_VERSION, UPS_VERSION);
 
443
}
 
444
 
 
445
static void sync_serial(void) {
 
446
        char buffer[10];
 
447
 
 
448
        ser_flush_in(upsfd, "", 1);
 
449
 
 
450
        ser_send(upsfd, "\r");
 
451
        sleep(UPSDELAY);
 
452
        ser_get_line(upsfd, buffer, sizeof(buffer), '\r', "\012", 3, 0);
 
453
        ser_get_line(upsfd, buffer, sizeof(buffer), ENDCHAR, IGNCHARS, 3, 0);
 
454
 
 
455
        while (ser_get_line(upsfd, buffer, sizeof(buffer), '>', "\012", 3, 0) <= 0) {
 
456
                printf(".");
 
457
                ser_send(upsfd, "\r");
 
458
                sleep(UPSDELAY);
 
459
        }
 
460
}
 
461
 
 
462
/* Begin code stolen from bestups.c */
 
463
static void setup_serial(void)
 
464
{
 
465
        struct termios tio;
 
466
                         
 
467
        if (tcgetattr(upsfd, &tio) == -1)
 
468
                fatal("tcgetattr");
 
469
                                 
 
470
        tio.c_iflag = IXON | IXOFF;
 
471
        tio.c_oflag = 0;
 
472
        tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL);
 
473
        tio.c_lflag = 0;
 
474
        tio.c_cc[VMIN] = 1;
 
475
        tio.c_cc[VTIME] = 0;
 
476
 
 
477
#ifdef HAVE_CFSETISPEED
 
478
        cfsetispeed(&tio, B1200); /* baud change here */
 
479
        cfsetospeed(&tio, B1200);
 
480
#else
 
481
#error This system lacks cfsetispeed() and has no other means to set the speed
 
482
#endif
 
483
 
 
484
        if (tcsetattr(upsfd, TCSANOW, &tio) == -1)
 
485
                fatal("tcsetattr");
 
486
/* end code stolen from bestups.c */
 
487
 
 
488
        sync_serial();
 
489
}
 
490
 
 
491
/*
 
492
These models don't support the formatconfig (fc) command so use
 
493
the identify command.
 
494
 
 
495
"id\r" returns :
 
496
 
 
497
FERRUPS Uninterruptible Power System
 
498
By Best Power Technology, Inc.
 
499
Route 1 Highway 80 / P.O. Box 280
 
500
Necedah, WI      54646  USA
 
501
Sales:   (800) 356-5794
 
502
Service: (800) 356-5737
 
503
FAX:     (608) 565-2221
 
504
 
 
505
Copyright (C) 1993, 1994, 1995 Best Power Technology, Inc.
 
506
 
 
507
Model:    FE4.3KVA
 
508
Unit ID:  FE4.3K02376
 
509
Serial #: FE4.3K02376
 
510
Version:  8.07
 
511
Released: 08/01/1995
 
512
*/
 
513
 
 
514
void upsdrv_init_nofc()
 
515
{
 
516
        char tmp[256], rstring[1024];
 
517
 
 
518
        /* This is a Best UPS   
 
519
         * Set initial values for old Fortress???
 
520
         */
 
521
 
 
522
        /* Attempt the id command       */
 
523
        ser_send(upsfd, "id\r");
 
524
 
 
525
        /* prevent upsrecv from timing out      */
 
526
        sleep(UPSDELAY);
 
527
 
 
528
        ser_get_line(upsfd, rstring, sizeof(rstring), '>', "", 3, 0);
 
529
 
 
530
        rstring[sizeof(rstring)] = '\0';
 
531
        upsdebugx(2, "id response: %s", rstring);
 
532
 
 
533
        /* Better way to identify this unit is using "d 15\r", which results in
 
534
           "15 M#        MD1KVA", "id\r" yields "Unit ID "C1K03588"" */
 
535
        if (strstr(rstring, "Unit ID \"C1K")){
 
536
                fc.model = MDxxxx;
 
537
                snprintf(fc.name, sizeof(fc.name), "%s", "Micro Ferrups");
 
538
 
 
539
                /* Determine load rating by Unit Id? */
 
540
                if (strstr(rstring, "Unit ID \"C1K"))  {
 
541
                        fc.va = 1100;
 
542
                        fc.watts = 770; /* Approximate, based on 0.7 power factor */
 
543
                }
 
544
        } else
 
545
        if (strstr(rstring, "Unit ID \"ME")){
 
546
                fc.model = MExxxx;
 
547
                snprintf(fc.name, sizeof(fc.name), "%s", "Micro Ferrups");
 
548
 
 
549
                /* Determine load rating by Unit Id? */
 
550
                if (strstr(rstring, "Unit ID \"ME3.1K"))  {
 
551
                        fc.va = 3100;
 
552
                        fc.watts = 2200;
 
553
                }
 
554
        } else
 
555
        if (strstr(rstring, "Unit ID \"FD")){
 
556
                fc.model = FDxxxx;
 
557
                snprintf(fc.name, sizeof(fc.name), "%s", "Ferrups");
 
558
 
 
559
                /* Determine load rating by Unit Id? */
 
560
                if (strstr(rstring, "Unit ID \"FD4.3K"))  {
 
561
                        fc.va = 4300;
 
562
                        fc.watts = 3000;
 
563
                }
 
564
        } else
 
565
        if (strstr(rstring, "Model:        FE")){
 
566
                fc.model = FExxxx;
 
567
                fc.type = FERRUPS;
 
568
                snprintf(fc.name, sizeof(fc.name), "%s", "Ferrups");
 
569
        } else
 
570
        if (strlen(rstring) < 300 ) {
 
571
                /* How does the old Fortress respond to this? */
 
572
                upsdebugx(2, "Old Best Fortress???");
 
573
                /* fc.model = FORTRESS; */
 
574
        } 
 
575
 
 
576
        if (fc.model == UNKNOWN) {
 
577
                upsdebugx(1, "Unknown model %s in upsdrv_init_nofc()", rstring);
 
578
                exit(EXIT_FAILURE);
 
579
        }
 
580
 
 
581
        switch(fc.model) {
 
582
          case MExxxx:
 
583
          case MDxxxx:
 
584
          case FDxxxx:
 
585
                /* determine shutdown battery voltage */
 
586
                if (execute("d 27\r", tmp, sizeof(tmp)) > 0) {
 
587
                        sscanf(tmp, "27 LowBatt  %f", &fc.emptyvolts);
 
588
                }
 
589
                /* determine near low battery voltage */
 
590
                if (execute("d 30\r", tmp, sizeof(tmp)) > 0) {
 
591
                        sscanf(tmp, "30 NLBatt  %f", &fc.lowvolts);
 
592
                }
 
593
                /* determine fully charged battery voltage */
 
594
                if (execute("d 28\r", tmp, sizeof(tmp)) > 0) {
 
595
                        sscanf(tmp, "28 Hi Batt  %f", &fc.fullvolts);
 
596
                }
 
597
                fc.fullvolts = 13.70;
 
598
                /* determine "ideal" voltage by a guess */
 
599
                fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts;
 
600
                break;
 
601
          case FExxxx:
 
602
                if (execute("d 45\r", tmp, sizeof(tmp)) > 0) {
 
603
                        sscanf(tmp, "45 RatedVA  %d", &fc.va);                  /* 4300  */
 
604
                }
 
605
                if (execute("d 46\r", tmp, sizeof(tmp)) > 0) {
 
606
                        sscanf(tmp, "46 RatedW  %d", &fc.watts);                /* 3000  */
 
607
                }
 
608
                if (execute("d 65\r", tmp, sizeof(tmp)) > 0) {
 
609
                        sscanf(tmp, "65 LoBatV  %f", &fc.emptyvolts);   /* 41.00 */
 
610
                }
 
611
                if (execute("d 66\r", tmp, sizeof(tmp)) > 0) {
 
612
                        sscanf(tmp, "66 NLBatV  %f", &fc.lowvolts);             /* 44.00 */
 
613
                }
 
614
                if (execute("d 67\r", tmp, sizeof(tmp)) > 0) {
 
615
                        sscanf(tmp, "67 HiBatV  %f", &fc.fullvolts);    /* 59.60 */
 
616
                }
 
617
                fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts;
 
618
                if (fc.va < 1.0) {
 
619
                        upsdebugx(1, "Error determining Ferrups UPS rating.");
 
620
                        exit(EXIT_FAILURE);
 
621
                }
 
622
                break;
 
623
          default:
 
624
                upsdebugx(1, "Unknown model %s in upsdrv_init_nofc()", rstring);
 
625
                exit(EXIT_FAILURE);
 
626
                break;
 
627
        }
 
628
        fc.valid = 1;
 
629
}
 
630
 
 
631
/*
 
632
These models support the formatconfig (fc) command
 
633
 
 
634
formatconfig (fc) response is a one-line packed string starting with $
 
635
   Model        Wt rat  Vout  VHi    FrLo    BatVN  BatNLo  LRuntime    Model
 
636
   |  |         |   |   | |   | |    |  |    |  |    |  |    | |       |      |  E
 
637
rev       VA rat     Vin   VLo   FrN     FrHi    BatHi   BatLo      Opt          O
 
638
 ||        |   |     | |   | |   |  |    |  |    |  |    |  |         |          T
 
639
$010207010600720004701201200911446000570063000240028802150190003??????\LI????VA\\|
 
640
$010207010600720004701201200911446000570063000240028802150190003??????\LI720VU\LI720VU18112\|
 
641
0    0    1    1    2    2    3    3    4    4    5    5    6    6    7    7    8
 
642
0    5    0    5    0    5    0    5    0    5    0    5    0    5    0    5    0
 
643
 
 
644
Model: [0,1] => 00 = unk, 01 = Patriot/SPS, 02 = FortressII, 03 = Ferrups, 04 = Unity/1
 
645
       [2,3] => 00 = LI520, 01 = LI720, 02 = LI1020, 03 = LI1420, 07 = ???
 
646
*/
 
647
 
 
648
void upsdrv_init_fc(const char *fcstring)
 
649
{
 
650
        char tmp[256];
 
651
 
 
652
        upsdebugx(3, "fc response: %d %s", strlen(fcstring), fcstring);
 
653
 
 
654
        /* Obtain Model */
 
655
        if (memcmp(fcstring, "$", 1)) {
 
656
                upsdebugx(1, "Bad response from formatconfig command in upsdrv_init_fc()");
 
657
                exit(EXIT_FAILURE);
 
658
        }
 
659
        if (memcmp(fcstring+3, "00", 2) == 0) {
 
660
                upsdebugx(1, "ups type unknown in upsdrv_init_fc()");
 
661
                exit(EXIT_FAILURE);
 
662
        }
 
663
 
 
664
        if (memcmp(fcstring+3, "01", 2) == 0) {
 
665
                upsdebugx(1, "Best Patriot ups not supported");
 
666
                exit(EXIT_FAILURE);
 
667
        }
 
668
        else if (memcmp(fcstring+3, "02", 2) == 0) {
 
669
                snprintf(fc.name, sizeof(fc.name), "%s", "FortressII");
 
670
                fc.type = FORTRESSII;
 
671
        }
 
672
        else if (memcmp(fcstring+3, "03", 2) == 0) {
 
673
                snprintf(fc.name, sizeof(fc.name), "%s", "Ferrups");
 
674
                fc.type = FERRUPS;
 
675
        }
 
676
        else if (memcmp(fcstring+3, "04", 2) == 0) {
 
677
                snprintf(fc.name, sizeof(fc.name), "%s", "Unity/1");
 
678
                fc.type = UNITY1;
 
679
        }
 
680
 
 
681
        /*      (fc.type == FORTRESSII || fc.type == FERRUPS || fc.type == UNITY1) */
 
682
        if (memcmp(fcstring+5, "00", 2) == 0) {
 
683
                /* fc.model = LI520; */
 
684
                fc.model = LIxxxx;
 
685
        }
 
686
        else if (memcmp(fcstring+5, "01", 2) == 0) {
 
687
                /* fc.model = LI720; */
 
688
                fc.model = LIxxxx;
 
689
        }
 
690
        else if (memcmp(fcstring+5, "02", 2) == 0) {
 
691
                /* fc.model = LI1020; */
 
692
                fc.model = LIxxxx;
 
693
        }
 
694
        else if (memcmp(fcstring+5, "03", 2) == 0) {
 
695
                /* fc.model = LI1420; */
 
696
                fc.model = LIxxxx;
 
697
        }
 
698
        else if (memcmp(fcstring+71, "LI", 2) == 0) {
 
699
                fc.model = LIxxxx;
 
700
        }
 
701
 
 
702
        switch(fc.model) {
 
703
          case LIxxxx:
 
704
                fc.va = bcd2i(&fcstring[11], 5);
 
705
                fc.watts = bcd2i(&fcstring[16], 5);
 
706
 
 
707
                /* determine shutdown battery voltage */
 
708
                fc.emptyvolts= ((double)bcd2i(&fcstring[57], 4) / 10.0);
 
709
 
 
710
                /* determine fully charged battery voltage */
 
711
                fc.lowvolts= ((double)bcd2i(&fcstring[53], 4) / 10.0);
 
712
 
 
713
                /* determine fully charged battery voltage */
 
714
                fc.fullvolts= ((double)bcd2i(&fcstring[49], 4) / 10.0);
 
715
 
 
716
                /* determine "ideal" voltage by a guess */
 
717
                fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts;
 
718
                break;
 
719
          default:
 
720
                upsdebugx(1, "Unknown model %s in upsdrv_init_fc()", tmp);
 
721
                exit(EXIT_FAILURE);
 
722
                break;
 
723
        }
 
724
        fc.valid = 1;
 
725
}
 
726
 
 
727
void upsdrv_initups ()
 
728
{
 
729
        char rstring[256];
 
730
 
 
731
        upsfd = ser_open(device_path);
 
732
        ser_set_speed(upsfd, device_path, B1200);
 
733
        setup_serial();
 
734
        ups_sync();
 
735
 
 
736
        experimental_driver = 1;
 
737
 
 
738
        inverter_status = 0;
 
739
        fc.model = UNKNOWN;
 
740
        if (execute("f\r", rstring, sizeof(rstring)) < 0 ) {
 
741
                upsdebugx(1, "Failed format request in upsdrc_initups()");
 
742
                exit(EXIT_FAILURE);
 
743
        }
 
744
 
 
745
        execute("fc\r", rstring, sizeof(rstring));
 
746
        if (strlen(rstring) < 80 ) {
 
747
                upsdrv_init_nofc();
 
748
        } else {
 
749
                upsdrv_init_fc(rstring);
 
750
        }
 
751
 
 
752
        return;
 
753
}
 
754
 
 
755
void upsdrv_cleanup(void)
 
756
{
 
757
        ser_close(upsfd, device_path);
 
758
}