feat(inputs.modbus): Allow grouping across register types (#16040)

This commit is contained in:
Sven Rebhan 2024-10-24 17:50:16 +02:00 committed by GitHub
parent c73559dc6c
commit f06111499e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 110 additions and 39 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
duplicated in measurement "modbus"

View File

@ -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},
]

View File

@ -0,0 +1 @@
modbus,name=modbus,slave_id=1 3x0135:INT=134i,4x0102:INT=0i,4x0103:INT=101i 1729239973009490185

View File

@ -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'},
]