Modbus proxy: make readonly ignore writes, add deny option (BC) (#10658)
This commit is contained in:
parent
cdd9e856a8
commit
0242dad7e9
7 changed files with 124 additions and 7 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ type goConfig struct {
|
|||
|
||||
type proxyConfig struct {
|
||||
Port int
|
||||
ReadOnly bool
|
||||
ReadOnly string
|
||||
modbus.Settings `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
11
server/modbus/readonlymode.go
Normal file
11
server/modbus/readonlymode.go
Normal 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
|
||||
)
|
||||
90
server/modbus/readonlymode_enumer.go
Normal file
90
server/modbus/readonlymode_enumer.go
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue