modbus plugin: Restore compatibility to libmodbus 2.0.3.
[collectd.git] / src / modbus.c
index 52e6bd8..6a753e1 100644 (file)
 #include "plugin.h"
 #include "configfile.h"
 
+#include <netdb.h>
+
 #include <modbus/modbus.h>
 
+#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
+
 /*
  * <Data "data_name">
  *   RegisterBase 1234
 /*
  * 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,23 +78,23 @@ struct mb_data_s
   char instance[DATA_MAX_NAME_LEN];
 
   mb_data_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;
@@ -92,18 +102,18 @@ struct mb_host_s
   modbus_param_t connection;
   _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
@@ -204,6 +214,33 @@ static int data_copy_by_name (mb_data_t **dst, mb_data_t *src, /* {{{ */
 
 /* Read functions */
 
+static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */
+    mb_data_t *data, value_t value)
+{
+  value_list_t vl = VALUE_LIST_INIT;
+
+  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));
+  sstrncpy (vl.type, data->type, sizeof (vl.type));
+  sstrncpy (vl.type_instance, data->instance, sizeof (vl.type_instance));
+
+  return (plugin_dispatch_values (&vl));
+} /* }}} int mb_submit */
+
 static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
 {
   union
@@ -270,6 +307,17 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   return (0);
 } /* }}} int mb_init_connection */
 
+#define CAST_TO_VALUE_T(ds,vt,raw) do { \
+  if ((ds)->ds[0].type == DS_TYPE_COUNTER) \
+    (vt).counter = (counter_t) (raw); \
+  else if ((ds)->ds[0].type == DS_TYPE_GAUGE) \
+    (vt).gauge = (gauge_t) (raw); \
+  else if ((ds)->ds[0].type == DS_TYPE_DERIVE) \
+    (vt).derive = (derive_t) (raw); \
+  else /* if (ds->ds[0].type == DS_TYPE_ABSOLUTE) */ \
+    (vt).absolute = (absolute_t) (raw); \
+} while (0)
+
 static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     mb_data_t *data)
 {
@@ -297,6 +345,14 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     return (-1);
   }
 
+  if ((ds->ds[0].type != DS_TYPE_GAUGE)
+      && (data->register_type != REG_TYPE_UINT32))
+  {
+    NOTICE ("Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
+        "This will most likely result in problems, because the register type "
+        "is not UINT32.", data->type, DS_TYPE_TO_STRING (ds->ds[0].type));
+  }
+
   memset (values, 0, sizeof (values));
   if ((data->register_type == REG_TYPE_UINT32)
       || (data->register_type == REG_TYPE_FLOAT))
@@ -347,11 +403,37 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
 
   if (data->register_type == REG_TYPE_FLOAT)
   {
-    float value;
+    float float_value;
+    value_t vt;
 
-    value = mb_register_to_float (values[0], values[1]);
+    float_value = mb_register_to_float (values[0], values[1]);
     DEBUG ("Modbus plugin: mb_read_data: "
-        "Returned float value is %g", (double) value);
+        "Returned float value is %g", (double) float_value);
+
+    CAST_TO_VALUE_T (ds, vt, float_value);
+    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];
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned uint32 value is %"PRIu32, v32);
+
+    CAST_TO_VALUE_T (ds, vt, v32);
+    mb_submit (host, slave, data, vt);
+  }
+  else /* if (data->register_type == REG_TYPE_UINT16) */
+  {
+    value_t vt;
+
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned uint16 value is %"PRIu16, values[0]);
+
+    CAST_TO_VALUE_T (ds, vt, values[0]);
+    mb_submit (host, slave, data, vt);
   }
 
   return (0);
@@ -685,7 +767,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);
@@ -711,21 +793,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
@@ -760,47 +839,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);