From 3650d74de2b4b2ca3638435354b836ae5fd0e970 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Mon, 30 Mar 2020 22:30:42 +0200 Subject: [PATCH] Add possibility to specify measurement per register (#7231) --- plugins/inputs/modbus/README.md | 25 ++++++++-------- plugins/inputs/modbus/modbus.go | 53 ++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/plugins/inputs/modbus/README.md b/plugins/inputs/modbus/README.md index 2c73ba5ec..98661e84d 100644 --- a/plugins/inputs/modbus/README.md +++ b/plugins/inputs/modbus/README.md @@ -12,17 +12,17 @@ The Modbus plugin collects Discrete Inputs, Coils, Input Registers and Holding R ## ## Device name name = "Device" - + ## Slave ID - addresses a MODBUS device on the bus ## Range: 0 - 255 [0 = broadcast; 248 - 255 = reserved] slave_id = 1 - + ## Timeout for each request timeout = "1s" - + # TCP - connect via Modbus/TCP controller = "tcp://localhost:502" - + # Serial (RS485; RS232) #controller = "file:///dev/ttyUSB0" #baud_rate = 9600 @@ -30,15 +30,15 @@ The Modbus plugin collects Discrete Inputs, Coils, Input Registers and Holding R #parity = "N" #stop_bits = 1 #transmission_mode = "RTU" - - + + ## Measurements ## - + ## Digital Variables, Discrete Inputs and Coils ## name - the variable name ## address - variable address - + discrete_inputs = [ { name = "Start", address = [0]}, { name = "Stop", address = [1]}, @@ -49,11 +49,12 @@ The Modbus plugin collects Discrete Inputs, Coils, Input Registers and Holding R { name = "Motor1-Run", address = [0]}, { name = "Motor1-Jog", address = [1]}, { name = "Motor1-Stop", address = [2]}, - ] - + ] + ## Analog Variables, Input Registers and Holding Registers + ## measurement - the (optional) measurement name, defaults to "modbus" ## name - the variable name - ## byte_order - the ordering of bytes + ## byte_order - the ordering of bytes ## |---AB, ABCD - Big Endian ## |---BA, DCBA - Little Endian ## |---BADC - Mid-Big Endian @@ -61,7 +62,7 @@ The Modbus plugin collects Discrete Inputs, Coils, Input Registers and Holding R ## data_type - INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT32, FLOAT32-IEEE (the IEEE 754 binary representation) ## scale - the final numeric variable representation ## address - variable address - + holding_registers = [ { name = "PowerFactor", byte_order = "AB", data_type = "FLOAT32", scale=0.01, address = [8]}, { name = "Voltage", byte_order = "AB", data_type = "FLOAT32", scale=0.1, address = [0]}, diff --git a/plugins/inputs/modbus/modbus.go b/plugins/inputs/modbus/modbus.go index 46dc748c7..6775465b6 100644 --- a/plugins/inputs/modbus/modbus.go +++ b/plugins/inputs/modbus/modbus.go @@ -7,10 +7,12 @@ import ( "net" "net/url" "sort" + "time" mb "github.com/goburrow/modbus" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/metric" "github.com/influxdata/telegraf/plugins/inputs" ) @@ -44,12 +46,13 @@ type register struct { } type fieldContainer struct { - Name string `toml:"name"` - ByteOrder string `toml:"byte_order"` - DataType string `toml:"data_type"` - Scale float64 `toml:"scale"` - Address []uint16 `toml:"address"` - value interface{} + Measurement string `toml:"measurement"` + Name string `toml:"name"` + ByteOrder string `toml:"byte_order"` + DataType string `toml:"data_type"` + Scale float64 `toml:"scale"` + Address []uint16 `toml:"address"` + value interface{} } type registerRange struct { @@ -97,14 +100,15 @@ const sampleConfig = ` ## ## Digital Variables, Discrete Inputs and Coils - ## name - the variable name - ## address - variable address + ## measurement - the (optional) measurement name, defaults to "modbus" + ## name - the variable name + ## address - variable address discrete_inputs = [ { name = "start", address = [0]}, { name = "stop", address = [1]}, { name = "reset", address = [2]}, - { name = "emergency_stop", address = [3]}, + { name = "emergency_stop", address = [3]}, ] coils = [ { name = "motor1_run", address = [0]}, @@ -113,8 +117,9 @@ const sampleConfig = ` ] ## Analog Variables, Input Registers and Holding Registers - ## name - the variable name - ## byte_order - the ordering of bytes + ## measurement - the (optional) measurement name, defaults to "modbus" + ## name - the variable name + ## byte_order - the ordering of bytes ## |---AB, ABCD - Big Endian ## |---BA, DCBA - Little Endian ## |---BADC - Mid-Big Endian @@ -134,7 +139,7 @@ const sampleConfig = ` input_registers = [ { name = "tank_level", byte_order = "AB", data_type = "INT16", scale=1.0, address = [0]}, { name = "tank_ph", byte_order = "AB", data_type = "INT16", scale=1.0, address = [1]}, - { name = "pump1_speed", byte_order = "ABCD", data_type = "INT32", scale=1.0, address = [3,4]}, + { name = "pump1_speed", byte_order = "ABCD", data_type = "INT32", scale=1.0, address = [3,4]}, ] ` @@ -319,10 +324,11 @@ func validateFieldContainers(t []fieldContainer, n string) error { } //search name duplicate - if nameEncountered[item.Name] { - return fmt.Errorf("name '%s' is duplicated in '%s' - '%s'", item.Name, n, item.Name) + canonical_name := item.Measurement + "." + item.Name + if nameEncountered[canonical_name] { + return fmt.Errorf("name '%s' is duplicated in measurement '%s' '%s' - '%s'", item.Name, item.Measurement, n, item.Name) } else { - nameEncountered[item.Name] = true + nameEncountered[canonical_name] = true } if n == cInputRegisters || n == cHoldingRegisters { @@ -635,6 +641,7 @@ func (m *Modbus) Gather(acc telegraf.Accumulator) error { } } + timestamp := time.Now() err := m.getFields() if err != nil { disconnect(m) @@ -642,18 +649,28 @@ func (m *Modbus) Gather(acc telegraf.Accumulator) error { return err } + grouper := metric.NewSeriesGrouper() for _, reg := range m.registers { - fields := make(map[string]interface{}) tags := map[string]string{ "name": m.Name, "type": reg.Type, } for _, field := range reg.Fields { - fields[field.Name] = field.value + // In case no measurement was specified we use "modbus" as default + measurement := "modbus" + if field.Measurement != "" { + measurement = field.Measurement + } + + // Group the data by series + grouper.Add(measurement, tags, timestamp, field.Name, field.value) } - acc.AddFields("modbus", fields, tags) + // Add the metrics grouped by series to the accumulator + for _, metric := range grouper.Metrics() { + acc.AddMetric(metric) + } } return nil