Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / multimeter.c
1 /**
2  * collectd - src/multimeter.c
3  * Copyright (C) 2005,2006  Peter Holik
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Peter Holik <peter at holik.at>
21  *
22  * Used multimeter: Metex M-4650CR
23  **/
24
25 #include "collectd.h"
26
27 #include "common.h"
28 #include "plugin.h"
29
30 #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
31 #include <sys/ioctl.h>
32 #include <termios.h>
33 #else
34 #error "No applicable input method."
35 #endif
36
37 static int fd = -1;
38
39 #define LINE_LENGTH 14
40 static int multimeter_read_value(double *value) {
41   int retry = 3; /* sometimes we receive garbadge */
42
43   do {
44     struct timeval time_end;
45
46     tcflush(fd, TCIFLUSH);
47
48     if (gettimeofday(&time_end, NULL) < 0) {
49       char errbuf[1024];
50       ERROR("multimeter plugin: gettimeofday failed: %s",
51             sstrerror(errno, errbuf, sizeof(errbuf)));
52       return -1;
53     }
54     time_end.tv_sec++;
55
56     while (1) {
57       char buf[LINE_LENGTH];
58       char *range;
59       int status;
60       fd_set rfds;
61       struct timeval timeout;
62       struct timeval time_now;
63
64       status = swrite(fd, "D", 1);
65       if (status != 0) {
66         ERROR("multimeter plugin: swrite failed.");
67         return -1;
68       }
69
70       FD_ZERO(&rfds);
71       FD_SET(fd, &rfds);
72
73       if (gettimeofday(&time_now, NULL) < 0) {
74         char errbuf[1024];
75         ERROR("multimeter plugin: "
76               "gettimeofday failed: %s",
77               sstrerror(errno, errbuf, sizeof(errbuf)));
78         return -1;
79       }
80       if (timeval_cmp(time_end, time_now, &timeout) < 0)
81         break;
82
83       status = select(fd + 1, &rfds, NULL, NULL, &timeout);
84
85       if (status > 0) /* usually we succeed */
86       {
87         status = read(fd, buf, LINE_LENGTH);
88
89         if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
90           continue;
91
92         /* Format: "DC 00.000mV  \r" */
93         if (status > 0 && status == LINE_LENGTH) {
94           *value = strtod(buf + 2, &range);
95
96           if (range > (buf + 6)) {
97             range = buf + 9;
98
99             switch (*range) {
100             case 'p':
101               *value *= 1.0E-12;
102               break;
103             case 'n':
104               *value *= 1.0E-9;
105               break;
106             case 'u':
107               *value *= 1.0E-6;
108               break;
109             case 'm':
110               *value *= 1.0E-3;
111               break;
112             case 'k':
113               *value *= 1.0E3;
114               break;
115             case 'M':
116               *value *= 1.0E6;
117               break;
118             case 'G':
119               *value *= 1.0E9;
120               break;
121             }
122           } else
123             return -1; /* Overflow */
124
125           return 0; /* value received */
126         } else
127           break;
128       } else if (!status) /* Timeout */
129       {
130         break;
131       } else if ((status == -1) && ((errno == EAGAIN) || (errno == EINTR))) {
132         continue;
133       } else /* status == -1 */
134       {
135         char errbuf[1024];
136         ERROR("multimeter plugin: "
137               "select failed: %s",
138               sstrerror(errno, errbuf, sizeof(errbuf)));
139         break;
140       }
141     }
142   } while (--retry);
143
144   return -2; /* no value received */
145 } /* int multimeter_read_value */
146
147 static int multimeter_init(void) {
148   char device[] = "/dev/ttyS ";
149
150   for (int i = 0; i < 10; i++) {
151     device[strlen(device) - 1] = i + '0';
152
153     if ((fd = open(device, O_RDWR | O_NOCTTY)) != -1) {
154       struct termios tios = {0};
155       int rts = TIOCM_RTS;
156       double value;
157
158       tios.c_cflag = B1200 | CS7 | CSTOPB | CREAD | CLOCAL;
159       tios.c_iflag = IGNBRK | IGNPAR;
160       tios.c_oflag = 0;
161       tios.c_lflag = 0;
162       tios.c_cc[VTIME] = 3;
163       tios.c_cc[VMIN] = LINE_LENGTH;
164
165       tcflush(fd, TCIFLUSH);
166       tcsetattr(fd, TCSANOW, &tios);
167       ioctl(fd, TIOCMBIC, &rts);
168
169       if (multimeter_read_value(&value) < -1) {
170         close(fd);
171         fd = -1;
172       } else {
173         INFO("multimeter plugin: Device "
174              "found at %s",
175              device);
176         return 0;
177       }
178     }
179   }
180
181   ERROR("multimeter plugin: No device found");
182   return -1;
183 }
184 #undef LINE_LENGTH
185
186 static void multimeter_submit(double value) {
187   value_list_t vl = VALUE_LIST_INIT;
188
189   vl.values = &(value_t){.gauge = value};
190   vl.values_len = 1;
191   sstrncpy(vl.plugin, "multimeter", sizeof(vl.plugin));
192   sstrncpy(vl.type, "multimeter", sizeof(vl.type));
193
194   plugin_dispatch_values(&vl);
195 }
196
197 static int multimeter_read(void) {
198   double value;
199
200   if (fd < 0)
201     return -1;
202
203   if (multimeter_read_value(&value) != 0)
204     return -1;
205
206   multimeter_submit(value);
207   return 0;
208 } /* int multimeter_read */
209
210 static int multimeter_shutdown(void) {
211   if (fd >= 0) {
212     close(fd);
213     fd = -1;
214   }
215
216   return 0;
217 }
218
219 void module_register(void) {
220   plugin_register_init("multimeter", multimeter_init);
221   plugin_register_read("multimeter", multimeter_read);
222   plugin_register_shutdown("multimeter", multimeter_shutdown);
223 } /* void module_register */