[collectd] [PATCH] support for Modbus/RTU
Daniel Golle
daniel.golle at gmail.com
Mon Mar 28 20:20:59 CEST 2011
This patch adds Modbus/RTU support to the modbus plugin of collectd 4.10.3.
It's a quick hack and didn't receive any testing yet, but please please review and let me know what you think and help me.
Signed-off-by: Daniel Golle <daniel.golle at gmail.com>
--- a/src/modbus.c 2011-03-28 20:01:58.000000000 +0200
+++ b/src/modbus.c 2011-03-28 20:03:45.000000000 +0200
@@ -91,6 +91,16 @@
struct mb_host_s /* {{{ */
{
char host[DATA_MAX_NAME_LEN];
+ type_com_t type_com;
+#ifdef __APPLE_CC__
+ char device[64];
+#else
+ char device[16];
+#endif
+ int baud;
+ int data_bits;
+ int stop_bits;
+ char parity[5];
char node[NI_MAXHOST];
/* char service[NI_MAXSERV]; */
int port;
@@ -265,7 +275,7 @@
return (conv.f);
} /* }}} float mb_register_to_float */
-static int mb_init_connection (mb_host_t *host) /* {{{ */
+static inline int mb_init_connection_tcp (mb_host_t *host) /* {{{ */
{
int status;
@@ -305,6 +315,69 @@
host->is_connected = 1;
host->have_reconnected = 1;
return (0);
+} /* }}} int mb_init_connection_tcp */
+
+static inline int mb_init_connection_rtu (mb_host_t *host) /* {{{ */
+{
+ int status;
+
+ if (host == NULL)
+ return (EINVAL);
+
+ if (host->is_connected)
+ return (0);
+
+ /* Only reconnect once per interval. */
+ if (host->have_reconnected)
+ return (-1);
+
+ modbus_set_debug (&host->connection, 1);
+
+ /* We'll do the error handling ourselves. */
+ modbus_set_error_handling (&host->connection, NOP_ON_ERROR);
+
+ if (host->baud < 300)
+ host->baud = 9600;
+
+ if ((host->data_bits < 7) || (host->data_bits > 16))
+ host->data_bits = 8;
+
+ if ((host->stop_bits < 1) || (host->stop_bits > 16))
+ host->stop_bits = 1;
+
+ if ( ! (strcmp(host->parity, "even") || strcmp(host->parity, "odd") || strcmp(host->parity, "none")))
+ memcpy(host->parity, "none", sizeof("none"));
+
+ DEBUG ("Modbus plugin: Trying to open \"%s\".",
+ host->device);
+
+ modbus_init_rtu (&host->connection,
+ /* device = */ host->device,
+ /* baud = */ host->baud,
+ /* parity = */ host->parity,
+ /* data_bit = */ (uint8_t)host->data_bits,
+ /* stop_bit = */ (uint8_t)host->stop_bits);
+
+ status = modbus_connect (&host->connection);
+ if (status != 0)
+ {
+ ERROR ("Modbus plugin: modbus_connect (%s, %i baud, %i %s %i) failed with status %i.",
+ host->device, host->baud, host->data_bits, host->parity, host->stop_bits, status);
+ return (status);
+ }
+
+ host->is_connected = 1;
+ host->have_reconnected = 1;
+ return (0);
+} /* }}} int mb_init_connection_rtu */
+
+static int mb_init_connection (mb_host_t *host) /* {{{ */
+{
+ if ( host->type_com == TCP )
+ return mb_init_connection_tcp(host);
+ else if ( host->type_com == RTU )
+ return mb_init_connection_rtu(host);
+ else return -1;
} /* }}} int mb_init_connection */
#define CAST_TO_VALUE_T(ds,vt,raw) do { \
@@ -748,6 +821,7 @@
if (host->host[0] == 0)
return (EINVAL);
+ host->type_com = TCP;
for (i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
@@ -817,6 +891,114 @@
return (status);
} /* }}} int mb_config_add_host */
+static int mb_config_add_device (oconfig_item_t *ci) /* {{{ */
+{
+ mb_host_t *host;
+ int status;
+ int i;
+
+ host = malloc (sizeof (*host));
+ if (host == NULL)
+ return (ENOMEM);
+ memset (host, 0, sizeof (*host));
+ host->slaves = NULL;
+
+ status = cf_util_get_string_buffer (ci, host->host, sizeof (host->host));
+ if (status != 0)
+ return (status);
+ if (host->host[0] == 0)
+ return (EINVAL);
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ status = 0;
+
+ if (strcasecmp ("Device", child->key) == 0)
+ {
+ status = cf_util_get_string_buffer (child, host->device, sizeof (host->device));
+ if (status != 0)
+ return (status);
+ if (host->device[0] == 0)
+ return (EINVAL);
+ }
+ else if (strcasecmp ("Baud", child->key) == 0)
+ {
+ status = cf_util_get_int (child, &host->baud);
+ if (host->baud <= 0)
+ status = -1;
+ }
+ else if (strcasecmp ("DataBits", child->key) == 0)
+ {
+ status = cf_util_get_int (child, &host->data_bits);
+ if (host->data_bits < 7)
+ status = -1;
+ }
+ else if (strcasecmp ("StopBits", child->key) == 0)
+ {
+ status = cf_util_get_int (child, &host->stop_bits);
+ if (host->stop_bits <= 0)
+ status = -1;
+ }
+ else if (strcasecmp ("Parity", child->key) == 0)
+ {
+ status = cf_util_get_string_buffer (child, host->parity, sizeof (host->parity));
+ if (status != 0)
+ return (status);
+ if (host->parity[0] == 0)
+ return (EINVAL);
+
+ }
+ else if (strcasecmp ("Interval", child->key) == 0)
+ status = cf_util_get_int (child, &host->interval);
+ else if (strcasecmp ("Slave", child->key) == 0)
+ /* Don't set status: Gracefully continue if a slave fails. */
+ mb_config_add_slave (host, child);
+ else
+ {
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ assert (host->host[0] != 0);
+ if (host->host[0] == 0)
+ {
+ ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+ host->host);
+ status = -1;
+ }
+
+ if (status == 0)
+ {
+ user_data_t ud;
+ char name[1024];
+ struct timespec interval;
+
+ ud.data = host;
+ ud.free_func = host_free;
+
+ ssnprintf (name, sizeof (name), "modbus-%s", host->host);
+
+ interval.tv_nsec = 0;
+ if (host->interval > 0)
+ interval.tv_sec = host->interval;
+ else
+ interval.tv_sec = 0;
+
+ plugin_register_complex_read (/* group = */ NULL, name,
+ mb_read, (interval.tv_sec > 0) ? &interval : NULL, &ud);
+ }
+ else
+ {
+ host_free (host);
+ }
+
+ return (status);
+} /* }}} int mb_config_add_device */
+
static int mb_config (oconfig_item_t *ci) /* {{{ */
{
int i;
@@ -832,6 +1014,8 @@
mb_config_add_data (child);
else if (strcasecmp ("Host", child->key) == 0)
mb_config_add_host (child);
+ else if (strcasecmp ("Device", child->key) == 0)
+ mb_config_add_device (child);
else
ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
}
--- a/src/collectd.conf.in 2011-03-28 20:05:14.000000000 +0200
+++ b/src/collectd.conf.in 2011-03-28 20:12:56.000000000 +0200
@@ -445,6 +445,19 @@
# Collect "data_name"
# </Slave>
# </Host>
+# <Device "name">
+# Device "/dev/serial"
+# Baud 9600
+# DataBits 8
+# StopBits 1
+# Parity "none"
+# Interval 60
+#
+# <Slave 1>
+# Instance "foobar" # optional
+# Collect "data_name"
+# </Slave>
+# </Host>
#</Plugin>
#<Plugin mysql>
--- a/src/collectd.conf.pod 2011-03-26 18:08:53.000000000 +0200
+++ b/src/collectd.conf.pod 2011-03-28 20:10:59.000000000 +0200
@@ -1731,10 +1731,11 @@
=head2 Plugin C<modbus>
-The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
-register values. It supports reading single registers (unsigned 16E<nbsp>bit
-values), large integer values (unsigned 32E<nbsp>bit values) and floating point
-values (two registers interpreted as IEEE floats in big endian notation).
+The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP or Modbus/RTU
+and reads register values.
+It supports reading single registers (unsigned 16E<nbsp>bit values),
+large integer values (unsigned 32E<nbsp>bit values) and floating point values
+(two registers interpreted as IEEE floats in big endian notation).
Synopsis:
@@ -1758,6 +1759,21 @@
Interval 60
<Slave 1>
+ Instance "power-supply"
+ Collect "voltage-input-1"
+ Collect "voltage-input-2"
+ </Slave>
+ </Host>
+
+ <Device "SerialDevice1">
+ Device "/dev/ttyS1"
+ Baud 9600
+ DataBits 8
+ StopBits 1
+ Parity "none"
+ Interval 60
+
+ <Slave 1>
Instance "power-supply"
Collect "voltage-input-1"
Collect "voltage-input-2"
More information about the collectd
mailing list