Modbus: unify delay and timeout settings (BC) (#31996)

This commit is contained in:
andig 2026-07-23 17:03:28 +02:00 • committed by GitHub
parent ff2905a366
commit 02a66252ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
82 changed files with 410 additions and 369 deletions

View file

@ -6,6 +6,7 @@ import (
"fmt"
"strings"
"sync"
"time"
"github.com/evcc-io/evcc/util"
"github.com/volkszaehler/mbmd/meters"
@ -26,21 +27,57 @@ const (
// RTU field is included for compatibility with modbus.tpl which renders rtu: false for TCP
// TODO remove RTU field (https://github.com/evcc-io/evcc/issues/3360)
type TcpSettings struct {
URI string
ID uint8
RTU *bool `mapstructure:"rtu"`
URI string
ID uint8
RTU *bool `mapstructure:"rtu"`
Delay time.Duration
Timeout time.Duration
}
// Connection creates a modbus TCP connection from the settings
func (s TcpSettings) Connection(ctx context.Context) (*Connection, error) {
settings := Settings{
ID: s.ID,
URI: s.URI,
RTU: s.RTU,
Delay: s.Delay,
Timeout: s.Timeout,
}
return settings.Connection(ctx, Tcp)
}
// Settings contains the ModBus settings
type Settings struct {
ID uint8 `json:"id,omitempty" yaml:",omitempty"`
SubDevice int `json:"subdevice,omitempty" yaml:",omitempty"`
URI string `json:"uri,omitempty" yaml:",omitempty"`
Device string `json:"device,omitempty" yaml:",omitempty"`
Comset string `json:"comset,omitempty" yaml:",omitempty"`
Baudrate int `json:"baudrate,omitempty" yaml:",omitempty"`
UDP bool `json:"udp,omitempty" yaml:",omitempty"`
RTU *bool `json:"rtu,omitempty" yaml:",omitempty"`
ID uint8 `json:"id,omitempty" yaml:",omitempty"`
SubDevice int `json:"subdevice,omitempty" yaml:",omitempty"`
URI string `json:"uri,omitempty" yaml:",omitempty"`
Device string `json:"device,omitempty" yaml:",omitempty"`
Comset string `json:"comset,omitempty" yaml:",omitempty"`
Baudrate int `json:"baudrate,omitempty" yaml:",omitempty"`
UDP bool `json:"udp,omitempty" yaml:",omitempty"`
RTU *bool `json:"rtu,omitempty" yaml:",omitempty"`
Delay time.Duration `json:"delay,omitempty" yaml:",omitempty"`
Timeout time.Duration `json:"timeout,omitempty" yaml:",omitempty"`
}
// Connection creates a modbus connection from the settings, applying delay and timeout.
// The optional proto overrides the protocol derived from the settings.
func (s Settings) Connection(ctx context.Context, proto ...Protocol) (*Connection, error) {
p := s.Protocol()
if len(proto) > 0 {
p = proto[0]
}
conn, err := NewConnection(ctx, s.URI, s.Device, s.Comset, s.Baudrate, p, s.ID)
if err != nil {
return nil, err
}
conn.Timeout(s.Timeout)
conn.Delay(s.Delay)
return conn, nil
}
// Protocol identifies the wire format from the RTU setting

View file

@ -40,6 +40,17 @@ func (c *configDefaults) Load() {
panic("failed to parse config defaults: " + err.Error())
}
// inherit shared param properties into modbus definitions, except examples
// which would render into every modbus template
for i, p := range c.Modbus.Definitions {
if idx, def := c.ParamByName(p.Name); idx >= 0 {
example := p.Example
p.OverwriteProperties(def)
p.Example = example
c.Modbus.Definitions[i] = p
}
}
// resolve modbus param references
for typ := range c.Modbus.Types {
for i, p := range c.Modbus.Types[typ].Params {

View file

@ -878,6 +878,14 @@ modbus:
en: Port
default: 502
type: int
- name: delay
help:
de: Wartezeit vor jedem Registerzugriff
en: Wait time before each register access
type: duration
advanced: true
- name: timeout
advanced: true
types:
rs485serial:
description:
@ -887,6 +895,8 @@ modbus:
- name: device
- name: baudrate
- name: comset
- name: delay
- name: timeout
rs485tcpip:
description:
generic: Serial (Ethernet-RS485 Adapter)
@ -895,6 +905,8 @@ modbus:
- name: host
- name: port
default: 502
- name: delay
- name: timeout
tcpip:
description:
generic: TCP/IP
@ -903,6 +915,8 @@ modbus:
- name: host
- name: port
default: 502
- name: delay
- name: timeout
udp:
description:
generic: UDP
@ -911,6 +925,8 @@ modbus:
- name: host
- name: port
default: 502
- name: delay
- name: timeout
devicegroups:
generic:

View file

@ -6,6 +6,12 @@ id: {{ .id }}
device: {{ .device }} # USB-RS485 Adapter Adresse
baudrate: {{ .baudrate }} # Prüfe die Geräteeinstellungen, typische Werte sind 9600, 19200, 38400, 57600, 115200
comset: "{{ .comset }}" # Kommunikationsparameter für den Adapter
{{- if .delay }}
delay: {{ .delay }} # optional
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }} # optional
{{- end }}
{{- end }}
{{- if .rs485tcpip }}
@ -14,6 +20,12 @@ modbus: rs485tcpip
id: {{ .id }}
host: {{ .host }} # Hostname
port: {{ .port }} # Port
{{- if .delay }}
delay: {{ .delay }} # optional
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }} # optional
{{- end }}
{{- end }}
{{- if .tcpip }}
@ -22,4 +34,10 @@ modbus: tcpip
id: {{ .id }}
host: {{ .host }} # Hostname
port: {{ .port }} # Port
{{- if .delay }}
delay: {{ .delay }} # optional
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }} # optional
{{- end }}
{{- end -}}

View file

@ -22,4 +22,10 @@ rtu: true
# configuration error - should not happen
modbusConnectionTypeNotDefined: {{ .modbus }}
{{- end }}
{{- if .delay }}
delay: {{ .delay }}
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }}
{{- end }}
{{- end }}

View file

@ -3,6 +3,7 @@ package templates
import (
_ "embed"
"fmt"
"slices"
"strconv"
"strings"
)
@ -29,7 +30,11 @@ func (t *Template) ModbusParams(modbusType string, values map[string]any) {
return
}
modbusParams := ConfigDefaults.Modbus.Types[values[ParamModbus].(string)].Params
// skip params the template already defines (e.g. deprecated delay, timeout)
modbusParams := slices.DeleteFunc(slices.Clone(ConfigDefaults.Modbus.Types[values[ParamModbus].(string)].Params), func(p Param) bool {
i, _ := t.ParamByName(p.Name)
return i >= 0
})
// add the modbus params at the beginning
t.Params = append(modbusParams, t.Params...)
@ -90,6 +95,14 @@ func (t *Template) ModbusValues(renderMode int, values map[string]any) {
if modbusParam.Comset != "" {
defaultValue = modbusParam.Comset
}
case ModbusParamDelay:
if modbusParam.Delay != "" {
defaultValue = modbusParam.Delay
}
case ModbusParamTimeout:
if modbusParam.Timeout != "" {
defaultValue = modbusParam.Timeout
}
}
if defaultValue != "" {

View file

@ -36,6 +36,8 @@ const (
ModbusParamHost = "host"
ModbusParamPort = "port"
ModbusParamRTU = "rtu"
ModbusParamDelay = "delay"
ModbusParamTimeout = "timeout"
)
const (
@ -51,6 +53,7 @@ var (
ModbusParams = []string{
ModbusParamId, ModbusParamDevice, ModbusParamBaudrate, ModbusParamComset,
ModbusParamURI, ModbusParamHost, ModbusParamPort, ModbusParamRTU,
ModbusParamDelay, ModbusParamTimeout,
}
ModbusConnectionTypes = []string{
@ -234,6 +237,8 @@ type Param struct {
Comset string `json:",omitempty"` // device specific default for modbus RS485 comset
Port int `json:",omitempty"` // device specific default for modbus TCPIP port
ID int `json:",omitempty"` // device specific default for modbus ID
Delay string `json:",omitempty"` // device specific default for modbus delay
Timeout string `json:",omitempty"` // device specific default for modbus timeout
}
// DefaultValue returns a default or example value depending on the renderMode