Merge branch 'collectd-5.4'
[collectd.git] / src / modbus.c
index cad4b2c..795465f 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * 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
@@ -49,6 +49,7 @@
  *   RegisterBase 1234
  *   RegisterType float
  *   Type gauge
+ *   ModbusRegisterType holding
  *   Instance "..."
  * </Data>
  *
  */
 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;
 
 struct mb_data_s;
 typedef struct mb_data_s mb_data_t;
@@ -82,6 +91,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];
 
@@ -113,7 +123,6 @@ struct mb_host_s /* {{{ */
   modbus_t *connection;
 #endif
   _Bool is_connected;
-  _Bool have_reconnected;
 }; /* }}} */
 typedef struct mb_host_s mb_host_t;
 
@@ -235,7 +244,7 @@ static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     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",
@@ -258,6 +267,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;
 
@@ -286,13 +296,6 @@ 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);
-
   modbus_set_debug (&host->connection, 1);
 
   /* We'll do the error handling ourselves. */
@@ -317,7 +320,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   }
 
   host->is_connected = 1;
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 /* #endif LEGACY_LIBMODBUS */
@@ -334,10 +336,6 @@ 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;
 
@@ -347,7 +345,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   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);
   }
@@ -367,7 +364,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
     return (status);
   }
 
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 #endif /* !LEGACY_LIBMODBUS */
@@ -390,7 +386,6 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   int values_num;
   const data_set_t *ds;
   int status;
-  int i;
 
   if ((host == NULL) || (slave == NULL) || (data == NULL))
     return (EINVAL);
@@ -411,6 +406,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   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. "
@@ -419,12 +415,51 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   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;
 
+  status = 0;
+  if (host->connection == NULL)
+  {
+    status = EBADF;
+  }
+  else
+  {
+    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". */
@@ -440,52 +475,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);
@@ -502,12 +515,47 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     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);
 
@@ -563,9 +611,6 @@ 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++)
   {
@@ -663,6 +708,10 @@ static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
       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)
@@ -675,6 +724,28 @@ static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
         status = -1;
       }
     }
+    else if (strcasecmp ("ModbusRegisterType", child->key) == 0)
+    {
+#if LEGACY_LIBMODBUS
+      ERROR("Modbus plugin: ModbusRegisterType 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 ("holding", tmp) == 0)
+        data.modbus_register_type = MREG_HOLDING;
+      else if (strcasecmp ("input", 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);