X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fmodbus.c;h=2276572c5b9875ac74324bbab13ee0ecaac9bb3a;hb=fadd1df67243af6d0d4f58b10b21755ee1f433d6;hp=d8e270e87828efcf0d2a01935802c56f9a342bbd;hpb=8f5a6a64dda60f8397f3c45fc6b3d0606e123d3b;p=collectd.git diff --git a/src/modbus.c b/src/modbus.c index d8e270e8..2276572c 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1,22 +1,25 @@ /** * collectd - src/modbus.c * Copyright (C) 2010 noris network AG + * Copyright (C) 2011 Universiteit Gent * * 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; only version 2 of the License is applicable. + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; only version 2.1 of the License is + * applicable. * * 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. + * Lesser 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 + * You should have received a copy of the GNU Lesser 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: * Florian Forster + * Ivo De Decker **/ #include "collectd.h" @@ -24,8 +27,25 @@ #include "plugin.h" #include "configfile.h" +#include + #include +#ifndef LIBMODBUS_VERSION_CHECK +/* Assume version 2.0.3 */ +# define LEGACY_LIBMODBUS 1 +#else +/* Assume version 2.9.2 */ +#endif + +#ifndef MODBUS_TCP_DEFAULT_PORT +# ifdef MODBUS_TCP_PORT +# define MODBUS_TCP_DEFAULT_PORT MODBUS_TCP_PORT +# else +# define MODBUS_TCP_DEFAULT_PORT 502 +# endif +#endif + /* * * RegisterBase 1234 @@ -49,17 +69,17 @@ /* * Data structures */ -enum mb_register_type_e +enum mb_register_type_e /* {{{ */ { REG_TYPE_UINT16, REG_TYPE_UINT32, REG_TYPE_FLOAT -}; +}; /* }}} */ typedef enum mb_register_type_e mb_register_type_t; struct mb_data_s; typedef struct mb_data_s mb_data_t; -struct mb_data_s +struct mb_data_s /* {{{ */ { char *name; int register_base; @@ -68,47 +88,62 @@ struct mb_data_s char instance[DATA_MAX_NAME_LEN]; mb_data_t *next; -}; +}; /* }}} */ + +struct mb_datagroup_s; +typedef struct mb_datagroup_s mb_datagroup_t; +struct mb_datagroup_s /* {{{ */ +{ + char *name; + mb_data_t *collect; + + mb_datagroup_t *next; +}; /* }}} */ -struct mb_slave_s +struct mb_slave_s /* {{{ */ { int id; char instance[DATA_MAX_NAME_LEN]; mb_data_t *collect; -}; +}; /* }}} */ typedef struct mb_slave_s mb_slave_t; -struct mb_host_s +struct mb_host_s /* {{{ */ { char host[DATA_MAX_NAME_LEN]; char node[NI_MAXHOST]; /* char service[NI_MAXSERV]; */ int port; - int interval; + cdtime_t interval; mb_slave_t *slaves; size_t slaves_num; +#if LEGACY_LIBMODBUS modbus_param_t connection; +#else + modbus_t *connection; +#endif _Bool is_connected; _Bool have_reconnected; -}; +}; /* }}} */ typedef struct mb_host_s mb_host_t; struct mb_data_group_s; typedef struct mb_data_group_s mb_data_group_t; -struct mb_data_group_s +struct mb_data_group_s /* {{{ */ { mb_data_t *registers; size_t registers_num; mb_data_group_t *next; -}; +}; /* }}} */ /* * Global variables */ static mb_data_t *data_definitions = NULL; +static mb_datagroup_t *data_groups = NULL; /* * Functions @@ -202,6 +237,44 @@ static int data_copy_by_name (mb_data_t **dst, mb_data_t *src, /* {{{ */ return (data_copy (dst, ptr)); } /* }}} int data_copy_by_name */ +static mb_datagroup_t *datagroup_get_by_name (mb_datagroup_t *src, /* {{{ */ + const char *name) +{ + mb_datagroup_t *ptr; + + if (name == NULL) + return (NULL); + + for (ptr = src; ptr != NULL; ptr = ptr->next) + if (strcasecmp (ptr->name, name) == 0) + return (ptr); + + return (NULL); +} /* }}} mb_datagroup_t *datagroup_get_by_name */ + +static int datagroup_append (mb_datagroup_t **dst, mb_datagroup_t *src) /* {{{ */ +{ + mb_datagroup_t *ptr; + + if ((dst == NULL) || (src == NULL)) + return (EINVAL); + + ptr = *dst; + + if (ptr == NULL) + { + *dst = src; + return (0); + } + + while (ptr->next != NULL) + ptr = ptr->next; + + ptr->next = src; + + return (0); +} /* }}} int datagroup_append */ + /* Read functions */ static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */ @@ -212,12 +285,16 @@ 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) + host->interval = interval_g; + if (slave->instance[0] == 0) ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i", slave->id); vl.values = &value; vl.values_len = 1; + vl.interval = host->interval; sstrncpy (vl.host, host->host, sizeof (vl.host)); sstrncpy (vl.plugin, "modbus", sizeof (vl.plugin)); sstrncpy (vl.plugin_instance, slave->instance, sizeof (vl.plugin_instance)); @@ -251,6 +328,8 @@ static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */ return (conv.f); } /* }}} float mb_register_to_float */ +#if LEGACY_LIBMODBUS +/* Version 2.0.3 */ static int mb_init_connection (mb_host_t *host) /* {{{ */ { int status; @@ -292,6 +371,57 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ host->have_reconnected = 1; return (0); } /* }}} int mb_init_connection */ +/* #endif LEGACY_LIBMODBUS */ + +#else /* if !LEGACY_LIBMODBUS */ +/* Version 2.9.2 */ +static int mb_init_connection (mb_host_t *host) /* {{{ */ +{ + int status; + + if (host == NULL) + return (EINVAL); + + 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; + + 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->have_reconnected = 1; + ERROR ("Modbus plugin: Creating new Modbus/TCP object failed."); + return (-1); + } + + modbus_set_debug (host->connection, 1); + + /* We'll do the error handling ourselves. */ + modbus_set_error_recovery (host->connection, 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); + modbus_free (host->connection); + host->connection = NULL; + return (status); + } + + host->have_reconnected = 1; + return (0); +} /* }}} int mb_init_connection */ +#endif /* !LEGACY_LIBMODBUS */ #define CAST_TO_VALUE_T(ds,vt,raw) do { \ if ((ds)->ds[0].type == DS_TYPE_COUNTER) \ @@ -346,22 +476,46 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ else values_num = 1; +#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". */ +# define modbus_read_registers(ctx, addr, nb, dest) \ + read_holding_registers (&(ctx), slave->id, (addr), (nb), (dest)) +#else /* if !LEGACY_LIBMODBUS */ + /* Version 2.9.2: Set the slave id once before querying the registers. */ + status = modbus_set_slave (host->connection, slave->id); + if (status != 0) + { + ERROR ("Modbus plugin: modbus_set_slave (%i) failed with status %i.", + slave->id, status); + return (-1); + } +#endif + for (i = 0; i < 2; i++) { - status = read_holding_registers (&host->connection, - /* slave = */ slave->id, /* start_addr = */ data->register_base, + 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 LEGACY_LIBMODBUS modbus_close (&host->connection); - host->is_connected = 0; + host->is_connected = 0; +#else + modbus_close (host->connection); + modbus_free (host->connection); + host->connection = NULL; +#endif + } /* If we already tried reconnecting this round, give up. */ if (host->have_reconnected) { - ERROR ("Modbus plugin: read_holding_registers (%s) failed. " + ERROR ("Modbus plugin: modbus_read_registers (%s) failed. " "Reconnecting has already been tried. Giving up.", host->host); return (-1); } @@ -371,7 +525,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ status = mb_init_connection (host); if (status != 0) { - ERROR ("Modbus plugin: read_holding_registers (%s) failed. " + ERROR ("Modbus plugin: modbus_read_registers (%s) failed. " "While trying to reconnect, connecting to \"%s\" failed. " "Giving up.", host->host, host->node); @@ -385,7 +539,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ } /* for (i = 0, 1) */ DEBUG ("Modbus plugin: mb_read_data: Success! " - "read_holding_registers returned with status %i.", status); + "modbus_read_registers returned with status %i.", status); if (data->register_type == REG_TYPE_FLOAT) { @@ -692,6 +846,24 @@ static int mb_config_add_slave (mb_host_t *host, oconfig_item_t *ci) /* {{{ */ data_copy_by_name (&slave->collect, data_definitions, buffer); status = 0; /* continue after failure. */ } + else if (strcasecmp ("Datagroup", child->key) == 0) + { + char buffer[1024]; + mb_datagroup_t *ds; + + status = cf_util_get_string_buffer (child, buffer, sizeof (buffer)); + if (status == 0) { + ds = datagroup_get_by_name (data_groups, buffer); + if (ds) { + mb_data_t *data; + for (data = ds->collect; data != NULL; data = data->next) + { + data_copy (&slave->collect, data); + } + } + } + status = 0; /* continue after failure. */ + } else { ERROR ("Modbus plugin: Unknown configuration option: %s", child->key); @@ -753,7 +925,7 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ status = -1; } else if (strcasecmp ("Interval", child->key) == 0) - status = cf_util_get_int (child, &host->interval); + status = cf_util_get_cdtime (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); @@ -779,21 +951,18 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ { user_data_t ud; char name[1024]; - struct timespec interval; + struct timespec interval = { 0, 0 }; 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; + CDTIME_T_TO_TIMESPEC (host->interval, &interval); - plugin_register_complex_read (name, mb_read, - (interval.tv_sec > 0) ? &interval : NULL, + plugin_register_complex_read (/* group = */ NULL, name, + /* callback = */ mb_read, + /* interval = */ (host->interval > 0) ? &interval : NULL, &ud); } else @@ -804,6 +973,65 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ return (status); } /* }}} int mb_config_add_host */ + +static int mb_config_add_datagroup (oconfig_item_t *ci) /* {{{ */ +{ + mb_datagroup_t *datagroup; + int status; + int i; + + datagroup = malloc (sizeof (*datagroup)); + if (datagroup == NULL) + return (ENOMEM); + memset (datagroup, 0, sizeof (*datagroup)); + datagroup->name = NULL; + datagroup->collect = NULL; + datagroup->next = NULL; + + status = cf_util_get_string (ci, &datagroup->name); + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + status = 0; + + if (strcasecmp ("Collect", child->key) == 0) + { + char buffer[1024]; + status = cf_util_get_string_buffer (child, buffer, sizeof (buffer)); + if (status == 0) { + data_copy_by_name (&datagroup->collect, data_definitions, buffer); + } + status = 0; /* continue after failure. */ + } + else + { + ERROR ("Modbus plugin: Unknown configuration option: %s", child->key); + status = -1; + } + + if (status != 0) + break; + } /* for (i = 0; i < ci->children_num; i++) */ + + if ((status == 0) && (datagroup->collect == NULL)) + status = ENOENT; + + if (status == 0) + { + datagroup_append (&data_groups, datagroup); + } + else /* if (status != 0) */ + { + sfree (datagroup->name); + data_free_all (datagroup->collect); + assert (datagroup->next == NULL); + sfree (datagroup); + } + + return (status); +} /* }}} int mb_config_add_datagroup */ + static int mb_config (oconfig_item_t *ci) /* {{{ */ { int i; @@ -819,6 +1047,8 @@ static int mb_config (oconfig_item_t *ci) /* {{{ */ mb_config_add_data (child); else if (strcasecmp ("Host", child->key) == 0) mb_config_add_host (child); + else if (strcasecmp ("Datagroup", child->key) == 0) + mb_config_add_datagroup (child); else ERROR ("Modbus plugin: Unknown configuration option: %s", child->key); } @@ -828,47 +1058,6 @@ static int mb_config (oconfig_item_t *ci) /* {{{ */ /* ========= */ -#if 0 -static int foo (void) /* {{{ */ -{ - int status; - uint16_t values[2]; - int values_num; - - if (dev == NULL) - return (EINVAL); - - printf ("mb_read (addr = %i, float = %s);\n", register_addr, - is_float ? "true" : "false"); - - memset (values, 0, sizeof (values)); - if (is_float) - values_num = 2; - else - values_num = 1; - - status = read_holding_registers (dev->connection, - /* slave = */ 1, /* start_addr = */ register_addr, - /* num_registers = */ values_num, /* buffer = */ values); - printf ("read_coil_status returned with status %i\n", status); - if (status <= 0) - return (EAGAIN); - - if (is_float) - { - float value = mb_register_to_float (values[0], values[1]); - printf ("read_coil_status returned value %g (hi %#"PRIx16", lo %#"PRIx16")\n", - value, values[0], values[1]); - } - else - { - printf ("read_coil_status returned value %"PRIu16"\n", values[0]); - } - - return (0); -} /* }}} int foo */ -#endif - static int mb_shutdown (void) /* {{{ */ { data_free_all (data_definitions);