X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fmodbus.c;h=db7d033d3f1c0be05cb0f5d08623a369390f0d8e;hb=d59524524af36bf9e3eb6108d21744076c79701d;hp=8a9fe93fd83eaf3b6a32221ea9c92598c225802a;hpb=5131a49ad0584aa22282aacf72b6e4ca75356bae;p=collectd.git diff --git a/src/modbus.c b/src/modbus.c index 8a9fe93f..db7d033d 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -21,13 +21,14 @@ **/ #include "collectd.h" + #include "common.h" #include "plugin.h" #include "configfile.h" #include -#include +#include #ifndef LIBMODBUS_VERSION_CHECK /* Assume version 2.0.3 */ @@ -47,6 +48,7 @@ /* * * RegisterBase 1234 + * RegisterCmd ReadHolding * RegisterType float * Type gauge * Instance "..." @@ -55,6 +57,10 @@ * * Address "addr" * Port "1234" + * # Or: + * # Device "/dev/ttyUSB0" + * # Baudrate 38400 + * # (Assumes 8N1) * Interval 60 * * @@ -75,7 +81,21 @@ enum mb_register_type_e /* {{{ */ 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; @@ -84,6 +104,7 @@ struct mb_data_s /* {{{ */ 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]; @@ -101,9 +122,11 @@ typedef struct mb_slave_s mb_slave_t; 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; @@ -115,7 +138,6 @@ struct mb_host_s /* {{{ */ modbus_t *connection; #endif _Bool is_connected; - _Bool have_reconnected; }; /* }}} */ typedef struct mb_host_s mb_host_t; @@ -140,12 +162,10 @@ static mb_data_t *data_definitions = NULL; 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); @@ -236,7 +256,7 @@ static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */ 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) @@ -260,6 +280,7 @@ static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */ union { uint8_t b[4]; + uint16_t s[2]; float f; } conv; @@ -288,38 +309,44 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ if (host == NULL) return (EINVAL); - if (host->is_connected) - return (0); - - /* Only reconnect once per interval. */ - if (host->have_reconnected) - return (-1); - +#if COLLECT_DEBUG modbus_set_debug (&host->connection, 1); +#endif /* 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 */ @@ -336,25 +363,37 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ 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); + } } +#if COLLECT_DEBUG modbus_set_debug (host->connection, 1); +#endif /* We'll do the error handling ourselves. */ modbus_set_error_recovery (host->connection, 0); @@ -363,13 +402,12 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ 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 */ @@ -388,11 +426,10 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ 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; - int i; + int status = 0; if ((host == NULL) || (slave == NULL) || (data == NULL)) return (EINVAL); @@ -406,7 +443,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ 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); @@ -421,7 +458,6 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ "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)) @@ -429,6 +465,43 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ 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". */ @@ -444,52 +517,30 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ 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); @@ -569,7 +620,6 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ static int mb_read_slave (mb_host_t *host, mb_slave_t *slave) /* {{{ */ { - mb_data_t *data; int success; int status; @@ -577,7 +627,7 @@ static int mb_read_slave (mb_host_t *host, mb_slave_t *slave) /* {{{ */ 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) @@ -593,7 +643,6 @@ static int mb_read_slave (mb_host_t *host, mb_slave_t *slave) /* {{{ */ static int mb_read (user_data_t *user_data) /* {{{ */ { mb_host_t *host; - size_t i; int success; int status; @@ -602,11 +651,8 @@ static int mb_read (user_data_t *user_data) /* {{{ */ host = user_data->data; - /* Clear the reconnect flag. */ - host->have_reconnected = 0; - 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) @@ -645,12 +691,10 @@ static void data_free_all (mb_data_t *data) /* {{{ */ 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 */ @@ -670,11 +714,9 @@ static void host_free (void *void_host) /* {{{ */ 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; @@ -683,10 +725,9 @@ static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */ 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 = 0; if (strcasecmp ("Type", child->key) == 0) status = cf_util_get_string_buffer (child, @@ -718,6 +759,28 @@ static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */ 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); @@ -748,24 +811,17 @@ static int mb_config_set_host_address (mb_host_t *host, /* {{{ */ 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) @@ -778,7 +834,7 @@ static int mb_config_set_host_address (mb_host_t *host, /* {{{ */ 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), @@ -805,7 +861,6 @@ static int mb_config_add_slave (mb_host_t *host, oconfig_item_t *ci) /* {{{ */ { mb_slave_t *slave; int status; - int i; if ((host == NULL) || (ci == NULL)) return (EINVAL); @@ -822,10 +877,9 @@ static int mb_config_add_slave (mb_host_t *host, oconfig_item_t *ci) /* {{{ */ 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 = 0; if (strcasecmp ("Instance", child->key) == 0) status = cf_util_get_string_buffer (child, @@ -866,21 +920,25 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ { 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)); if (status != 0) + { + sfree (host); return (status); + } if (host->host[0] == 0) + { + sfree (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; @@ -891,6 +949,8 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ 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) { @@ -898,6 +958,14 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ 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) @@ -914,9 +982,22 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ } /* 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; } @@ -925,18 +1006,15 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ { 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 @@ -949,12 +1027,10 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ 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;