/**
* collectd - src/modbus.c
- * Copyright (C) 2010 noris network AG
+ * Copyright (C) 2010,2011 noris network AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
#include <netdb.h>
-#include <modbus/modbus.h>
+#include <modbus.h>
#ifndef LIBMODBUS_VERSION_CHECK
/* Assume version 2.0.3 */
/*
* <Data "data_name">
* RegisterBase 1234
+ * RegisterCmd ReadHolding
* RegisterType float
* Type gauge
* Instance "..."
* <Host "name">
* Address "addr"
* Port "1234"
+ * # Or:
+ * # Device "/dev/ttyUSB0"
+ * # Baudrate 38400
+ * # (Assumes 8N1)
* Interval 60
*
* <Slave 1>
*/
enum mb_register_type_e /* {{{ */
{
+ REG_TYPE_INT16,
+ REG_TYPE_INT32,
REG_TYPE_UINT16,
REG_TYPE_UINT32,
REG_TYPE_FLOAT
}; /* }}} */
+enum mb_mreg_type_e /* {{{ */
+{
+ MREG_HOLDING,
+ MREG_INPUT
+}; /* }}} */
typedef enum mb_register_type_e mb_register_type_t;
+typedef enum mb_mreg_type_e mb_mreg_type_t;
+
+/* TCP or RTU depending on what is specified in host config block */
+enum mb_conntype_e /* {{{ */
+{
+ MBCONN_TCP,
+ MBCONN_RTU
+}; /* }}} */
+typedef enum mb_conntype_e mb_conntype_t;
struct mb_data_s;
typedef struct mb_data_s mb_data_t;
char *name;
int register_base;
mb_register_type_t register_type;
+ mb_mreg_type_t modbus_register_type;
char type[DATA_MAX_NAME_LEN];
char instance[DATA_MAX_NAME_LEN];
struct mb_host_s /* {{{ */
{
char host[DATA_MAX_NAME_LEN];
- char node[NI_MAXHOST];
+ char node[NI_MAXHOST]; /* TCP hostname or RTU serial device */
/* char service[NI_MAXSERV]; */
- int port;
+ int port; /* for Modbus/TCP */
+ int baudrate; /* for Modbus/RTU */
+ mb_conntype_t conntype;
cdtime_t interval;
mb_slave_t *slaves;
modbus_t *connection;
#endif
_Bool is_connected;
- _Bool have_reconnected;
}; /* }}} */
typedef struct mb_host_s mb_host_t;
return (EINVAL);
if (host->interval <= 0)
- host->interval = interval_g;
+ host->interval = plugin_get_interval ();
if (slave->instance[0] == 0)
ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i",
union
{
uint8_t b[4];
+ uint16_t s[2];
float f;
} conv;
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->port < 1) || (host->port > 65535))
- host->port = MODBUS_TCP_DEFAULT_PORT;
+ if (host->conntype == MBCONN_TCP)
+ {
+ if ((host->port < 1) || (host->port > 65535))
+ host->port = MODBUS_TCP_DEFAULT_PORT;
+
+ DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
+ host->node, host->port);
- DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
- host->node, host->port);
+ modbus_init_tcp (&host->connection,
+ /* host = */ host->node,
+ /* port = */ host->port);
+ }
+ else /* MBCONN_RTU */
+ {
+ DEBUG ("Modbus plugin: Trying to connect to \"%s\".", host->node);
- modbus_init_tcp (&host->connection,
- /* host = */ host->node,
- /* port = */ host->port);
+ modbus_init_rtu (&host->connection,
+ /* device = */ host->node,
+ /* baudrate = */ host->baudrate,
+ 'N', 8, 1, 0);
+ }
status = modbus_connect (&host->connection);
if (status != 0)
{
ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
- host->node, host->port, status);
+ host->node, host->port ? host->port : host->baudrate, status);
return (status);
}
host->is_connected = 1;
- host->have_reconnected = 1;
return (0);
} /* }}} int mb_init_connection */
/* #endif LEGACY_LIBMODBUS */
if (host->connection != NULL)
return (0);
- /* Only reconnect once per interval. */
- if (host->have_reconnected)
- return (-1);
-
- if ((host->port < 1) || (host->port > 65535))
- host->port = MODBUS_TCP_DEFAULT_PORT;
+ if (host->conntype == MBCONN_TCP)
+ {
+ if ((host->port < 1) || (host->port > 65535))
+ host->port = MODBUS_TCP_DEFAULT_PORT;
- DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
- host->node, host->port);
+ DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
+ host->node, host->port);
- host->connection = modbus_new_tcp (host->node, host->port);
- if (host->connection == NULL)
+ host->connection = modbus_new_tcp (host->node, host->port);
+ if (host->connection == NULL)
+ {
+ ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
+ return (-1);
+ }
+ }
+ else
{
- host->have_reconnected = 1;
- ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
- return (-1);
+ DEBUG ("Modbus plugin: Trying to connect to \"%s\", baudrate %i.",
+ host->node, host->baudrate);
+
+ host->connection = modbus_new_rtu (host->node, host->baudrate, 'N', 8, 1);
+ if (host->connection == NULL)
+ {
+ ERROR ("Modbus plugin: Creating new Modbus/RTU object failed.");
+ return (-1);
+ }
}
modbus_set_debug (host->connection, 1);
if (status != 0)
{
ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
- host->node, host->port, status);
+ host->node, host->port ? host->port : host->baudrate, status);
modbus_free (host->connection);
host->connection = NULL;
return (status);
}
- host->have_reconnected = 1;
return (0);
} /* }}} int mb_init_connection */
#endif /* !LEGACY_LIBMODBUS */
uint16_t values[2];
int values_num;
const data_set_t *ds;
- int status;
- int i;
+ int status = 0;
if ((host == NULL) || (slave == NULL) || (data == NULL))
return (EINVAL);
}
if ((ds->ds[0].type != DS_TYPE_GAUGE)
+ && (data->register_type != REG_TYPE_INT32)
&& (data->register_type != REG_TYPE_UINT32))
{
NOTICE ("Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
}
memset (values, 0, sizeof (values));
- if ((data->register_type == REG_TYPE_UINT32)
+ if ((data->register_type == REG_TYPE_INT32)
+ || (data->register_type == REG_TYPE_UINT32)
|| (data->register_type == REG_TYPE_FLOAT))
values_num = 2;
else
values_num = 1;
+ if (host->connection == NULL)
+ {
+ status = EBADF;
+ }
+ else if (host->conntype == MBCONN_TCP)
+ {
+ struct sockaddr sockaddr;
+ socklen_t saddrlen = sizeof (sockaddr);
+
+ status = getpeername (modbus_get_socket (host->connection),
+ &sockaddr, &saddrlen);
+ if (status != 0)
+ status = errno;
+ }
+
+ if ((status == EBADF) || (status == ENOTSOCK) || (status == ENOTCONN))
+ {
+ status = mb_init_connection (host);
+ if (status != 0)
+ {
+ ERROR ("Modbus plugin: mb_init_connection (%s/%s) failed. ",
+ host->host, host->node);
+ host->is_connected = 0;
+ host->connection = NULL;
+ return (-1);
+ }
+ }
+ else if (status != 0)
+ {
+#if LEGACY_LIBMODBUS
+ modbus_close (&host->connection);
+#else
+ modbus_close (host->connection);
+ modbus_free (host->connection);
+#endif
+ }
+
#if LEGACY_LIBMODBUS
/* Version 2.0.3: Pass the connection struct as a pointer and pass the slave
* id to each call of "read_holding_registers". */
return (-1);
}
#endif
-
- for (i = 0; i < 2; i++)
- {
+ if (data->modbus_register_type == MREG_INPUT){
+ status = modbus_read_input_registers (host->connection,
+ /* start_addr = */ data->register_base,
+ /* num_registers = */ values_num, /* buffer = */ values);
+ }
+ else{
status = modbus_read_registers (host->connection,
/* start_addr = */ data->register_base,
/* num_registers = */ values_num, /* buffer = */ values);
- if (status > 0)
- break;
-
- if (host->is_connected)
- {
+ }
+ if (status != values_num)
+ {
+ ERROR ("Modbus plugin: modbus read function (%s/%s) failed. "
+ " status = %i, values_num = %i. Giving up.",
+ host->host, host->node, status, values_num);
#if LEGACY_LIBMODBUS
- modbus_close (&host->connection);
- host->is_connected = 0;
+ modbus_close (&host->connection);
#else
- modbus_close (host->connection);
- modbus_free (host->connection);
- host->connection = NULL;
+ modbus_close (host->connection);
+ modbus_free (host->connection);
#endif
- }
-
- /* If we already tried reconnecting this round, give up. */
- if (host->have_reconnected)
- {
- ERROR ("Modbus plugin: modbus_read_registers (%s) failed. "
- "Reconnecting has already been tried. Giving up.", host->host);
- return (-1);
- }
-
- /* Maybe the device closed the connection during the waiting interval.
- * Try re-establishing the connection. */
- status = mb_init_connection (host);
- if (status != 0)
- {
- ERROR ("Modbus plugin: modbus_read_registers (%s) failed. "
- "While trying to reconnect, connecting to \"%s\" failed. "
- "Giving up.",
- host->host, host->node);
- return (-1);
- }
-
- DEBUG ("Modbus plugin: Re-established connection to %s", host->host);
-
- /* try again */
- continue;
- } /* for (i = 0, 1) */
+ host->connection = NULL;
+ return (-1);
+ }
DEBUG ("Modbus plugin: mb_read_data: Success! "
"modbus_read_registers returned with status %i.", status);
CAST_TO_VALUE_T (ds, vt, float_value);
mb_submit (host, slave, data, vt);
}
+ else if (data->register_type == REG_TYPE_INT32)
+ {
+ union
+ {
+ uint32_t u32;
+ int32_t i32;
+ } v;
+ value_t vt;
+
+ v.u32 = (((uint32_t) values[0]) << 16)
+ | ((uint32_t) values[1]);
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned int32 value is %"PRIi32, v.i32);
+
+ CAST_TO_VALUE_T (ds, vt, v.i32);
+ mb_submit (host, slave, data, vt);
+ }
+ else if (data->register_type == REG_TYPE_INT16)
+ {
+ union
+ {
+ uint16_t u16;
+ int16_t i16;
+ } v;
+ value_t vt;
+
+ v.u16 = values[0];
+
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned int16 value is %"PRIi16, v.i16);
+
+ CAST_TO_VALUE_T (ds, vt, v.i16);
+ mb_submit (host, slave, data, vt);
+ }
else if (data->register_type == REG_TYPE_UINT32)
{
uint32_t v32;
value_t vt;
- v32 = (values[0] << 16) | values[1];
+ v32 = (((uint32_t) values[0]) << 16)
+ | ((uint32_t) values[1]);
DEBUG ("Modbus plugin: mb_read_data: "
"Returned uint32 value is %"PRIu32, v32);
host = user_data->data;
- /* Clear the reconnect flag. */
- host->have_reconnected = 0;
-
success = 0;
for (i = 0; i < host->slaves_num; i++)
{
for (i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
- status = 0;
if (strcasecmp ("Type", child->key) == 0)
status = cf_util_get_string_buffer (child,
status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
if (status != 0)
/* do nothing */;
+ else if (strcasecmp ("Int16", tmp) == 0)
+ data.register_type = REG_TYPE_INT16;
+ else if (strcasecmp ("Int32", tmp) == 0)
+ data.register_type = REG_TYPE_INT32;
else if (strcasecmp ("Uint16", tmp) == 0)
data.register_type = REG_TYPE_UINT16;
else if (strcasecmp ("Uint32", tmp) == 0)
status = -1;
}
}
+ else if (strcasecmp ("RegisterCmd", child->key) == 0)
+ {
+#if LEGACY_LIBMODBUS
+ ERROR("Modbus plugin: RegisterCmd parameter can not be used "
+ "with your libmodbus version");
+#else
+ char tmp[16];
+ status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
+ if (status != 0)
+ /* do nothing */;
+ else if (strcasecmp ("ReadHolding", tmp) == 0)
+ data.modbus_register_type = MREG_HOLDING;
+ else if (strcasecmp ("ReadInput", tmp) == 0)
+ data.modbus_register_type = MREG_INPUT;
+ else
+ {
+ ERROR ("Modbus plugin: The modbus_register_type \"%s\" is unknown.",
+ tmp);
+ status = -1;
+ }
+#endif
+ }
else
{
ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
for (i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
- status = 0;
if (strcasecmp ("Instance", child->key) == 0)
status = cf_util_get_string_buffer (child,
status = cf_util_get_string_buffer (ci, host->host, sizeof (host->host));
if (status != 0)
+ {
+ sfree (host);
return (status);
+ }
if (host->host[0] == 0)
+ {
+ sfree (host);
return (EINVAL);
+ }
for (i = 0; i < ci->children_num; i++)
{
status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
if (status == 0)
status = mb_config_set_host_address (host, buffer);
+ if (status == 0)
+ host->conntype = MBCONN_TCP;
}
else if (strcasecmp ("Port", child->key) == 0)
{
if (host->port <= 0)
status = -1;
}
+ else if (strcasecmp ("Device", child->key) == 0)
+ {
+ status = cf_util_get_string_buffer (child, host->node, sizeof (host->node));
+ if (status == 0)
+ host->conntype = MBCONN_RTU;
+ }
+ else if (strcasecmp ("Baudrate", child->key) == 0)
+ status = cf_util_get_int(child, &host->baudrate);
else if (strcasecmp ("Interval", child->key) == 0)
status = cf_util_get_cdtime (child, &host->interval);
else if (strcasecmp ("Slave", child->key) == 0)
} /* for (i = 0; i < ci->children_num; i++) */
assert (host->host[0] != 0);
- if (host->host[0] == 0)
+ if (host->node[0] == 0)
{
- ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+ ERROR ("Modbus plugin: Data block \"%s\": No address or device has been specified.",
+ host->host);
+ status = -1;
+ }
+ if (host->conntype == MBCONN_RTU && !host->baudrate)
+ {
+ ERROR ("Modbus plugin: Data block \"%s\": No serial baudrate has been specified.",
+ host->host);
+ status = -1;
+ }
+ if ((host->conntype == MBCONN_TCP && host->baudrate) ||
+ (host->conntype == MBCONN_RTU && host->port))
+ {
+ ERROR ("Modbus plugin: Data block \"%s\": You've mixed up RTU and TCP options.",
host->host);
status = -1;
}