From 9a5e152336651ca19c55fef6e6e33db27ccb870e Mon Sep 17 00:00:00 2001 From: Peter Holik Date: Fri, 4 Aug 2006 20:37:13 +0200 Subject: [PATCH] This patch adds an new plugin called multimeter. I have a multimeter which can be connected to the serial port. It is called Metex M4650CR. No configuration is nesessary, this multimeter is autodetected on programm start if connected and switched on. Only your serial port has to be enabled (setserial /dev/ttyS0 autoconfig) --- AUTHORS | 2 +- collectd.spec | 1 + configure.in | 2 + contrib/collection.cgi | 16 +++- debian/control | 13 +++ debian/rules | 2 +- src/Makefile.am | 8 ++ src/collectd.conf.in | 1 + src/multimeter.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 src/multimeter.c diff --git a/AUTHORS b/AUTHORS index 7fbc7430..9a35d036 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,7 +4,7 @@ This package was written by: apcups plugin by: Anthony Gialluca -cpufreq module by: +cpufreq and multimeter module by: Peter Holik hddtemp module by: diff --git a/collectd.spec b/collectd.spec index e3050217..387763c0 100644 --- a/collectd.spec +++ b/collectd.spec @@ -80,6 +80,7 @@ rm -rf $RPM_BUILD_ROOT %attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so* %attr(0444,root,root) %{_libdir}/%{name}/load.so* %attr(0444,root,root) %{_libdir}/%{name}/memory.so* +%attr(0444,root,root) %{_libdir}/%{name}/multimeter.so* %attr(0444,root,root) %{_libdir}/%{name}/nfs.so* %attr(0444,root,root) %{_libdir}/%{name}/ntpd.so* %attr(0444,root,root) %{_libdir}/%{name}/ping.so* diff --git a/configure.in b/configure.in index 8fccc0d2..6834d227 100644 --- a/configure.in +++ b/configure.in @@ -910,6 +910,7 @@ AC_COLLECTD([quota], [enable], [module], [quota statistics (experimental)]) AC_COLLECTD([hddtemp], [disable], [module], [hdd temperature statistics]) AC_COLLECTD([load], [disable], [module], [system load statistics]) AC_COLLECTD([memory], [disable], [module], [memory statistics]) +AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics]) AC_COLLECTD([mysql], [disable], [module], [mysql statistics]) AC_COLLECTD([nfs], [disable], [module], [nfs statistics]) AC_COLLECTD([ntpd], [disable], [module], [nfs statistics]) @@ -957,6 +958,7 @@ Configuration: hddtemp . . . . . . $enable_hddtemp load . . . . . . . $enable_load memory . . . . . . $enable_memory + multimeter . . . . $enable_multimeter mysql . . . . . . . $enable_mysql nfs . . . . . . . . $enable_nfs ntpd . . . . . . . $enable_ntpd diff --git a/contrib/collection.cgi b/contrib/collection.cgi index 29ca2d0a..a0fc4fc9 100755 --- a/contrib/collection.cgi +++ b/contrib/collection.cgi @@ -918,6 +918,18 @@ our $GraphDefs; 'GPRINT:cpufreq_max:MAX:%5.1lf%s Max,', 'GPRINT:cpufreq_avg:LAST:%5.1lf%s Last\l' ], + multimeter => [ + 'DEF:multimeter_avg={file}:value:AVERAGE', + 'DEF:multimeter_min={file}:value:MIN', + 'DEF:multimeter_max={file}:value:MAX', + "AREA:multimeter_max#$HalfBlue", + "AREA:multimeter_min#$Canvas", + "LINE1:multimeter_avg#$FullBlue:Multimeter", + 'GPRINT:multimeter_min:MIN:%4.1lf Min,', + 'GPRINT:multimeter_avg:AVERAGE:%4.1lf Average,', + 'GPRINT:multimeter_max:MAX:%4.1lf Max,', + 'GPRINT:multimeter_avg:LAST:%4.1lf Last\l' + ], users => [ 'DEF:users_avg={file}:users:AVERAGE', 'DEF:users_min={file}:users:MIN', @@ -1075,6 +1087,7 @@ our $GraphArgs = time_dispersion => ['-t', 'NTPd time dispersion ({inst})', '-v', 'Seconds'], traffic => ['-t', '{host} {inst} traffic', '-v', 'Bit/s'], users => ['-t', '{host} users', '-v', 'Users'], + multimeter => ['-t', '{host} multimeter', '-v', 'Value'], voltage => ['-t', '{host} voltage', '-v', 'Volts'], vs_threads => ['-t', '{host} threads', '-v', 'Threads'], vs_memory => ['-t', '{host} memory usage', '-v', 'Bytes'], @@ -1096,7 +1109,8 @@ our $GraphMulti = ping => \&output_graph_ping, sensors => 1, traffic => 1, - users => 1 + users => 1, + multimeter => 1 }; our @Info; diff --git a/debian/control b/debian/control index 3ee102e4..9c0e11b4 100644 --- a/debian/control +++ b/debian/control @@ -35,3 +35,16 @@ Architecture: any Depends: collectd (= ${Source-Version}), libsensors3 Description: collectd module for libsensors. collectd module to collect system temperatures. + +Package: collectd-multimeter +Architecture: any +Depends: collectd (= ${Source-Version}), ${shlibs:Depends} +Description: statistics collection daemon (multimeter plugin) + collectd is a small daemon written in C for performance. It reads various + system statistics and updates RRD files, creating them if necessary. Since + the daemon doesn't need to startup every time it wants to update the files + it's very fast and easy on the system. Also, the statistics are very fine + grained since the files are updated every 10 seconds. + . + This package contains the multimeter plugin which collects values from a + multimeter connected to a serial port. diff --git a/debian/rules b/debian/rules index de41de18..1575a821 100755 --- a/debian/rules +++ b/debian/rules @@ -18,7 +18,7 @@ DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) CFLAGS = -Wall -g -PLUGINS = apache mysql sensors +PLUGINS = apache mysql sensors multimeter ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 diff --git a/src/Makefile.am b/src/Makefile.am index 46d1a69c..5538f641 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -217,6 +217,14 @@ memory_la_LDFLAGS += -lstatgrab endif endif +if BUILD_MODULE_MULTIMETER +pkglib_LTLIBRARIES += multimeter.la +multimeter_la_SOURCES = multimeter.c +multimeter_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" multimeter.la +collectd_DEPENDENCIES += multimeter.la +endif + if BUILD_MODULE_MYSQL pkglib_LTLIBRARIES += mysql.la mysql_la_SOURCES = mysql.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 5e894de5..29035abf 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -31,6 +31,7 @@ @BUILD_MODULE_HDDTEMP_TRUE@LoadPlugin hddtemp @BUILD_MODULE_LOAD_TRUE@LoadPlugin load @BUILD_MODULE_MEMORY_TRUE@LoadPlugin memory +@BUILD_MODULE_MULTIMETER_TRUE@LoadPlugin multimeter @BUILD_MODULE_MYSQL_TRUE@LoadPlugin mysql @BUILD_MODULE_NFS_TRUE@LoadPlugin nfs @BUILD_MODULE_NTPD_TRUE@LoadPlugin ntpd diff --git a/src/multimeter.c b/src/multimeter.c new file mode 100644 index 00000000..317745b2 --- /dev/null +++ b/src/multimeter.c @@ -0,0 +1,234 @@ +/** + * collectd - src/multimeter.c + * Copyright (C) 2005,2006 Peter Holik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Peter Holik + * + * Used multimeter: Metex M-4650CR + * + **/ + +#include +#include +#include +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#define MODULE_NAME "multimeter" + +static char *multimeter_file = "multimeter.rrd"; + +static char *ds_def[] = +{ + "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U", + NULL +}; +static int ds_num = 1; + +static int fd = -1; + +static int multimeter_timeval_sub (struct timeval *tv1, struct timeval *tv2, + struct timeval *res) +{ + if ((tv1->tv_sec < tv2->tv_sec) || + ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec < tv2->tv_usec))) + return (-1); + + res->tv_sec = tv1->tv_sec - tv2->tv_sec; + res->tv_usec = tv1->tv_usec - tv2->tv_usec; + + assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec > 0))); + + while (res->tv_usec < 0) + { + res->tv_usec += 1000000; + res->tv_sec--; + } + return (0); +} +#define LINE_LENGTH 14 +static int multimeter_read_value(double *value) +{ + int retry = 3; /* sometimes we receive garbadge */ + + do + { + struct timeval time_end; + + tcflush(fd, TCIFLUSH); + + if (gettimeofday (&time_end, NULL) < 0) + { + syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s", + strerror (errno)); + return (-1); + } + time_end.tv_sec++; + + while (1) + { + char buf[LINE_LENGTH]; + char *range; + int status; + fd_set rfds; + struct timeval timeout; + struct timeval time_now; + + write(fd, "D", 1); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + if (gettimeofday (&time_now, NULL) < 0) + { + syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s", + strerror (errno)); + return (-1); + } + if (multimeter_timeval_sub (&time_end, &time_now, &timeout) == -1) + break; + + status = select(fd+1, &rfds, NULL, NULL, &timeout); + + if (status > 0) /* usually we succeed */ + { + status = read(fd, buf, LINE_LENGTH); + + if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) + continue; + + /* Format: "DC 00.000mV \r" */ + if (status > 0 && status == LINE_LENGTH) + { + *value = strtod(buf + 2, &range); + + if ( range > (buf + 6) ) + { + range = buf + 9; + + switch ( *range ) + { + case 'p': *value *= 1.0E-12; break; + case 'n': *value *= 1.0E-9; break; + case 'u': *value *= 1.0E-6; break; + case 'm': *value *= 1.0E-3; break; + case 'k': *value *= 1.0E3; break; + case 'M': *value *= 1.0E6; break; + case 'G': *value *= 1.0E9; break; + } + } + else + return (-1); /* Overflow */ + + return (0); /* value received */ + } + else break; + } + else if (!status) /* Timeout */ + { + break; + } + else if ((status == -1) && ((errno == EAGAIN) || (errno == EINTR))) + { + continue; + } + else /* status == -1 */ + { + syslog (LOG_ERR, MODULE_NAME": select failed: %s", + strerror (errno)); + break; + } + } + } while (--retry); + + return (-2); /* no value received */ +} + +static void multimeter_init (void) +{ + int i; + char device[] = "/dev/ttyS "; + + for (i = 0; i < 10; i++) + { + device[strlen(device)-1] = i + '0'; + + if ((fd = open(device, O_RDWR | O_NOCTTY)) > 0) + { + struct termios tios; + int rts = TIOCM_RTS; + double value; + + tios.c_cflag = B1200 | CS7 | CSTOPB | CREAD | CLOCAL; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + tios.c_cc[VTIME] = 3; + tios.c_cc[VMIN] = LINE_LENGTH; + + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &tios); + ioctl(fd, TIOCMBIC, &rts); + + if (multimeter_read_value(&value) < -1) + { + close(fd); + fd = -1; + } + else + { + syslog (LOG_INFO, MODULE_NAME" found (%s)", device); + return; + } + } + } + syslog (LOG_ERR, MODULE_NAME" not found"); +} +#undef LINE_LENGTH + +static void multimeter_write (char *host, char *inst, char *val) +{ + rrd_update_file (host, multimeter_file, val, ds_def, ds_num); +} +#define BUFSIZE 128 +static void multimeter_submit (double *value) +{ + char buf[BUFSIZE]; + + if (snprintf (buf, BUFSIZE, "%u:%f", (unsigned int) curtime, *value) >= BUFSIZE) + return; + + plugin_submit (MODULE_NAME, NULL, buf); +} +#undef BUFSIZE + +static void multimeter_read (void) +{ + double value; + + if (fd > -1 && !(multimeter_read_value(&value))) + multimeter_submit (&value); +} + +void module_register (void) +{ + plugin_register (MODULE_NAME, multimeter_init, multimeter_read, multimeter_write); +} + +#undef MODULE_NAME -- 2.11.0