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

« back to all changes in this revision

Viewing changes to drivers/bestuferrups.c

  • Committer: Bazaar Package Importer
  • Author(s): Arnaud Quette
  • Date: 2004-05-28 13:10:01 UTC
  • mto: (16.1.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040528131001-yj2m9qcez4ya2w14
Tags: upstream-1.4.2
ImportĀ upstreamĀ versionĀ 1.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
   bestuferrups.c - model specific routines for Best Power Micro-Ferrups
 
3
 
 
4
   This module is a 40% rewritten mangle of the bestfort module by
 
5
   Grant, which is a 75% rewritten mangle of the bestups module by
 
6
   Russell.   It has no test battery command since my ME3100 does this
 
7
   by itself. (same as Grant's driver in this respect)
 
8
 
 
9
   Copyright (C) 2002  Andreas Wrede  <andreas@planix.com>
 
10
   Copyright (C) 2000  John Stone  <johns@megapixel.com>
 
11
   Copyright (C) 2000  Grant Taylor <gtaylor@picante.com>
 
12
   Copyright (C) 1999  Russell Kroll <rkroll@exploits.org>
 
13
 
 
14
   This program is free software; you can redistribute it and/or modify
 
15
   it under the terms of the GNU General Public License as published by
 
16
   the Free Software Foundation; either version 2 of the License, or
 
17
   (at your option) any later version.
 
18
 
 
19
   This program is distributed in the hope that it will be useful,
 
20
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
22
   GNU General Public License for more details.
 
23
 
 
24
   You should have received a copy of the GNU General Public License
 
25
   along with this program; if not, write to the Free Software
 
26
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
27
 
 
28
*/
 
29
 
 
30
#define DRV_VERSION "0.01"
 
31
 
 
32
#include "main.h"
 
33
 
 
34
#define ENDCHAR         '\r'
 
35
#define IGNCHARS        "\012"
 
36
 
 
37
/* UPS Model Codes */
 
38
#define UNKNOWN         100
 
39
#define ME3100          200
 
40
#define MD1KVA          300 /* Software version P5.05 dated 05/18/89 */
 
41
 
 
42
#include <stdio.h>
 
43
#include <string.h>
 
44
#include <unistd.h>
 
45
 
 
46
int debugging = 0;
 
47
 
 
48
 
 
49
/* Blob of UPS configuration data from the formatconfig string */
 
50
struct {
 
51
  int valid;                    /* set to 1 when this is filled in */
 
52
 
 
53
  float  idealbvolts;           /* various interestin battery voltages */
 
54
  float  fullvolts;
 
55
  float  emptyvolts;
 
56
  int va;                       /* capacity of UPS in Volt-Amps */
 
57
  int watts;                    /* capacity of UPS in watts */
 
58
  int model;                    /* enumerated model type */
 
59
} fc;
 
60
 
 
61
 
 
62
/* Forward decls */
 
63
 
 
64
/* Set up all the funky shared memory stuff used to communicate with upsd */
 
65
void  upsdrv_initinfo (void)
 
66
{
 
67
        /* now set up room for all future variables that are supported */
 
68
 
 
69
        dstate_setinfo("ups.mfr", "%s", "Best Power");
 
70
        switch(fc.model) {
 
71
          case ME3100:
 
72
            dstate_setinfo("ups.model", "Micro Ferrups (ME) %d", fc.va);
 
73
            break;
 
74
          case MD1KVA:
 
75
            dstate_setinfo("ups.model", "Micro Ferrups (MD) %d", fc.va);
 
76
            break;
 
77
          default:
 
78
            exit(1); /* Will never get here, upsdrv_initups() will catch */
 
79
        } 
 
80
        fprintf(stderr, "Best Power %s detected\n", 
 
81
                dstate_getinfo("ups.model"));
 
82
        fprintf(stderr, "Battery voltages %5.1f nominal, %5.1f full, %5.1f empty\n", 
 
83
         fc.idealbvolts,
 
84
         fc.fullvolts,
 
85
         fc.emptyvolts);
 
86
}
 
87
 
 
88
 
 
89
/* Debugging display from kermit:
 
90
 
 
91
----------------------------------------------------
 
92
time^M^M^JFeb 20, 22:13:32^M^J^M^J=>id^M^JUnit ID "ME3.1K12345"^M^J^M^J=>
 
93
----------------------------------------------------
 
94
*/
 
95
 
 
96
int execute(char *cmd, char *result, int resultsize) 
 
97
{
 
98
  int ret;
 
99
  char buf[256];
 
100
  
 
101
  upssend(cmd);
 
102
  upsrecv(buf, sizeof(buf), '\012', "");
 
103
  ret=upsrecv(result, resultsize, '\015', "\012");
 
104
  upsrecv(buf, sizeof(buf), '>', "");
 
105
  return ret;
 
106
 
 
107
}
 
108
 
 
109
 
 
110
void upsdrv_updateinfo(void)
 
111
{
 
112
  char fstring[512];
 
113
 
 
114
  if (! fc.valid) {
 
115
    fprintf(stderr, 
 
116
            "upsupdate run before ups_ident() read ups config\n");
 
117
    assert(0);
 
118
  }
 
119
 
 
120
  if (execute("f\r", fstring, sizeof(fstring)) > 0) {
 
121
    int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, 
 
122
      alstat=0, vaout=0;
 
123
    double ampsout=0.0, vbatt=0.0, battpercent=0.0, loadpercent=0.0,
 
124
      upstemp=0.0, acfreq=0.0;
 
125
    char tmp[16];
 
126
 
 
127
    /* Inverter status.  0=off 1=on */
 
128
    memcpy(tmp, fstring+16, 2);
 
129
    tmp[2] = '\0';
 
130
    inverter = atoi(tmp);
 
131
 
 
132
    /* Charger status.  0=off 1=on */
 
133
    memcpy(tmp, fstring+18, 2);
 
134
    tmp[2] = '\0';
 
135
    charger = atoi(tmp);
 
136
    
 
137
    /* Input Voltage. integer number */
 
138
    memcpy(tmp, fstring+24, 4);
 
139
    tmp[4] = '\0';
 
140
    vin = atoi(tmp);
 
141
 
 
142
    /* Output Voltage. integer number */
 
143
    memcpy(tmp, fstring+28, 4);
 
144
    tmp[4] = '\0';
 
145
    vout = atoi(tmp);
 
146
 
 
147
    /* Iout.  int times 10 */
 
148
    memcpy(tmp, fstring+36, 4);
 
149
    tmp[4] = '\0';
 
150
    ampsout = ((double)(atoi(tmp)) / 10.0);
 
151
 
 
152
    /* Battery voltage.  int times 10 */
 
153
    memcpy(tmp, fstring+50, 4);
 
154
    tmp[4] = '\0';
 
155
    vbatt = ((double)(atoi(tmp)) / 10.0);
 
156
 
 
157
    /* Volt-amps out.  int  */
 
158
    memcpy(tmp, fstring+40, 6);
 
159
    tmp[6] = '\0';
 
160
    vaout = atoi(tmp);
 
161
 
 
162
    /* Line status.  Bitmask */
 
163
    memcpy(tmp, fstring+72, 2);
 
164
    tmp[2] = '\0';
 
165
    linestat = atoi(tmp);
 
166
 
 
167
    /* Alarm status reg 1.  Bitmask */
 
168
    memcpy(tmp, fstring+20, 2);
 
169
    tmp[2] = '\0';
 
170
    alstat = atoi(tmp);
 
171
 
 
172
    /* Alarm status reg 2.  Bitmask */
 
173
    memcpy(tmp, fstring+22, 2);
 
174
    tmp[2] = '\0';
 
175
    alstat = alstat | (atoi(tmp) << 8);
 
176
 
 
177
    /* AC line frequency */
 
178
    memcpy(tmp, fstring+54, 4);
 
179
    tmp[4]= '\0';
 
180
    acfreq = ((double)(atoi(tmp)) / 100.0);
 
181
 
 
182
    /* Runtime remaining */
 
183
    memcpy(tmp, fstring+58, 4);
 
184
    tmp[4]= '\0';
 
185
    btimeleft = atoi(tmp);
 
186
 
 
187
    /* UPS Temperature */
 
188
    memcpy(tmp, fstring+62, 4);
 
189
    tmp[4]= '\0';
 
190
    upstemp = (double)(atoi(tmp));
 
191
 
 
192
    /* Percent Load */
 
193
    switch(fc.model) {
 
194
      case ME3100:
 
195
        if (execute("d 16\r", fstring, sizeof(fstring)) > 0) {
 
196
          int l;
 
197
          sscanf(fstring, "16 FullLoad%% %d", &l);
 
198
          loadpercent = (double) l;
 
199
        }
 
200
        break;
 
201
      case MD1KVA:
 
202
        if (execute("d 22\r", fstring, sizeof(fstring)) > 0) {
 
203
          int l;
 
204
          sscanf(fstring, "22 FullLoad%% %d", &l);
 
205
          loadpercent = (double) l;
 
206
        }
 
207
        break;
 
208
      default: /* Will never happen, caught in upsdrv_initups() */
 
209
        fprintf(stderr, "Uknown model in upsdrv_updateinfo()\n");
 
210
        exit(1);
 
211
    }
 
212
    /* Compute battery percent left based on battery voltages. */
 
213
    battpercent = ((vbatt - fc.emptyvolts) 
 
214
                   / (fc.fullvolts - fc.emptyvolts) * 100.0);
 
215
    if (battpercent < 0.0) 
 
216
      battpercent = 0.0;
 
217
    else if (battpercent > 100.0)
 
218
      battpercent = 100.0;
 
219
    
 
220
    /* Compute status string */
 
221
    {
 
222
        int lowbatt, overload, replacebatt, boosting, trimming;
 
223
 
 
224
        lowbatt = alstat & (1<<1);
 
225
        overload = alstat & (1<<6);
 
226
        replacebatt = alstat & (1<<10);
 
227
        boosting = inverter && (linestat & (1<<2)) && (vin < 115);
 
228
        trimming = inverter && (linestat & (1<<2)) && (vin > 115);
 
229
 
 
230
        status_init();
 
231
      
 
232
        if (inverter) 
 
233
                status_set("OB");
 
234
        else
 
235
                status_set("OL");
 
236
 
 
237
        if (lowbatt)
 
238
                status_set("LB");
 
239
 
 
240
        if (trimming)
 
241
                status_set("TRIM");
 
242
 
 
243
        if (boosting)
 
244
                status_set("BOOST");
 
245
 
 
246
        if (replacebatt)
 
247
                status_set("RB");
 
248
 
 
249
        if (overload)
 
250
                status_set("OVER");
 
251
 
 
252
        status_commit();
 
253
    }
 
254
 
 
255
    if (debugging) {
 
256
      fprintf(stderr,
 
257
              "Poll: inverter %d charger %d vin %d vout %d vaout %d btimeleft %d\n",
 
258
              inverter, charger, vin, vout, vaout, btimeleft);
 
259
      fprintf(stderr,
 
260
              "      ampsout %5.1f vbatt %5.1f batpcnt %5.1f loadpcnt %5.1f upstemp %5.1f acfreq %5.2f\n",
 
261
              ampsout, vbatt, battpercent, loadpercent, upstemp, acfreq);
 
262
 
 
263
    }
 
264
 
 
265
    /* Stuff information into info structures */
 
266
 
 
267
    dstate_setinfo("input.voltage", "%05.1f", (double)vin);
 
268
    dstate_setinfo("output.voltage", "%05.1f", (double)vout);
 
269
    dstate_setinfo("battery.charge", "%02.1f", battpercent);
 
270
    dstate_setinfo("ups.load", "%02.1f", loadpercent);
 
271
    dstate_setinfo("battery.voltage", "%02.1f", vbatt);
 
272
    dstate_setinfo("input.frequency", "%05.2f", (double)acfreq);
 
273
    dstate_setinfo("ups.temperature", "%05.1f", (double)upstemp);
 
274
    dstate_setinfo("battery.runtime", "%d", btimeleft);
 
275
 
 
276
    dstate_dataok();
 
277
 
 
278
  } else {
 
279
 
 
280
    dstate_datastale();  
 
281
 
 
282
  } /* if (execute("f\r", fstring, sizeof(fstring)) > 0) */
 
283
 
 
284
  dstate_datastale();
 
285
  return;
 
286
}
 
287
 
 
288
 
 
289
void ups_sync(void)
 
290
{
 
291
  char  buf[256];
 
292
 
 
293
  printf ("Syncing: ");
 
294
  fflush (stdout);
 
295
 
 
296
  /* A bit better sanity might be good here.  As is, we expect the
 
297
     human to observe the time being totally not a time. */
 
298
 
 
299
  if (execute("time\r", buf, sizeof(buf)) > 0) {
 
300
    fprintf(stderr, "UPS Time: %s\n", buf);
 
301
  } else {
 
302
    fprintf(stderr, "Error connecting to UPS.\n");
 
303
    exit(1);
 
304
  }
 
305
}
 
306
 
 
307
/* power down the attached load immediately */
 
308
void upsdrv_shutdown(void)
 
309
{
 
310
/* NB: hard-wired password */
 
311
  upssend("pw377\r");
 
312
  upssend("off 1 a\r"); /* power off in 1 second and restart when line power returns */
 
313
}
 
314
 
 
315
/* list flags and values that you want to receive via -x */
 
316
void upsdrv_makevartable(void)
 
317
{
 
318
        /* allow '-x xyzzy' */
 
319
        /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */
 
320
 
 
321
        /* allow '-x foo=<some value>' */
 
322
        /* addvar(VAR_VALUE, "foo", "Override foo setting"); */
 
323
}
 
324
 
 
325
void upsdrv_help(void)
 
326
{
 
327
}
 
328
 
 
329
 
 
330
void upsdrv_banner(void)
 
331
{
 
332
        printf("Network UPS Tools - Best Ferrups Series ME %s (%s)\n\n", 
 
333
                DRV_VERSION, UPS_VERSION);
 
334
}
 
335
 
 
336
static void sync_serial(void) {
 
337
        char buffer[10];
 
338
 
 
339
        upssend("\r");
 
340
        upsrecv(buffer,sizeof(buffer), '\r', "\012");
 
341
        upsrecv(buffer, sizeof(buffer), ENDCHAR, IGNCHARS);
 
342
 
 
343
        while (upsrecv(buffer,sizeof(buffer), '>', "\012") <= 0) {
 
344
                upssend("\r");
 
345
        }
 
346
}
 
347
 
 
348
/* Begin code stolen from bestups.c */
 
349
static void setup_serial(void)
 
350
{  
 
351
        struct   termios  tio;
 
352
                             
 
353
        if (tcgetattr(upsfd, &tio) == -1)
 
354
                fatal("tcgetattr");
 
355
                                     
 
356
        tio.c_iflag = IXON | IXOFF;
 
357
        tio.c_oflag = 0;
 
358
        tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL);
 
359
        tio.c_lflag = 0;
 
360
        tio.c_cc[VMIN] = 1;
 
361
        tio.c_cc[VTIME] = 0;
 
362
 
 
363
#ifdef HAVE_CFSETISPEED
 
364
        cfsetispeed(&tio, B1200); /* baud change here */
 
365
        cfsetospeed(&tio, B1200);
 
366
#else
 
367
#error This system lacks cfsetispeed() and has no other means to set the speed
 
368
#endif
 
369
 
 
370
        if (tcsetattr(upsfd, TCSANOW, &tio) == -1)
 
371
                fatal("tcsetattr");
 
372
/* end code stolen from bestups.c */
 
373
 
 
374
        sync_serial();
 
375
}
 
376
 
 
377
 
 
378
void upsdrv_initups ()
 
379
{
 
380
  char  temp[256], fcstring[512];
 
381
 
 
382
  open_serial(device_path, B1200);
 
383
  setup_serial();
 
384
  ups_sync();
 
385
 
 
386
  fc.model = UNKNOWN;
 
387
  /* Obtain Model */
 
388
  if (execute("id\r", fcstring, sizeof(fcstring)) < 0) {
 
389
    fprintf(stderr, "Failed execute in ups_ident()\n");
 
390
    exit(1);
 
391
  }
 
392
  
 
393
  /* response is a one-line packed string starting with $ */
 
394
  if (memcmp(fcstring, "Unit", 4)) {
 
395
    fprintf(stderr, "Bad response from formatconfig command in ups_ident()\n");
 
396
    fprintf(stderr, "id: %s\n", fcstring);
 
397
    exit(1);
 
398
  }
 
399
 
 
400
  if (debugging)
 
401
    fprintf(stderr, "id: %s\n", fcstring);
 
402
  
 
403
  /* chars 4:2  are a two-digit ascii hex enumerated model code */
 
404
  memcpy(temp, fcstring+9, 2);
 
405
  temp[2] = '\0';
 
406
 
 
407
  if (memcmp(temp, "ME", 2) == 0)  {
 
408
    fc.model = ME3100;
 
409
  }
 
410
  if (memcmp(temp, "C1", 2) == 0)  {
 
411
    /* Better way to identify unit is using "d 15\r", which results in
 
412
       "15 M#    MD1KVA", "id\r" yields "Unit ID "C1K03588"" */
 
413
    fc.model = MD1KVA;
 
414
  }
 
415
 
 
416
  switch(fc.model) {
 
417
    case ME3100:
 
418
      fc.va = 3100;
 
419
      fc.watts = 2200;
 
420
      /* determine shutdown battery voltage */
 
421
      if (execute("d 29\r", fcstring, sizeof(fcstring)) > 0) {
 
422
        sscanf(fcstring, "29 LowBat   %f", &fc.emptyvolts);
 
423
      }
 
424
      /* determine fully charged battery voltage */
 
425
      if (execute("d 31\r", fcstring, sizeof(fcstring)) > 0) {
 
426
        sscanf(fcstring, "31 HiBatt   %f", &fc.fullvolts);
 
427
      }
 
428
      fc.fullvolts = 54.20;
 
429
      /* determine "ideal" voltage by a guess */
 
430
      fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts;
 
431
      break;
 
432
    case MD1KVA:
 
433
      fc.va = 1100;
 
434
      fc.watts = 770; /* Approximate, based on 0.7 power factor */
 
435
      /* determine shutdown battery voltage */
 
436
      if (execute("d 27\r", fcstring, sizeof(fcstring)) > 0) {
 
437
        sscanf(fcstring, "27 LowBatt  %f", &fc.emptyvolts);
 
438
      }
 
439
      /* determine fully charged battery voltage */
 
440
      if (execute("d 28\r", fcstring, sizeof(fcstring)) > 0) {
 
441
        sscanf(fcstring, "28 Hi Batt  %f", &fc.fullvolts);
 
442
      }
 
443
      fc.fullvolts = 13.70;
 
444
      /* determine "ideal" voltage by a guess */
 
445
      fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts;
 
446
      break;
 
447
    default:
 
448
      fprintf(stderr, "Uknown model %s in ups_ident()\n", temp);
 
449
      exit(1);
 
450
      break;
 
451
  }
 
452
 
 
453
  fc.valid = 1;
 
454
  return;
 
455
}
 
456
 
 
457
void upsdrv_cleanup(void)
 
458
{
 
459
}