Modbus proxy: make readonly ignore writes, add deny option (BC) (#10658)

This commit is contained in:
andig 2023-11-08 08:28:08 +01:00 • committed by GitHub
parent cdd9e856a8
commit 0242dad7e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 124 additions and 7 deletions

View file

@ -179,7 +179,13 @@ func runRoot(cmd *cobra.Command, args []string) {
// setup modbus proxy
if err == nil {
for _, cfg := range conf.ModbusProxy {
if err = modbus.StartProxy(cfg.Port, cfg.Settings, cfg.ReadOnly); err != nil {
var mode modbus.ReadOnlyMode
mode, err = modbus.ReadOnlyModeString(cfg.ReadOnly)
if err != nil {
break
}
if err = modbus.StartProxy(cfg.Port, cfg.Settings, mode); err != nil {
break
}
}

View file

@ -113,7 +113,7 @@ type goConfig struct {
type proxyConfig struct {
Port int
ReadOnly bool
ReadOnly string
modbus.Settings `mapstructure:",squash"`
}

View file

@ -49,7 +49,7 @@ modbusproxy:
# - port: 5200
# uri: solar-edge:502
# # rtu: true
# # readonly: true
# # readonly: true # use `deny` to raise modbus errors
# meter definitions
# name can be freely chosen and is used as reference when assigning meters to site and loadpoints

View file

@ -13,7 +13,7 @@ import (
type handler struct {
log *util.Logger
readOnly bool
readOnly ReadOnlyMode
conn *modbus.Connection
}
@ -104,8 +104,13 @@ func (h *handler) HandleDiscreteInputs(req *mbserver.DiscreteInputsRequest) ([]b
func (h *handler) HandleCoils(req *mbserver.CoilsRequest) ([]bool, error) {
if req.IsWrite {
if h.readOnly {
switch h.readOnly {
case ReadOnlyDeny:
h.log.TRACE.Printf("deny: write coils: id %d addr %d qty %d val %v", req.UnitId, req.Addr, req.Quantity, req.Args)
return nil, mbserver.ErrIllegalFunction
case ReadOnlyTrue:
h.log.TRACE.Printf("ignore: write coils: id %d addr %d qty %d val %v", req.UnitId, req.Addr, req.Quantity, req.Args)
return req.Args, nil
}
if req.WriteFuncCode == gridx.FuncCodeWriteSingleCoil {
@ -138,8 +143,13 @@ func (h *handler) HandleInputRegisters(req *mbserver.InputRegistersRequest) ([]u
func (h *handler) HandleHoldingRegisters(req *mbserver.HoldingRegistersRequest) ([]uint16, error) {
if req.IsWrite {
if h.readOnly {
switch h.readOnly {
case ReadOnlyDeny:
h.log.TRACE.Printf("deny: write holdings: id %d addr %d qty %d val %0x", req.UnitId, req.Addr, req.Quantity, asBytes(req.Args))
return nil, mbserver.ErrIllegalFunction
case ReadOnlyTrue:
h.log.TRACE.Printf("ignore: write holdings: id %d addr %d qty %d val %0x", req.UnitId, req.Addr, req.Quantity, asBytes(req.Args))
return req.Args, nil
}
if req.WriteFuncCode == gridx.FuncCodeWriteSingleRegister {

View file

@ -11,7 +11,7 @@ import (
"github.com/evcc-io/evcc/util/sponsor"
)
func StartProxy(port int, config modbus.Settings, readOnly bool) error {
func StartProxy(port int, config modbus.Settings, readOnly ReadOnlyMode) error {
conn, err := modbus.NewConnection(config.URI, config.Device, config.Comset, config.Baudrate, modbus.ProtocolFromRTU(config.RTU), config.ID)
if err != nil {
return err

View file

@ -0,0 +1,11 @@
package modbus
// go:generate enumer -type ReadOnlyMode -trimprefix ReadOnly -transform=lower
type ReadOnlyMode int
const (
ReadOnlyFalse ReadOnlyMode = iota
ReadOnlyDeny
ReadOnlyTrue
)

View file

@ -0,0 +1,90 @@
// Code generated by "enumer -type ReadOnlyMode -trimprefix ReadOnly -transform=lower"; DO NOT EDIT.
package modbus
import (
"fmt"
"strings"
)
const _ReadOnlyModeName = "falsedenytrue"
var _ReadOnlyModeIndex = [...]uint8{0, 5, 9, 13}
const _ReadOnlyModeLowerName = "falsedenytrue"
func (i ReadOnlyMode) String() string {
if i < 0 || i >= ReadOnlyMode(len(_ReadOnlyModeIndex)-1) {
return fmt.Sprintf("ReadOnlyMode(%d)", i)
}
return _ReadOnlyModeName[_ReadOnlyModeIndex[i]:_ReadOnlyModeIndex[i+1]]
}
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
func _ReadOnlyModeNoOp() {
var x [1]struct{}
_ = x[ReadOnlyFalse-(0)]
_ = x[ReadOnlyDeny-(1)]
_ = x[ReadOnlyTrue-(2)]
}
var _ReadOnlyModeValues = []ReadOnlyMode{ReadOnlyFalse, ReadOnlyDeny, ReadOnlyTrue}
var _ReadOnlyModeNameToValueMap = map[string]ReadOnlyMode{
_ReadOnlyModeName[0:5]: ReadOnlyFalse,
_ReadOnlyModeLowerName[0:5]: ReadOnlyFalse,
_ReadOnlyModeName[5:9]: ReadOnlyDeny,
_ReadOnlyModeLowerName[5:9]: ReadOnlyDeny,
_ReadOnlyModeName[9:13]: ReadOnlyTrue,
_ReadOnlyModeLowerName[9:13]: ReadOnlyTrue,
}
var _ReadOnlyModeNames = []string{
_ReadOnlyModeName[0:5],
_ReadOnlyModeName[5:9],
_ReadOnlyModeName[9:13],
}
// ReadOnlyModeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func ReadOnlyModeString(s string) (ReadOnlyMode, error) {
if val, ok := _ReadOnlyModeNameToValueMap[s]; ok {
return val, nil
}
if val, ok := _ReadOnlyModeNameToValueMap[strings.ToLower(s)]; ok {
return val, nil
}
switch s {
case "", "0":
return ReadOnlyFalse, nil
case "1":
return ReadOnlyTrue, nil
}
return 0, fmt.Errorf("%s does not belong to ReadOnlyMode values", s)
}
// ReadOnlyModeValues returns all values of the enum
func ReadOnlyModeValues() []ReadOnlyMode {
return _ReadOnlyModeValues
}
// ReadOnlyModeStrings returns a slice of all String values of the enum
func ReadOnlyModeStrings() []string {
strs := make([]string, len(_ReadOnlyModeNames))
copy(strs, _ReadOnlyModeNames)
return strs
}
// IsAReadOnlyMode returns "true" if the value is listed in the enum definition. "false" otherwise
func (i ReadOnlyMode) IsAReadOnlyMode() bool {
for _, v := range _ReadOnlyModeValues {
if i == v {
return true
}
}
return false
}