This patch adds an new plugin called multimeter.
authorPeter Holik <peter@holik.at>
Fri, 4 Aug 2006 18:37:13 +0000 (20:37 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 14 Aug 2006 15:15:38 +0000 (17:15 +0200)
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
collectd.spec
configure.in
contrib/collection.cgi
debian/control
debian/rules
src/Makefile.am
src/collectd.conf.in
src/multimeter.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 7fbc743..9a35d03 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,7 +4,7 @@ This package was written by:
 apcups plugin by:
   Anthony Gialluca <tonyabg at charter.net>
 
-cpufreq module by:
+cpufreq and multimeter module by:
   Peter Holik <peter at holik.at>
 
 hddtemp module by:
index e305021..387763c 100644 (file)
@@ -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*
index 8fccc0d..6834d22 100644 (file)
@@ -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
index 29ca2d0..a0fc4fc 100755 (executable)
@@ -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;
index 3ee102e..9c0e11b 100644 (file)
@@ -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.
index de41de1..1575a82 100755 (executable)
@@ -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
index 46d1a69..5538f64 100644 (file)
@@ -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
index 5e894de..29035ab 100644 (file)
@@ -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 (file)
index 0000000..317745b
--- /dev/null
@@ -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 <peter at holik.at>
+ *
+ * Used multimeter: Metex M-4650CR
+ *
+ **/
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#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