Add Modbus proxy (#4981)
This commit is contained in:
parent
3e4b50a3bc
commit
a5b611a83e
9 changed files with 387 additions and 26 deletions
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/evcc-io/evcc/server"
|
||||
autoauth "github.com/evcc-io/evcc/server/auth"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/modbus"
|
||||
"github.com/evcc-io/evcc/vehicle"
|
||||
"github.com/evcc-io/evcc/vehicle/wrapper"
|
||||
"github.com/gorilla/handlers"
|
||||
|
|
@ -47,13 +48,14 @@ type config struct {
|
|||
Network networkConfig
|
||||
Log string
|
||||
SponsorToken string
|
||||
Telemetry bool
|
||||
Plant string // telemetry plant id
|
||||
Telemetry bool
|
||||
Metrics bool
|
||||
Profile bool
|
||||
Levels map[string]string
|
||||
Interval time.Duration
|
||||
Mqtt mqttConfig
|
||||
ModbusProxy []proxyConfig
|
||||
Database dbConfig
|
||||
Javascript map[string]interface{}
|
||||
Influx server.InfluxConfig
|
||||
|
|
@ -73,6 +75,12 @@ type mqttConfig struct {
|
|||
Topic string
|
||||
}
|
||||
|
||||
type proxyConfig struct {
|
||||
Port int
|
||||
ReadOnly bool
|
||||
modbus.Settings `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
type dbConfig struct {
|
||||
Type string
|
||||
Dsn string
|
||||
|
|
|
|||
10
cmd/setup.go
10
cmd/setup.go
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/evcc-io/evcc/server"
|
||||
"github.com/evcc-io/evcc/server/db"
|
||||
"github.com/evcc-io/evcc/server/db/settings"
|
||||
"github.com/evcc-io/evcc/server/modbus"
|
||||
"github.com/evcc-io/evcc/tariff"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/locale"
|
||||
|
|
@ -106,6 +107,15 @@ func configureEnvironment(cmd *cobra.Command, conf config) (err error) {
|
|||
err = configureMQTT(conf.Mqtt)
|
||||
}
|
||||
|
||||
// setup modbus proxy listeners
|
||||
if err == nil {
|
||||
for _, cfg := range conf.ModbusProxy {
|
||||
if err = modbus.StartProxy(cfg.Port, cfg.Settings, cfg.ReadOnly); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup javascript VMs
|
||||
if err == nil {
|
||||
err = configureJavascript(conf.Javascript)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,14 @@ levels:
|
|||
cache: error
|
||||
db: error
|
||||
|
||||
# modbus proxy for allowing external programs to reuse the evcc modbus connection
|
||||
# each entry will start a proxy instance at the given port speaking Modbus TCP and
|
||||
# relaying to the given modbus downstream device (either TCP or RTU, RS485 or TCP)
|
||||
modbusproxy:
|
||||
# - port: 5200
|
||||
# uri: localhost:502
|
||||
# readonly: true
|
||||
|
||||
# meter definitions
|
||||
# name can be freely chosen and is used as reference when assigning meters to site and loadpoints
|
||||
# for documentation see https://docs.evcc.io/docs/devices/meters
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -8,6 +8,7 @@ require (
|
|||
github.com/Masterminds/sprig/v3 v3.2.2
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/andig/gosunspec v0.0.0-20211108155140-af2e73b86e71
|
||||
github.com/andig/mbserver v0.0.0-20221101145037-fcaa2dfef9fb
|
||||
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef
|
||||
github.com/avast/retry-go/v3 v3.1.1
|
||||
github.com/aws/aws-sdk-go v1.44.127
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -98,6 +98,8 @@ github.com/andig/go-powerwall v0.2.1-0.20220205120646-e5220ad9a9a0 h1:9fQ/afZNwq
|
|||
github.com/andig/go-powerwall v0.2.1-0.20220205120646-e5220ad9a9a0/go.mod h1:NA12RXBKXFQFx3Nb9xMTQjHIphu1Fl64i/gQMuRdMZc=
|
||||
github.com/andig/gosunspec v0.0.0-20211108155140-af2e73b86e71 h1:tnjVNZjuz+CK6fdc7ohJpMHjcEGFI5APp0l5T5Ocr/Y=
|
||||
github.com/andig/gosunspec v0.0.0-20211108155140-af2e73b86e71/go.mod h1:c6P6szcR+ROkqZruOR4f6qbDKFjZX6OitPpj+yJ/r8k=
|
||||
github.com/andig/mbserver v0.0.0-20221101145037-fcaa2dfef9fb h1:cw8kFzV0kbG4E4Bv4UOmbgNkhCEZ0p7frNCFO4LKzm0=
|
||||
github.com/andig/mbserver v0.0.0-20221101145037-fcaa2dfef9fb/go.mod h1:4VtYzTm//oUipwvO3yh0g/udTE7pYJM+U/kyAuFDsgM=
|
||||
github.com/andig/rct v0.0.0-20221101081802-96d01efdc68c h1:iXNLsesR2rTRPmr+QbjGgRTAOue8QpIkibakicNR7Qg=
|
||||
github.com/andig/rct v0.0.0-20221101081802-96d01efdc68c/go.mod h1:0lfd2mmBnBzIvuzYtdhG+2371u+cUfIxsYErm4P9KRI=
|
||||
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
|
||||
|
|
@ -292,6 +294,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
|||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
|
|
@ -774,6 +777,7 @@ github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR
|
|||
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc=
|
||||
github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/simonvetter/modbus v1.6.0 h1:RDHJevtc7LDIVoHAbhDun8fy+QwnGe+ZU+sLm9ZZzjc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
|
|
|
|||
141
server/modbus/handler.go
Normal file
141
server/modbus/handler.go
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
package modbus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/andig/mbserver"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/modbus"
|
||||
gridx "github.com/grid-x/modbus"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
log *util.Logger
|
||||
readOnly bool
|
||||
mbserver.RequestHandler
|
||||
conn *modbus.Connection
|
||||
}
|
||||
|
||||
func bytesAsUint16(b []byte) []uint16 {
|
||||
u := make([]uint16, 0, len(b)/2)
|
||||
for i := 0; i < len(b)/2; i++ {
|
||||
u = append(u, binary.BigEndian.Uint16(b[2*i:]))
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func asBytes(u []uint16) []byte {
|
||||
b := make([]byte, 2*len(u))
|
||||
for i, u := range u {
|
||||
binary.BigEndian.PutUint16(b[2*i:], u)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func bytesAsBool(b []byte) []bool {
|
||||
var res []bool
|
||||
for _, c := range bytesAsUint16(b) {
|
||||
if c != 0 {
|
||||
res = append(res, true)
|
||||
continue
|
||||
}
|
||||
res = append(res, false)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func boolAsBytes(b []bool) []byte {
|
||||
res := make([]byte, 2*len(b))
|
||||
for i, bb := range b {
|
||||
if bb {
|
||||
binary.BigEndian.PutUint16(res[2*i:], 0xFF00)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (h *handler) logResult(op string, b []byte, err error) {
|
||||
if err == nil {
|
||||
h.log.TRACE.Printf(op+" response: %0x", b)
|
||||
} else {
|
||||
h.log.TRACE.Printf(op+" response: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) exceptionToUint16AndError(op string, b []byte, err error) ([]uint16, error) {
|
||||
h.logResult(op, b, err)
|
||||
|
||||
var modbusError *gridx.Error
|
||||
if errors.As(err, &modbusError) {
|
||||
err = mbserver.MapExceptionCodeToError(modbusError.ExceptionCode)
|
||||
}
|
||||
|
||||
return bytesAsUint16(b), err
|
||||
}
|
||||
|
||||
func (h *handler) exceptionToBoolAndError(op string, b []byte, err error) ([]bool, error) {
|
||||
h.logResult(op, b, err)
|
||||
|
||||
var modbusError *gridx.Error
|
||||
if errors.As(err, &modbusError) {
|
||||
err = mbserver.MapExceptionCodeToError(modbusError.ExceptionCode)
|
||||
}
|
||||
|
||||
return bytesAsBool(b), err
|
||||
}
|
||||
|
||||
func (h *handler) HandleCoils(req *mbserver.CoilsRequest) ([]bool, error) {
|
||||
if req.IsWrite {
|
||||
if h.readOnly {
|
||||
return nil, mbserver.ErrIllegalFunction
|
||||
}
|
||||
|
||||
if req.Quantity == 1 {
|
||||
h.log.TRACE.Printf("write coil: id: %d addr: %d val: %t", req.UnitId, req.Addr, req.Args[0])
|
||||
var u uint16
|
||||
if req.Args[0] {
|
||||
u = 0xFF00
|
||||
}
|
||||
|
||||
b, err := h.conn.WriteSingleCoilWithSlave(req.UnitId, req.Addr, u)
|
||||
return h.exceptionToBoolAndError("write coil", b, err)
|
||||
}
|
||||
|
||||
h.log.TRACE.Printf("write multiple coils: id: %d addr: %d qty: %d val: %v", req.UnitId, req.Addr, req.Quantity, req.Args)
|
||||
b, err := h.conn.WriteMultipleCoilsWithSlave(req.UnitId, req.Addr, req.Quantity, boolAsBytes(req.Args))
|
||||
return h.exceptionToBoolAndError("write multiple coils", b, err)
|
||||
}
|
||||
|
||||
h.log.TRACE.Printf("read coil: id: %d addr: %d qty: %d", req.UnitId, req.Addr, req.Quantity)
|
||||
b, err := h.conn.ReadCoilsWithSlave(req.UnitId, req.Addr, req.Quantity)
|
||||
return h.exceptionToBoolAndError("read coil", b, err)
|
||||
}
|
||||
|
||||
func (h *handler) HandleInputRegisters(req *mbserver.InputRegistersRequest) (res []uint16, err error) {
|
||||
h.log.TRACE.Printf("read input: id: %d addr: %d qty: %d", req.UnitId, req.Addr, req.Quantity)
|
||||
b, err := h.conn.ReadInputRegistersWithSlave(req.UnitId, req.Addr, req.Quantity)
|
||||
return h.exceptionToUint16AndError("read input", b, err)
|
||||
}
|
||||
|
||||
func (h *handler) HandleHoldingRegisters(req *mbserver.HoldingRegistersRequest) (res []uint16, err error) {
|
||||
if req.IsWrite {
|
||||
if h.readOnly {
|
||||
return nil, mbserver.ErrIllegalFunction
|
||||
}
|
||||
|
||||
if req.Quantity == 1 {
|
||||
h.log.TRACE.Printf("write holding: id: %d addr: %d val: %0x", req.UnitId, req.Addr, req.Args[0])
|
||||
b, err := h.conn.WriteSingleRegisterWithSlave(req.UnitId, req.Addr, req.Args[0])
|
||||
return h.exceptionToUint16AndError("write holding", b, err)
|
||||
}
|
||||
|
||||
h.log.TRACE.Printf("write multiple holding: id: %d addr: %d qty: %d val: %0x", req.UnitId, req.Addr, req.Quantity, asBytes(req.Args))
|
||||
b, err := h.conn.WriteMultipleRegistersWithSlave(req.UnitId, req.Addr, req.Quantity, asBytes(req.Args))
|
||||
return h.exceptionToUint16AndError("write multiple holding", b, err)
|
||||
}
|
||||
|
||||
h.log.TRACE.Printf("read holding: id: %d addr: %d qty: %d", req.UnitId, req.Addr, req.Quantity)
|
||||
b, err := h.conn.ReadHoldingRegistersWithSlave(req.UnitId, req.Addr, req.Quantity)
|
||||
return h.exceptionToUint16AndError("read holding", b, err)
|
||||
}
|
||||
45
server/modbus/proxy.go
Normal file
45
server/modbus/proxy.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package modbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/andig/mbserver"
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/modbus"
|
||||
"github.com/evcc-io/evcc/util/sponsor"
|
||||
)
|
||||
|
||||
func StartProxy(port int, config modbus.Settings, readOnly bool) error {
|
||||
conn, err := modbus.NewConnection(config.URI, config.Device, config.Comset, config.Baudrate, modbus.ProtocolFromRTU(config.RTU), config.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !sponsor.IsAuthorized() {
|
||||
return api.ErrSponsorRequired
|
||||
}
|
||||
|
||||
h := &handler{
|
||||
log: util.NewLogger(fmt.Sprintf("proxy-%d", port)),
|
||||
readOnly: readOnly,
|
||||
RequestHandler: new(mbserver.DummyHandler),
|
||||
conn: conn,
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.log.DEBUG.Printf("modbus proxy for %s listening at :%d", config.String(), port)
|
||||
|
||||
srv, err := mbserver.New(h)
|
||||
|
||||
if err == nil {
|
||||
err = srv.Start(l)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
70
server/modbus/proxy_test.go
Normal file
70
server/modbus/proxy_test.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package modbus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/andig/mbserver"
|
||||
"github.com/evcc-io/evcc/util/modbus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProxyRead(t *testing.T) {
|
||||
l, err := net.Listen("tcp", ":0")
|
||||
assert.NoError(t, err)
|
||||
defer l.Close()
|
||||
|
||||
t.Log(l.Addr().String())
|
||||
|
||||
conn, err := modbus.NewConnection(l.Addr().String(), "", "", 0, modbus.Tcp, 1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
h := &echoHandler{
|
||||
id: 0,
|
||||
RequestHandler: new(mbserver.DummyHandler),
|
||||
conn: conn,
|
||||
}
|
||||
|
||||
srv, _ := mbserver.New(h)
|
||||
assert.NoError(t, srv.Start(l))
|
||||
defer func() { _ = srv.Stop() }()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 1; i <= 10; i++ {
|
||||
wg.Add(1)
|
||||
|
||||
go func(id int) {
|
||||
for i := 0; i < 50; i++ {
|
||||
addr := uint16(rand.Int31n(200) + 1)
|
||||
|
||||
b, err := conn.ReadInputRegistersWithSlave(uint8(id), addr, 1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if err == nil {
|
||||
assert.Equal(t, addr^uint16(id), binary.BigEndian.Uint16(b))
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(rand.Int31n(1000)) * time.Microsecond)
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
type echoHandler struct {
|
||||
id int
|
||||
mbserver.RequestHandler
|
||||
conn *modbus.Connection
|
||||
}
|
||||
|
||||
func (h *echoHandler) HandleInputRegisters(req *mbserver.InputRegistersRequest) (res []uint16, err error) {
|
||||
return []uint16{req.Addr ^ uint16(req.UnitId)}, err
|
||||
}
|
||||
|
|
@ -46,15 +46,23 @@ type Settings struct {
|
|||
RTU *bool // indicates RTU over TCP if true
|
||||
}
|
||||
|
||||
func (s *Settings) String() string {
|
||||
if s.URI != "" {
|
||||
return s.URI
|
||||
}
|
||||
return s.Device
|
||||
}
|
||||
|
||||
// Connection decorates a meters.Connection with transparent slave id and error handling
|
||||
type Connection struct {
|
||||
slaveID uint8
|
||||
mu sync.Mutex
|
||||
conn meters.Connection
|
||||
delay time.Duration
|
||||
}
|
||||
|
||||
func (mb *Connection) prepare() {
|
||||
mb.conn.Slave(mb.slaveID)
|
||||
func (mb *Connection) prepare(slaveID uint8) {
|
||||
mb.conn.Slave(slaveID)
|
||||
if mb.delay > 0 {
|
||||
time.Sleep(mb.delay)
|
||||
}
|
||||
|
|
@ -88,71 +96,137 @@ func (mb *Connection) Timeout(timeout time.Duration) {
|
|||
}
|
||||
|
||||
// ReadCoils wraps the underlying implementation
|
||||
func (mb *Connection) ReadCoils(address, quantity uint16) ([]byte, error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) ReadCoilsWithSlave(slaveID uint8, address, quantity uint16) ([]byte, error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().ReadCoils(address, quantity))
|
||||
}
|
||||
|
||||
// WriteSingleCoil wraps the underlying implementation
|
||||
func (mb *Connection) WriteSingleCoil(address, quantity uint16) ([]byte, error) {
|
||||
mb.prepare()
|
||||
return mb.handle(mb.conn.ModbusClient().WriteSingleCoil(address, quantity))
|
||||
func (mb *Connection) WriteSingleCoilWithSlave(slaveID uint8, address, value uint16) ([]byte, error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().WriteSingleCoil(address, value))
|
||||
}
|
||||
|
||||
// ReadInputRegisters wraps the underlying implementation
|
||||
func (mb *Connection) ReadInputRegisters(address, quantity uint16) ([]byte, error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) ReadInputRegistersWithSlave(slaveID uint8, address, quantity uint16) ([]byte, error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().ReadInputRegisters(address, quantity))
|
||||
}
|
||||
|
||||
// ReadHoldingRegisters wraps the underlying implementation
|
||||
func (mb *Connection) ReadHoldingRegisters(address, quantity uint16) ([]byte, error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) ReadHoldingRegistersWithSlave(slaveID uint8, address, quantity uint16) ([]byte, error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().ReadHoldingRegisters(address, quantity))
|
||||
}
|
||||
|
||||
// WriteSingleRegister wraps the underlying implementation
|
||||
func (mb *Connection) WriteSingleRegister(address, value uint16) ([]byte, error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) WriteSingleRegisterWithSlave(slaveID uint8, address, value uint16) ([]byte, error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().WriteSingleRegister(address, value))
|
||||
}
|
||||
|
||||
// WriteMultipleRegisters wraps the underlying implementation
|
||||
func (mb *Connection) WriteMultipleRegisters(address, quantity uint16, value []byte) ([]byte, error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) WriteMultipleRegistersWithSlave(slaveID uint8, address, quantity uint16, value []byte) ([]byte, error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().WriteMultipleRegisters(address, quantity, value))
|
||||
}
|
||||
|
||||
// ReadDiscreteInputs wraps the underlying implementation
|
||||
func (mb *Connection) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) ReadDiscreteInputsWithSlave(slaveID uint8, address, quantity uint16) (results []byte, err error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().ReadDiscreteInputs(address, quantity))
|
||||
}
|
||||
|
||||
// WriteMultipleCoils wraps the underlying implementation
|
||||
func (mb *Connection) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) WriteMultipleCoilsWithSlave(slaveID uint8, address, quantity uint16, value []byte) (results []byte, err error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().WriteMultipleCoils(address, quantity, value))
|
||||
}
|
||||
|
||||
// ReadWriteMultipleRegisters wraps the underlying implementation
|
||||
func (mb *Connection) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) ReadWriteMultipleRegistersWithSlave(slaveID uint8, readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity, value))
|
||||
}
|
||||
|
||||
// MaskWriteRegister wraps the underlying implementation
|
||||
func (mb *Connection) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) MaskWriteRegisterWithSlave(slaveID uint8, address, andMask, orMask uint16) (results []byte, err error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().MaskWriteRegister(address, andMask, orMask))
|
||||
}
|
||||
|
||||
// ReadFIFOQueue wraps the underlying implementation
|
||||
func (mb *Connection) ReadFIFOQueue(address uint16) (results []byte, err error) {
|
||||
mb.prepare()
|
||||
func (mb *Connection) ReadFIFOQueueWithSlave(slaveID uint8, address uint16) (results []byte, err error) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.prepare(slaveID)
|
||||
return mb.handle(mb.conn.ModbusClient().ReadFIFOQueue(address))
|
||||
}
|
||||
|
||||
func (mb *Connection) ReadCoils(address, quantity uint16) ([]byte, error) {
|
||||
return mb.ReadCoilsWithSlave(mb.slaveID, address, quantity)
|
||||
}
|
||||
|
||||
func (mb *Connection) WriteSingleCoil(address, quantity uint16) ([]byte, error) {
|
||||
return mb.WriteSingleCoilWithSlave(mb.slaveID, address, quantity)
|
||||
}
|
||||
|
||||
func (mb *Connection) ReadInputRegisters(address, quantity uint16) ([]byte, error) {
|
||||
return mb.ReadInputRegistersWithSlave(mb.slaveID, address, quantity)
|
||||
}
|
||||
|
||||
func (mb *Connection) ReadHoldingRegisters(address, quantity uint16) ([]byte, error) {
|
||||
return mb.ReadHoldingRegistersWithSlave(mb.slaveID, address, quantity)
|
||||
}
|
||||
|
||||
func (mb *Connection) WriteSingleRegister(address, value uint16) ([]byte, error) {
|
||||
return mb.WriteSingleRegisterWithSlave(mb.slaveID, address, value)
|
||||
}
|
||||
|
||||
func (mb *Connection) WriteMultipleRegisters(address, quantity uint16, value []byte) ([]byte, error) {
|
||||
return mb.WriteMultipleRegistersWithSlave(mb.slaveID, address, quantity, value)
|
||||
}
|
||||
|
||||
func (mb *Connection) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) {
|
||||
return mb.ReadDiscreteInputsWithSlave(mb.slaveID, address, quantity)
|
||||
}
|
||||
|
||||
func (mb *Connection) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) {
|
||||
return mb.WriteMultipleCoilsWithSlave(mb.slaveID, address, quantity, value)
|
||||
}
|
||||
|
||||
func (mb *Connection) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) {
|
||||
return mb.ReadWriteMultipleRegistersWithSlave(mb.slaveID, readAddress, readQuantity, writeAddress, writeQuantity, value)
|
||||
}
|
||||
|
||||
func (mb *Connection) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) {
|
||||
return mb.MaskWriteRegisterWithSlave(mb.slaveID, address, andMask, orMask)
|
||||
}
|
||||
|
||||
func (mb *Connection) ReadFIFOQueue(address uint16) (results []byte, err error) {
|
||||
return mb.ReadFIFOQueueWithSlave(mb.slaveID, address)
|
||||
}
|
||||
|
||||
var (
|
||||
connections = make(map[string]meters.Connection)
|
||||
mu sync.Mutex
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue