Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/inputs/modbus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions plugins/inputs/modbus/configuration_metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
14 changes: 9 additions & 5 deletions plugins/inputs/modbus/configuration_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -389,8 +391,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)
Expand Down
74 changes: 43 additions & 31 deletions plugins/inputs/modbus/modbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -517,12 +535,11 @@ 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
ftags := map[string]string{}
ftags := make(map[string]string, len(tags)+len(field.tags))
for k, v := range tags {
ftags[k] = v
}
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions plugins/inputs/modbus/sample_general_begin.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
duplicated in measurement "modbus"
Original file line number Diff line number Diff line change
@@ -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},
]

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
modbus,name=modbus,slave_id=1 3x0135:INT=134i,4x0102:INT=0i,4x0103:INT=101i 1729239973009490185
Original file line number Diff line number Diff line change
@@ -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'},
]
Loading