diff --git a/plugins/inputs/modbus/README.md b/plugins/inputs/modbus/README.md index 76e829093..bc6be1452 100644 --- a/plugins/inputs/modbus/README.md +++ b/plugins/inputs/modbus/README.md @@ -69,6 +69,11 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ## |---metric -- define fields on a metric base configuration_type = "register" + ## Exclude the register type tag + ## Please note, this will also influence the grouping of metrics as you won't + ## see one metric per register type anymore! + # exclude_register_type_tag = false + ## --- "register" configuration style --- ## Measurements diff --git a/plugins/inputs/modbus/configuration_metric.go b/plugins/inputs/modbus/configuration_metric.go index 7547ec137..70323dda8 100644 --- a/plugins/inputs/modbus/configuration_metric.go +++ b/plugins/inputs/modbus/configuration_metric.go @@ -36,8 +36,10 @@ type ConfigurationPerMetric struct { Optimization string `toml:"optimization"` MaxExtraRegisters uint16 `toml:"optimization_max_register_fill"` Metrics []metricDefinition `toml:"metric"` - workarounds ModbusWorkarounds - logger telegraf.Logger + + workarounds ModbusWorkarounds + excludeRegisterType bool + logger telegraf.Logger } func (c *ConfigurationPerMetric) SampleConfigPart() string { @@ -343,8 +345,10 @@ func (c *ConfigurationPerMetric) fieldID(seed maphash.Seed, def metricDefinition mh.WriteByte(def.SlaveID) mh.WriteByte(0) - mh.WriteString(field.RegisterType) - mh.WriteByte(0) + if !c.excludeRegisterType { + mh.WriteString(field.RegisterType) + mh.WriteByte(0) + } mh.WriteString(def.Measurement) mh.WriteByte(0) mh.WriteString(field.Name) diff --git a/plugins/inputs/modbus/configuration_request.go b/plugins/inputs/modbus/configuration_request.go index d443016f2..644f361ee 100644 --- a/plugins/inputs/modbus/configuration_request.go +++ b/plugins/inputs/modbus/configuration_request.go @@ -38,9 +38,11 @@ type requestDefinition struct { } type ConfigurationPerRequest struct { - Requests []requestDefinition `toml:"request"` - workarounds ModbusWorkarounds - logger telegraf.Logger + Requests []requestDefinition `toml:"request"` + + workarounds ModbusWorkarounds + excludeRegisterType bool + logger telegraf.Logger } func (c *ConfigurationPerRequest) SampleConfigPart() string { @@ -388,8 +390,10 @@ func (c *ConfigurationPerRequest) fieldID(seed maphash.Seed, def requestDefiniti mh.WriteByte(def.SlaveID) mh.WriteByte(0) - mh.WriteString(def.RegisterType) - mh.WriteByte(0) + if !c.excludeRegisterType { + mh.WriteString(def.RegisterType) + mh.WriteByte(0) + } mh.WriteString(field.Measurement) mh.WriteByte(0) mh.WriteString(field.Name) diff --git a/plugins/inputs/modbus/modbus.go b/plugins/inputs/modbus/modbus.go index 69ee6df17..abd727fac 100644 --- a/plugins/inputs/modbus/modbus.go +++ b/plugins/inputs/modbus/modbus.go @@ -47,21 +47,22 @@ type RS485Config struct { // Modbus holds all data relevant to the plugin type Modbus struct { - Name string `toml:"name"` - Controller string `toml:"controller"` - TransmissionMode string `toml:"transmission_mode"` - BaudRate int `toml:"baud_rate"` - DataBits int `toml:"data_bits"` - Parity string `toml:"parity"` - StopBits int `toml:"stop_bits"` - RS485 *RS485Config `toml:"rs485"` - Timeout config.Duration `toml:"timeout"` - Retries int `toml:"busy_retries"` - RetriesWaitTime config.Duration `toml:"busy_retries_wait"` - DebugConnection bool `toml:"debug_connection" deprecated:"1.35.0;use 'log_level' 'trace' instead"` - Workarounds ModbusWorkarounds `toml:"workarounds"` - ConfigurationType string `toml:"configuration_type"` - Log telegraf.Logger `toml:"-"` + Name string `toml:"name"` + Controller string `toml:"controller"` + TransmissionMode string `toml:"transmission_mode"` + BaudRate int `toml:"baud_rate"` + DataBits int `toml:"data_bits"` + Parity string `toml:"parity"` + StopBits int `toml:"stop_bits"` + RS485 *RS485Config `toml:"rs485"` + Timeout config.Duration `toml:"timeout"` + Retries int `toml:"busy_retries"` + RetriesWaitTime config.Duration `toml:"busy_retries_wait"` + DebugConnection bool `toml:"debug_connection" deprecated:"1.35.0;use 'log_level' 'trace' instead"` + Workarounds ModbusWorkarounds `toml:"workarounds"` + ConfigurationType string `toml:"configuration_type"` + ExcludeRegisterTypeTag bool `toml:"exclude_register_type_tag"` + Log telegraf.Logger `toml:"-"` // Configuration type specific settings ConfigurationOriginal @@ -147,10 +148,12 @@ func (m *Modbus) Init() error { cfg = &m.ConfigurationOriginal case "request": m.ConfigurationPerRequest.workarounds = m.Workarounds + m.ConfigurationPerRequest.excludeRegisterType = m.ExcludeRegisterTypeTag m.ConfigurationPerRequest.logger = m.Log cfg = &m.ConfigurationPerRequest case "metric": m.ConfigurationPerMetric.workarounds = m.Workarounds + m.ConfigurationPerMetric.excludeRegisterType = m.ExcludeRegisterTypeTag m.ConfigurationPerMetric.logger = m.Log cfg = &m.ConfigurationPerMetric default: @@ -242,21 +245,36 @@ func (m *Modbus) Gather(acc telegraf.Accumulator) error { } timestamp := time.Now() + grouper := metric.NewSeriesGrouper() tags := map[string]string{ "name": m.Name, - "type": cCoils, "slave_id": strconv.Itoa(int(slaveID)), } - m.collectFields(acc, timestamp, tags, requests.coil) - tags["type"] = cDiscreteInputs - m.collectFields(acc, timestamp, tags, requests.discrete) + if !m.ExcludeRegisterTypeTag { + tags["type"] = cCoils + } + m.collectFields(grouper, timestamp, tags, requests.coil) - tags["type"] = cHoldingRegisters - m.collectFields(acc, timestamp, tags, requests.holding) + if !m.ExcludeRegisterTypeTag { + tags["type"] = cDiscreteInputs + } + m.collectFields(grouper, timestamp, tags, requests.discrete) - tags["type"] = cInputRegisters - m.collectFields(acc, timestamp, tags, requests.input) + if !m.ExcludeRegisterTypeTag { + tags["type"] = cHoldingRegisters + } + m.collectFields(grouper, timestamp, tags, requests.holding) + + if !m.ExcludeRegisterTypeTag { + tags["type"] = cInputRegisters + } + m.collectFields(grouper, timestamp, tags, requests.input) + + // Add the metrics grouped by series to the accumulator + for _, x := range grouper.Metrics() { + acc.AddMetric(x) + } } // Disconnect after read if configured @@ -517,8 +535,7 @@ func (m *Modbus) gatherRequestsInput(requests []request) error { return nil } -func (m *Modbus) collectFields(acc telegraf.Accumulator, timestamp time.Time, tags map[string]string, requests []request) { - grouper := metric.NewSeriesGrouper() +func (m *Modbus) collectFields(grouper *metric.SeriesGrouper, timestamp time.Time, tags map[string]string, requests []request) { for _, request := range requests { for _, field := range request.fields { // Collect tags from global and per-request @@ -539,11 +556,6 @@ func (m *Modbus) collectFields(acc telegraf.Accumulator, timestamp time.Time, ta grouper.Add(measurement, ftags, timestamp, field.name, field.value) } } - - // Add the metrics grouped by series to the accumulator - for _, x := range grouper.Metrics() { - acc.AddMetric(x) - } } // Implement the logger interface of the modbus client diff --git a/plugins/inputs/modbus/sample_general_begin.conf b/plugins/inputs/modbus/sample_general_begin.conf index 4a77eee58..e1688a9f3 100644 --- a/plugins/inputs/modbus/sample_general_begin.conf +++ b/plugins/inputs/modbus/sample_general_begin.conf @@ -50,3 +50,8 @@ ## |---request -- define fields on a requests base ## |---metric -- define fields on a metric base configuration_type = "register" + + ## Exclude the register type tag + ## Please note, this will also influence the grouping of metrics as you won't + ## see one metric per register type anymore! + # exclude_register_type_tag = false diff --git a/plugins/inputs/modbus/testcases/duplicate_fields_different_registers/init.err b/plugins/inputs/modbus/testcases/duplicate_fields_different_registers/init.err new file mode 100644 index 000000000..1037bff4c --- /dev/null +++ b/plugins/inputs/modbus/testcases/duplicate_fields_different_registers/init.err @@ -0,0 +1 @@ +duplicated in measurement "modbus" \ No newline at end of file diff --git a/plugins/inputs/modbus/testcases/duplicate_fields_different_registers/telegraf.conf b/plugins/inputs/modbus/testcases/duplicate_fields_different_registers/telegraf.conf new file mode 100644 index 000000000..9709b6281 --- /dev/null +++ b/plugins/inputs/modbus/testcases/duplicate_fields_different_registers/telegraf.conf @@ -0,0 +1,24 @@ +[[inputs.modbus]] + name = "Device" + controller = "tcp://localhost:502" + configuration_type = "request" + exclude_register_type_tag = true + + [[inputs.modbus.request]] + slave_id = 1 + register = "holding" + fields = [ + { name = "humidity", type = "INT16", scale=1.0, address = 1}, + { name = "temperature", type = "INT16", scale=1.0, address = 4}, + { name = "active", type = "INT16", scale=1.0, address = 7}, + ] + + [[inputs.modbus.request]] + slave_id = 1 + register = "input" + fields = [ + { name = "humidity", type = "INT16", scale=1.0, address = 2}, + { name = "temperature", type = "INT16", scale=1.0, address = 5}, + { name = "active", type = "INT16", scale=1.0, address = 8}, + ] + diff --git a/plugins/inputs/modbus/testcases/metric_style_issue_16031/expected.out b/plugins/inputs/modbus/testcases/metric_style_issue_16031/expected.out new file mode 100644 index 000000000..b0deeb551 --- /dev/null +++ b/plugins/inputs/modbus/testcases/metric_style_issue_16031/expected.out @@ -0,0 +1 @@ +modbus,name=modbus,slave_id=1 3x0135:INT=134i,4x0102:INT=0i,4x0103:INT=101i 1729239973009490185 diff --git a/plugins/inputs/modbus/testcases/metric_style_issue_16031/telegraf.conf b/plugins/inputs/modbus/testcases/metric_style_issue_16031/telegraf.conf new file mode 100644 index 000000000..fe0562a36 --- /dev/null +++ b/plugins/inputs/modbus/testcases/metric_style_issue_16031/telegraf.conf @@ -0,0 +1,15 @@ +[[inputs.modbus]] + name_override = "modbus" + name = "modbus" + timeout = "1s" + controller = "tcp://172.16.2.31:502" + configuration_type = "metric" + exclude_register_type_tag = true + [[inputs.modbus.metric]] + slave_id = 1 + byte_order = "ABCD" + fields = [ + {register = "holding", address = 101, name = '4x0102:INT', type = 'INT16'}, + {register = "holding", address = 102, name = '4x0103:INT', type = 'INT16'}, + {register = "input", address = 134, name = '3x0135:INT', type = 'INT16'}, + ]