First commit
This commit is contained in:
109
client.go
Normal file
109
client.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package mbtcpclient
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Mbclient struct {
|
||||
transactionCounter uint16
|
||||
conn net.Conn
|
||||
header [7]byte
|
||||
unit uint8
|
||||
}
|
||||
|
||||
func New(address string, unit uint8) (*Mbclient, error) {
|
||||
var err error
|
||||
|
||||
c := new(Mbclient)
|
||||
c.conn, err = net.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.unit = unit
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *Mbclient) ReadRegisters(first uint16, numRegs uint16) ([]uint16, error) {
|
||||
const requestLength = 12
|
||||
m.transactionCounter++
|
||||
req := make([]byte, requestLength)
|
||||
var responseHeader mbapHeader
|
||||
var mbpayload mbPDU
|
||||
|
||||
binary.BigEndian.PutUint16(req[0:2], m.transactionCounter)
|
||||
binary.BigEndian.PutUint16(req[2:4], 0)
|
||||
binary.BigEndian.PutUint16(req[4:6], 6) // Length
|
||||
req[6] = m.unit
|
||||
req[7] = 3 //FunctionCode
|
||||
binary.BigEndian.PutUint16(req[8:10], first-1)
|
||||
binary.BigEndian.PutUint16(req[10:12], numRegs)
|
||||
byteswritten, err := m.conn.Write(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if byteswritten != requestLength {
|
||||
return nil, fmt.Errorf("Failed to send request")
|
||||
}
|
||||
m.conn.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
_, err = io.ReadFull(m.conn, m.header[:])
|
||||
responseHeader.unMarshal(m.header)
|
||||
expectedDataLength := responseHeader.length - 1
|
||||
|
||||
response := make([]byte, expectedDataLength)
|
||||
_, err = m.conn.Read(response)
|
||||
|
||||
err = mbpayload.unMarshal(response)
|
||||
if mbpayload.functionCode != 3 {
|
||||
return nil, fmt.Errorf("modbus exception %v", mbpayload.functionCode&0x7F)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mbpayload.registers, nil
|
||||
}
|
||||
|
||||
type mbapHeader struct {
|
||||
transactionID uint16
|
||||
protocolIdentifier uint16
|
||||
length uint16
|
||||
unitIdentifier byte
|
||||
}
|
||||
|
||||
// func (m *mbapHeader) marshalBinary() ([]byte, error) {
|
||||
|
||||
// }
|
||||
|
||||
func (m *mbapHeader) unMarshal(data [7]byte) error {
|
||||
m.transactionID = binary.BigEndian.Uint16(data[0:2])
|
||||
m.protocolIdentifier = binary.BigEndian.Uint16(data[2:4])
|
||||
m.length = binary.BigEndian.Uint16(data[4:6])
|
||||
m.unitIdentifier = data[6]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type mbPDU struct {
|
||||
functionCode uint8
|
||||
length uint8
|
||||
registers []uint16
|
||||
}
|
||||
|
||||
func (d *mbPDU) unMarshal(data []byte) error {
|
||||
d.functionCode = data[0]
|
||||
d.length = data[1]
|
||||
if d.length+2 != uint8(len(data)) {
|
||||
return fmt.Errorf("Lenght mismatch in modbus payload")
|
||||
}
|
||||
d.registers = make([]uint16, d.length/2)
|
||||
var n uint8
|
||||
for n < d.length/2 {
|
||||
d.registers[n] = binary.BigEndian.Uint16(data[n*2+2 : n*2+4])
|
||||
n++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
32
client_test.go
Normal file
32
client_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package mbtcpclient
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReadOneRegister(t *testing.T) {
|
||||
c, err := New("192.168.0.154:502", 1)
|
||||
assert.NoError(t, err)
|
||||
res, err := c.ReadRegisters(12401, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 2)
|
||||
t.Log(res)
|
||||
|
||||
res, err = c.ReadRegisters(12102, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 2)
|
||||
t.Log(res)
|
||||
|
||||
res, err = c.ReadRegisters(12544, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 1)
|
||||
t.Log( float32(res[0])/10)
|
||||
|
||||
res, err = c.ReadRegisters(12136, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 1)
|
||||
t.Log(res)
|
||||
|
||||
}
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module mbtcpclient
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/stretchr/testify v1.6.1
|
||||
10
go.sum
Normal file
10
go.sum
Normal file
@@ -0,0 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
Reference in New Issue
Block a user