Merge pull request #2041 from mfournier/contrib-docker
[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 && HAVE_MATH_H
31 #include <math.h>
32 #include <sys/ioctl.h>
33 #include <termios.h>
34 #else
35 #error "No applicable input method."
36 #endif
37
38 static int fd = -1;
39
40 #define LINE_LENGTH 14
41 static int multimeter_read_value(double *value) {
42   int retry = 3; /* sometimes we receive garbadge */
43
44   do {
45     struct timeval time_end;
46
47     tcflush(fd, TCIFLUSH);
48
49     if (gettimeofday(&time_end, NULL) < 0) {
50       char errbuf[1024];
51       ERROR("multimeter plugin: gettimeofday failed: %s",
52             sstrerror(errno, errbuf, sizeof(errbuf)));
53       return (-1);
54     }
55     time_end.tv_sec++;
56
57     while (1) {
58       char buf[LINE_LENGTH];
59       char *range;
60       int status;
61       fd_set rfds;
62       struct timeval timeout;
63       struct timeval time_now;
64
65       status = swrite(fd, "D", 1);
66       if (status < 0) {
67         ERROR("multimeter plugin: swrite failed.");
68         return (-1);
69       }
70
71       FD_ZERO(&rfds);
72       FD_SET(fd, &rfds);
73
74       if (gettimeofday(&time_now, NULL) < 0) {
75         char errbuf[1024];
76         ERROR("multimeter plugin: "
77               "gettimeofday failed: %s",
78               sstrerror(errno, errbuf, sizeof(errbuf)));
79         return (-1);
80       }
81       if (timeval_cmp(time_end, time_now, &timeout) < 0)
82         break;
83
84       status = select(fd + 1, &rfds, NULL, NULL, &timeout);
85
86       if (status > 0) /* usually we succeed */
87       {
88         status = read(fd, buf, LINE_LENGTH);
89
90         if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
91           continue;
92
93         /* Format: "DC 00.000mV  \r" */
94         if (status > 0 && status == LINE_LENGTH) {
95           *value = strtod(buf + 2, &range);
96
97           if (range > (buf + 6)) {
98             range = buf + 9;
99
100             switch (*range) {
101             case 'p':
102               *value *= 1.0E-12;
103               break;
104             case 'n':
105               *value *= 1.0E-9;
106               break;
107             case 'u':
108               *value *= 1.0E-6;
109               break;
110             case 'm':
111               *value *= 1.0E-3;
112               break;
113             case 'k':
114               *value *= 1.0E3;
115               break;
116             case 'M':
117               *value *= 1.0E6;
118               break;
119             case 'G':
120               *value *= 1.0E9;
121               break;
122             }
123           } else
124             return (-1); /* Overflow */
125
126           return (0); /* value received */
127         } else
128           break;
129       } else if (!status) /* Timeout */
130       {
131         break;
132       } else if ((status == -1) && ((errno == EAGAIN) || (errno == EINTR))) {
133         continue;
134       } else /* status == -1 */
135       {
136         char errbuf[1024];
137         ERROR("multimeter plugin: "
138               "select failed: %s",
139               sstrerror(errno, errbuf, sizeof(errbuf)));
140         break;
141       }
142     }
143   } while (--retry);
144
145   return (-2); /* no value received */
146 } /* int multimeter_read_value */
147
148 static int multimeter_init(void) {
149   char device[] = "/dev/ttyS ";
150
151   for (int i = 0; i < 10; i++) {
152     device[strlen(device) - 1] = i + '0';
153
154     if ((fd = open(device, O_RDWR | O_NOCTTY)) != -1) {
155       struct termios tios = {0};
156       int rts = TIOCM_RTS;
157       double value;
158
159       tios.c_cflag = B1200 | CS7 | CSTOPB | CREAD | CLOCAL;
160       tios.c_iflag = IGNBRK | IGNPAR;
161       tios.c_oflag = 0;
162       tios.c_lflag = 0;
163       tios.c_cc[VTIME] = 3;
164       tios.c_cc[VMIN] = LINE_LENGTH;
165
166       tcflush(fd, TCIFLUSH);
167       tcsetattr(fd, TCSANOW, &tios);
168       ioctl(fd, TIOCMBIC, &rts);
169
170       if (multimeter_read_value(&value) < -1) {
171         close(fd);
172         fd = -1;
173       } else {
174         INFO("multimeter plugin: Device "
175              "found at %s",
176              device);
177         return (0);
178       }
179     }
180   }
181
182   ERROR("multimeter plugin: No device found");
183   return (-1);
184 }
185 #undef LINE_LENGTH
186
187 static void multimeter_submit(double value) {
188   value_list_t vl = VALUE_LIST_INIT;
189
190   vl.values = &(value_t){.gauge = value};
191   vl.values_len = 1;
192   sstrncpy(vl.plugin, "multimeter", sizeof(vl.plugin));
193   sstrncpy(vl.type, "multimeter", sizeof(vl.type));
194
195   plugin_dispatch_values(&vl);
196 }
197
198 static int multimeter_read(void) {
199   double value;
200
201   if (fd < 0)
202     return (-1);
203
204   if (multimeter_read_value(&value) != 0)
205     return (-1);
206
207   multimeter_submit(value);
208   return (0);
209 } /* int multimeter_read */
210
211 static int multimeter_shutdown(void) {
212   if (fd >= 0) {
213     close(fd);
214     fd = -1;
215   }
216
217   return (0);
218 }
219
220 void module_register(void) {
221   plugin_register_init("multimeter", multimeter_init);
222   plugin_register_read("multimeter", multimeter_read);
223   plugin_register_shutdown("multimeter", multimeter_shutdown);
224 } /* void module_register */