UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / examples / smartsim.c
blobce965458d377b35e12d62a189e78e7784bb92cc6
1 /*
2 * smartsim.c
4 * A SmartUPS serial protocol simulator.
6 * This is a basic SmartUPS protocol simulator. It answers
7 * queries from the host (i.e., apcuspd) and allows you to toggle
8 * flags such as onbattery and adjust readouts such as timeleft.
9 * This can be very useful for exercising apcupsd with a series
10 * of events that would otherwise require draining your UPS battery
11 * repeatedly.
13 * Point smartsim at a serial port (/dev/ttyS0 is the default) and
14 * connect that port via a null modem cable to another serial port
15 * on which you are running apcupsd configured for the apcsmart
16 * driver.
18 * smartsim responds to the Smart protocol commands listed in the
19 * upscmds[] table. More protocol commands can easily be added.
21 * Keyboard commands consisting of a single keypress cause smartsim
22 * to toggle flags and readouts. These commands can be found in the
23 * keycmds[] table. Supported keypress commands:
25 * ? Help
26 * b Onbattery toggle
27 * o Overload toggle
28 * t Trim toggle
29 * s Boost toggle
30 * l BattLow toggle
31 * x Selftest toggle
32 * r ReplaceBatt toggle
33 * d BattDetach toggle
34 * c CommFail toggle
35 * 7/4 BattPct (inc/dec) (use numeric keypad)
36 * 8/5 LoadPct (inc/dec) (use numeric keypad)
37 * 9/6 TimeLeft (inc/dec) (use numeric keypad)
39 * Some keypress commands produce procotol events. For example,
40 * toggling on/off battery changes the status variable and also
41 * issues '!' and '$' "interrupt" events.
45 * Copyright (C) 2005 Adam Kropelin
47 * This program is free software; you can redistribute it and/or
48 * modify it under the terms of version 2 of the GNU General
49 * Public License as published by the Free Software Foundation.
51 * This program is distributed in the hope that it will be useful,
52 * but WITHOUT ANY WARRANTY; without even the implied warranty of
53 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
54 * General Public License for more details.
56 * You should have received a copy of the GNU General Public
57 * License along with this program; if not, write to the Free
58 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
59 * MA 02111-1307, USA.
61 #include <stdio.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <fcntl.h>
65 #include <errno.h>
66 #include <string.h>
67 #include <unistd.h>
68 #include <termios.h>
70 #define DEFAULT_LINEF 60.0
71 #define DEFAULT_LOADPCT 25.0
72 #define DEFAULT_BATTPCT 100.0
73 #define DEfAULT_TIMELEFT 20.0
74 #define DEFAULT_BATTV 24.0
75 #define DEFAULT_LINEV 120.0
76 #define DEFAULT_OUTV 120.0
77 #define DEFAULT_REG2 0x00
79 int debug = 1;
80 int ups = -1;
82 /* UPS variables */
83 float linefreq = DEFAULT_LINEF;
84 float loadpct = DEFAULT_LOADPCT;
85 float battpct = DEFAULT_BATTPCT;
86 float timeleft = DEfAULT_TIMELEFT;
87 float battv = DEFAULT_BATTV;
88 float linev = DEFAULT_LINEV;
89 float outv = DEFAULT_OUTV;
90 unsigned char reg2 = DEFAULT_REG2;
91 char xfercause[] = "O";
92 char selftest[] = "OK";
93 int onbatt = 0;
94 int online = 1;
95 int overload = 0;
96 int battlow = 0;
97 int rebatt = 0;
98 int trim = 0;
99 int boost = 0;
100 int commfail = 0;
102 #define dbg(str, args...) \
103 do \
105 if (debug) \
107 printf(str, ## args); \
108 fflush(stdout); \
111 while(0)
113 /* Response callbacks */
114 static void rsp_string(const void* arg);
115 static void rsp_float(const void* arg);
116 static void rsp_cmds(const void* arg);
117 static void rsp_status(const void* arg);
118 static void rsp_hex(const void *arg);
120 /* Mapping of UPS commands to response callbacks */
121 struct upscmd
123 char cmd;
124 void (*func)(const void*);
125 const void* arg;
126 } upscmds[] =
128 { 'Y', rsp_string, "SM" },
129 { '\x01', rsp_string, "SMART-UPS Simulator" },
130 { 'c', rsp_string, "SMARTSIM" },
131 { 'F', rsp_float, &linefreq },
132 { 'Q', rsp_status, NULL },
133 { 'P', rsp_float, &loadpct },
134 { 'f', rsp_float, &battpct },
135 { 'a', rsp_cmds, NULL },
136 { 'j', rsp_float, &timeleft },
137 { 'G', rsp_string, xfercause },
138 { 'X', rsp_string, selftest },
139 { 'B', rsp_float, &battv },
140 { 'L', rsp_float, &linev },
141 { 'O', rsp_float, &outv },
142 { '\'', rsp_hex, &reg2 },
143 { 'V', rsp_string, "Lzy" },
144 { '\0', NULL, NULL }
147 /* The alert characters we support */
148 const char alerts[] = "!$%+#";
150 static void key_toggle(const void* arg);
151 static void key_onbatt(const void* arg);
152 static void key_battlow(const void* arg);
153 static void key_inc(const void* arg);
154 static void key_dec(const void* arg);
155 static void key_selftest(const void* arg);
156 static void key_rebatt(const void* arg);
157 static void key_batdet(const void *arg);
158 static void key_commfail(const void *arg);
159 static void key_help(const void *arg);
161 /* Mapping of keyboard commands to callbacks */
162 struct keycmd
164 char key;
165 void (*func)(const void*);
166 void* arg;
167 } keycmds[] =
169 { 'b', key_onbatt, NULL },
170 { 'o', key_toggle, &overload },
171 { 't', key_toggle, &trim },
172 { 's', key_toggle, &boost },
173 { 'l', key_battlow, NULL },
174 { '7', key_inc, &battpct },
175 { '4', key_dec, &battpct },
176 { '8', key_inc, &loadpct },
177 { '5', key_dec, &loadpct },
178 { '9', key_inc, &timeleft },
179 { '6', key_dec, &timeleft },
180 { 'x', key_selftest, NULL },
181 { 'r', key_rebatt, NULL },
182 { 'd', key_batdet, NULL },
183 { 'c', key_commfail, NULL },
184 { '?', key_help, NULL },
185 { '\0', NULL, NULL }
188 /* Defaults */
189 #define DEFAULT_DEVICE "/dev/ttyS0";
191 void wups(const char* str, int len)
193 if (debug)
194 write(fileno(stdout), str, len);
196 write(ups, str, len);
199 void rsp_string(const void* arg)
201 wups((char*)arg, strlen((char*)arg));
202 wups("\r\n", 2);
205 void rsp_float(const void* arg)
207 char buf[20];
209 sprintf(buf, "%03.3f\r\n", *(float*)arg);
210 wups(buf, strlen(buf));
213 void rsp_hex(const void* arg)
215 char buf[20];
217 sprintf(buf, "%02x\r\n", *(unsigned char*)arg);
218 wups(buf, strlen(buf));
221 void rsp_cmds(const void* arg)
223 int x;
225 /* Protocol version */
226 wups("3.", 2);
228 /* Supported alert characters */
229 wups(alerts, sizeof(alerts)-1);
230 wups(".", 1);
232 /* Supported commands */
233 for (x=0; upscmds[x].func; x++)
234 wups(&upscmds[x].cmd, 1);
236 wups("\r\n", 2);
239 void rsp_status(const void* arg)
241 char buf[20];
243 sprintf(buf, "%02x\r\n",
244 (trim << 1) |
245 (boost << 2) |
246 (online << 3) |
247 (onbatt << 4) |
248 (overload << 5) |
249 (battlow << 6) |
250 (rebatt << 7));
252 wups(buf, strlen(buf));
255 static void key_toggle(const void* arg)
257 *(int *)arg = !*(int *)arg;
260 static void key_onbatt(const void* arg)
262 if (!onbatt)
264 online = 0;
265 onbatt = 1;
266 xfercause[0] = 'L';
267 linev = 0;
268 dbg("ALERT: ");
269 wups("!", 1);
270 dbg("\n");
272 else
274 online = 1;
275 onbatt = 0;
276 linev = DEFAULT_LINEV;
277 dbg("ALERT: ");
278 wups("$", 1);
279 dbg("\n");
283 static void key_battlow(const void* arg)
285 if (!battlow)
287 battlow = 1;
288 dbg("ALERT: ");
289 wups("%", 1);
290 dbg("\n");
292 else
294 battlow = 0;
295 dbg("ALERT: ");
296 wups("+", 1);
297 dbg("\n");
301 static void key_inc(const void* arg)
303 *(float*)arg += 1;
304 dbg("%3.3f\n", *(float*)arg);
307 static void key_dec(const void* arg)
309 *(float*)arg -= 1;
310 dbg("%3.3f\n", *(float*)arg);
313 static void key_selftest(const void* arg)
315 key_onbatt(NULL);
317 if (onbatt)
318 xfercause[0] = 'S';
321 static void key_rebatt(const void* arg)
323 if (!rebatt)
325 rebatt = 1;
326 dbg("ALERT: ");
327 wups("#", 1);
328 dbg("\n");
330 else
332 rebatt = 0;
336 static void key_batdet(const void *arg)
338 if (reg2 & 0x20)
339 reg2 &= ~0x20;
340 else
341 reg2 |= 0x20;
344 static void key_commfail(const void *arg)
346 if (commfail)
348 commfail = 0;
349 dbg("COMMFAIL disabled\n");
351 else
353 commfail = 1;
354 dbg("COMMFAIL enabled\n");
359 static void key_help(const void *arg)
361 dbg("Commands:\n");
362 dbg("? Help\n");
363 dbg("b Onbattery toggle\n");
364 dbg("o Overload toggle\n");
365 dbg("t Trim toggle\n");
366 dbg("s Boost toggle\n");
367 dbg("l BattLow toggle\n");
368 dbg("x Selftest toggle\n");
369 dbg("r ReplaceBatt toggle\n");
370 dbg("d BattDetach toggle\n");
371 dbg("c CommFail toggle\n");
372 dbg("7/4 BattPct (inc/dec) (use numeric keypad)\n");
373 dbg("8/5 LoadPct (inc/dec) (use numeric keypad)\n");
374 dbg("9/6 TimeLeft (inc/dec) (use numeric keypad)\n");
377 void handle_ups_cmd(char cmd)
379 int x;
381 if (commfail)
383 dbg("<COMMFAIL>\n");
384 return;
387 for (x=0; upscmds[x].func; x++)
389 if (upscmds[x].cmd == cmd)
391 upscmds[x].func(upscmds[x].arg);
392 break;
396 if (!upscmds[x].func)
397 rsp_string("NA");
400 void handle_key_cmd(char cmd)
402 int x;
404 for (x=0; keycmds[x].func; x++)
406 if (keycmds[x].key == cmd)
408 keycmds[x].func(keycmds[x].arg);
409 break;
414 #define max(a,b) \
415 ( ((a) > (b)) ? (a) : (b) )
417 int main(int argc, char* argv[])
419 fd_set fds;
420 int rc;
421 char cmd;
422 struct termios tio;
423 const char* dev;
424 int con;
426 /* Allow serial port device to be supplied on command line */
427 if (argc == 2)
428 dev = argv[1];
429 else
430 dev = DEFAULT_DEVICE;
432 /* Open serial port device */
433 ups = open(dev, O_RDWR | O_NOCTTY);
434 if (ups < 0)
436 perror("open");
437 return 1;
440 /* Set serial port for 2400 baud, N81 */
441 tio.c_cflag = B2400 | CS8 | CLOCAL | CREAD;
442 tio.c_iflag = IGNPAR; /* Ignore errors, raw input */
443 tio.c_oflag = 0; /* Raw output */
444 tio.c_lflag = 0; /* No local echo */
445 cfsetospeed(&tio, B2400);
446 cfsetispeed(&tio, B2400);
447 tcflush(ups, TCIFLUSH);
448 tcsetattr(ups, TCSANOW, &tio);
449 tcflush(ups, TCIFLUSH);
451 /* Disable echo and line buffering on stdin */
452 con = fileno(stdin);
453 tcgetattr(con, &tio);
454 tio.c_lflag &= ~(ECHO|ICANON);
455 tcsetattr(con, TCSANOW, &tio);
457 while (1)
459 FD_ZERO(&fds);
460 FD_SET(ups, &fds);
461 FD_SET(con, &fds);
465 rc = select(max(con,ups)+1, &fds, NULL, NULL, NULL);
467 while (rc == -1 && (errno == EAGAIN || errno == EINTR));
469 if (rc == -1)
471 perror("select");
472 return 1;
475 if (FD_ISSET(ups, &fds))
479 rc = read(ups, &cmd, 1);
481 while (rc != 1 && (errno == EAGAIN || errno == EINTR));
483 if (rc != 1)
485 perror("read");
486 return 1;
489 dbg("CMD: ");
490 if (cmd == 0)
491 dbg("<NUL>");
492 else if (cmd >= 1 && cmd <= 26)
493 dbg("^%c", cmd+64);
494 else
495 dbg("%c", cmd);
496 dbg("\nRSP: ");
498 handle_ups_cmd(cmd);
501 if (FD_ISSET(con, &fds))
505 rc = read(con, &cmd, 1);
507 while (rc != 1 && (errno == EAGAIN || errno == EINTR));
509 if (rc != 1)
511 perror("read");
512 return 1;
515 handle_key_cmd(cmd);
519 return 0;