**/
#include "collectd.h"
+
#include "common.h"
#include "plugin.h"
#include "configfile.h"
/*
* <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>
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;
static mb_data_t *data_get_by_name (mb_data_t *src, /* {{{ */
const char *name)
{
- mb_data_t *ptr;
-
if (name == NULL)
return (NULL);
- for (ptr = src; ptr != NULL; ptr = ptr->next)
+ for (mb_data_t *ptr = src; ptr != NULL; ptr = ptr->next)
if (strcasecmp (ptr->name, name) == 0)
return (ptr);
if ((host == NULL) || (slave == NULL) || (data == NULL))
return (EINVAL);
- if (host->interval <= 0)
+ if (host->interval == 0)
host->interval = plugin_get_interval ();
if (slave->instance[0] == 0)
/* 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);
}
if (host->connection != NULL)
return (0);
- 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
{
- 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);
+ }
}
#if COLLECT_DEBUG
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);
static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
mb_data_t *data)
{
- uint16_t values[2];
+ uint16_t values[2] = { 0 };
int values_num;
const data_set_t *ds;
int status = 0;
if (ds->ds_num != 1)
{
- ERROR ("Modbus plugin: The type \"%s\" has %i data sources. "
+ ERROR ("Modbus plugin: The type \"%s\" has %zu data sources. "
"I can only handle data sets with only one data source.",
data->type, ds->ds_num);
return (-1);
"is not UINT32.", data->type, DS_TYPE_TO_STRING (ds->ds[0].type));
}
- memset (values, 0, sizeof (values));
if ((data->register_type == REG_TYPE_INT32)
|| (data->register_type == REG_TYPE_UINT32)
|| (data->register_type == REG_TYPE_FLOAT))
{
status = EBADF;
}
- else
+ else if (host->conntype == MBCONN_TCP)
{
struct sockaddr sockaddr;
socklen_t saddrlen = sizeof (sockaddr);
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
-
- status = modbus_read_registers (host->connection,
+ 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 != values_num)
{
- ERROR ("Modbus plugin: modbus_read_registers (%s/%s) failed. status = %i, values_num = %i "
- "Giving up.", host->host, host->node, 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);
#else
static int mb_read_slave (mb_host_t *host, mb_slave_t *slave) /* {{{ */
{
- mb_data_t *data;
int success;
int status;
return (EINVAL);
success = 0;
- for (data = slave->collect; data != NULL; data = data->next)
+ for (mb_data_t *data = slave->collect; data != NULL; data = data->next)
{
status = mb_read_data (host, slave, data);
if (status == 0)
static int mb_read (user_data_t *user_data) /* {{{ */
{
mb_host_t *host;
- size_t i;
int success;
int status;
host = user_data->data;
success = 0;
- for (i = 0; i < host->slaves_num; i++)
+ for (size_t i = 0; i < host->slaves_num; i++)
{
status = mb_read_slave (host, host->slaves + i);
if (status == 0)
static void slaves_free_all (mb_slave_t *slaves, size_t slaves_num) /* {{{ */
{
- size_t i;
-
if (slaves == NULL)
return;
- for (i = 0; i < slaves_num; i++)
+ for (size_t i = 0; i < slaves_num; i++)
data_free_all (slaves[i].collect);
sfree (slaves);
} /* }}} void slaves_free_all */
static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
{
- mb_data_t data;
+ mb_data_t data = { 0 };
int status;
- int i;
- memset (&data, 0, sizeof (data));
data.name = NULL;
data.register_type = REG_TYPE_UINT16;
data.next = NULL;
if (status != 0)
return (status);
- for (i = 0; i < ci->children_num; i++)
+ for (int i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
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);
const char *address)
{
struct addrinfo *ai_list;
- struct addrinfo *ai_ptr;
- struct addrinfo ai_hints;
int status;
if ((host == NULL) || (address == NULL))
return (EINVAL);
- memset (&ai_hints, 0, sizeof (ai_hints));
-#if AI_ADDRCONFIG
- ai_hints.ai_flags |= AI_ADDRCONFIG;
-#endif
- /* XXX: libmodbus can only handle IPv4 addresses. */
- ai_hints.ai_family = AF_INET;
- ai_hints.ai_addr = NULL;
- ai_hints.ai_canonname = NULL;
- ai_hints.ai_next = NULL;
+ struct addrinfo ai_hints = {
+ /* XXX: libmodbus can only handle IPv4 addresses. */
+ .ai_family = AF_INET,
+ .ai_flags = AI_ADDRCONFIG
+ };
- ai_list = NULL;
status = getaddrinfo (address, /* service = */ NULL,
&ai_hints, &ai_list);
if (status != 0)
return (status);
}
- for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
{
status = getnameinfo (ai_ptr->ai_addr, ai_ptr->ai_addrlen,
host->node, sizeof (host->node),
{
mb_slave_t *slave;
int status;
- int i;
if ((host == NULL) || (ci == NULL))
return (EINVAL);
if (status != 0)
return (status);
- for (i = 0; i < ci->children_num; i++)
+ for (int i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
{
mb_host_t *host;
int status;
- int i;
- host = malloc (sizeof (*host));
+ host = calloc (1, 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));
return (EINVAL);
}
- for (i = 0; i < ci->children_num; i++)
+ for (int i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
status = 0;
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;
}
{
user_data_t ud;
char name[1024];
- struct timespec interval = { 0, 0 };
ud.data = host;
ud.free_func = host_free;
ssnprintf (name, sizeof (name), "modbus-%s", host->host);
- CDTIME_T_TO_TIMESPEC (host->interval, &interval);
-
plugin_register_complex_read (/* group = */ NULL, name,
/* callback = */ mb_read,
- /* interval = */ (host->interval > 0) ? &interval : NULL,
+ /* interval = */ host->interval,
&ud);
}
else
static int mb_config (oconfig_item_t *ci) /* {{{ */
{
- int i;
-
if (ci == NULL)
return (EINVAL);
- for (i = 0; i < ci->children_num; i++)
+ for (int i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;