[collectd] New plugin - mbus
Florian Forster
octo at collectd.org
Thu Dec 1 15:42:41 CET 2011
Hi Tomas,
On Mon, Nov 07, 2011 at 10:08:17PM +0100, Tomas wrote:
> I have made a new plugin for reading data from MBus devices.
thank you very much for your patch! :) It looks great, thank you very
much for also updating the manpage!
Please see my comments inline:
> +Only numerical records are supported and all are stored as GAUGE. The output structure is:
> +
> + host = <hostname>
> + plugin = "mbus"
> + plugin_instance = <MBus slave address> e.g. 237483A847B34800
> + value_type = <Record quantity> e.g. "Volume flow"
> + value_type_instance = <Record number> e.g. 1
Could you please document that MBus provides information on which data
type is submits (the quantity setting) and that this is used to chose a
"type" automatically? This wasn't obvious for me.
Regarding type handling, I'd prefer to have a "MBus type" to "collectd
type" map within the plugin, rather than relying on the library to
provide a string which we then massage to get something compatible to
collectd types. E.g.:
"External_temperature" -> "temperature"
"Response_delay_time" -> "delay"
"Dimensionless" -> "gauge"
"Bus_Address" -> IGNORE
> +Synopsis of the configuration is:
> +
> + <Plugin mbus>
> + IsSerial true
> + SerialDevice "/dev/ttyUSB0"
> + Host "none"
> + Port 256
Please document ports as strings, i.e. 'Port "256"'. They are generally
called "services" and strings. You can use cf_util_get_port_number()
from configfile.h or service_name_to_port_number() from common.h to
convert a string to a number if needed.
> + <Slave 59>
> + IgnoreSelected true
> + Record 2
> + Record 5
> + Record 6
> + </Slave>
> + <Slave 237483A847B34800>
I'd prefer to use '<Slave "237483A847B34800">'. If the argument looks
numeric (i.e. no A-F present), it will be parsed as a double, which may
cause trouble.
> +=item B<IsSerial> I<Boolean>
Can't we tell what the user wants from the presence of the
"SerialDevice" vs. "Host" settings?
> +Slave blocks define individual MBus slaves - devices t be read. B<Address> is either primary
t -> to
> --- src/mbus_utils.c (.../collectd-5.0.1) (revision 0)
> +++ src/mbus_utils.c (.../branches/collectd-5.0.1_mbus) (revision 14)
Why is a separate file necessary for these functions?
> +mbus_slave * mbus_slave_new(void)
> +{
> + DEBUG("mbus: mbus_slave_new - creating new slave");
> + mbus_slave * slave = (mbus_slave *) malloc(sizeof(mbus_slave));
Please use 'malloc (sizeof (*slave))'.
> + memset(slave, 0, sizeof(mbus_slave));
Dito.
> Index: src/mbus.c
> ===================================================================
> --- src/mbus.c (.../collectd-5.0.1) (revision 0)
> +++ src/mbus.c (.../branches/collectd-5.0.1_mbus) (revision 14)
> @@ -0,0 +1,596 @@
> +#include "collectd.h"
> +#include "common.h"
> +#include "plugin.h"
You're missing a copyright and license header. Please copy the header
from a file that is licensed under the desired license and adapt it.
> +static pthread_mutex_t plugin_lock; /**< Global lock fof the MBus access */
I don't think this is necessary. The configure and init functions are
called before worker threads are created, i.e. these callbacks are
called sequentially. The read function must be thread-safe, but not
reentrant-safe, i.e. it is never called twice by two different threads.
> +/* Note that the MBus library is not thread safe. On the other hand given the MBus */
> +/* nature - bus, synchronous communication (i.e. we can have only one operation in */
> +/* progress and have ot wait till it is finished) this is not a problem here for us. */
How is the library not thread-safe? Does it use strtok(3) or strerror(3)
or similar non-thread-safe functions?
> +static _Bool conf_is_serial = -1;
_Bool can't hold -1, please use 0 or 1. (If you want to keep this
variable, see comment above.)
> +static int collectd_mbus_config_slave (oconfig_item_t *ci)
...
> + if (ci->values_num == 1)
/* about 40 lines */
> + else
> + {
> + ERROR("mbus: collectd_mbus_config_slave - missing or wrong slave address");
> + mbus_slave_free(slave);
> + return -1;
> + }
Please use:
if (ci->values_num != 1) { /* handle error */; return (-1); }
> + /* first sort out the selection logic; last setting wins */
> + for (i = 0; i < ci->children_num; i++)
> + {
> + child = ci->children + i;
> + if ((strcasecmp ("IgnoreSelected", child->key) == 0) && (!cf_util_get_boolean (child, &ignore_selected)))
Please don't hide function calls, like the one to cf_util_get_boolean()
like this. Rather do:
for (i = 0; i < ci->children_num; i++)
{
child = ci->children + i;
if (strcasecmp ("IgnoreSelected", child->key) != 0)
continue;
cf_util_get_boolean (child, &ignore_selected);
DEBUG("mbus: collectd_mbus_config_slave - IgnoreSelected = %d", ignore_selected);
}
> +static int collectd_mbus_config (oconfig_item_t *ci)
...
> + if ((strcasecmp ("IsSerial", child->key) == 0) && (!cf_util_get_boolean (child, &conf_is_serial)))
> + ;
> + else if ((strcasecmp ("SerialDevice", child->key) == 0) && (!cf_util_get_string (child, &conf_device)))
> + ;
> + else if ((strcasecmp ("Host", child->key) == 0) && (!cf_util_get_string (child, &conf_host)))
> + ;
> + else if ((strcasecmp ("Port", child->key) == 0) && (!cf_util_get_int (child, &conf_port)))
> + ;
> + else if ((strcasecmp ("Slave", child->key) == 0) && (!collectd_mbus_config_slave (child)))
> + ;
> + else
> + WARNING ("mbus: collectd_mbus_config - unknown config option or unsupported config value: %s", child->key);
Dito:
if (strcasecmp ("IsSerial", child->key) == 0)
cf_util_get_boolean (child, &conf_is_serial);
else if ...
This will print a warning if the value couldn't be handled (incorrect
type, for example), so no need to print anything here.
> +static int parse_and_submit (mbus_slave * slave, mbus_frame * frame)
...
> + if (frame_data.type == MBUS_DATA_TYPE_FIXED)
> + {
/* ca. 110 lines */
> + }
> + else
> + {
> + if (frame_data.type == MBUS_DATA_TYPE_VARIABLE)
> + {
/* ca. 65 lines */
> + }
> + }
Can you please put this logic in separate functions? The huge if/else
construct is hard to read.
> + tmp_int = mbus_data_bcd_decode(data->id_bcd, 4);
What does this 4 do? Please document this inline like:
tmp_int = mbus_data_bcd_decode(data->id_bcd, /* bytes in int = */ 4);
> + if(mbus_slave_record_check(slave,0))
> + {
/* ca. 40 lines */
> + }
> + else
> + {
> + DEBUG("mbus: parse_and_submit - Record #0 disabled by mask");
> + }
Once this is in a separate function, use:
if (!mbus_slave_record_check(slave,0))
{
DEBUG ("... disabled ...");
return (0);
}
> + if(NULL != (record = mbus_parse_fixed_record(data->status, data->cnt1_type, data->cnt1_val)))
Please don't put function calls into if conditions (functions like
strcmp are an exception, of course). Instead, do:
record = mbus_parse_fixed_record(data->status, data->cnt1_type, data->cnt1_val);
if (record == NULL)
{
/* error handling */
return (-1);
}
/* normal code */
> + if(record->is_numeric)
> + values[0].gauge = record->value.real_val;
> + else
> + values[0].gauge = 0;
Please use NAN if the value is not numeric.
Thanks again and best regards,
—octo
--
Florian octo Forster
Hacker in training
GnuPG: 0x0C705A15
http://octo.it/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://mailman.verplant.org/pipermail/collectd/attachments/20111201/831c428f/attachment.pgp>
More information about the collectd
mailing list