From e43b9039749d0dacef6cbc4116da5b2b767e2037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20S=C3=B6lver?= Date: Mon, 12 Oct 2020 20:35:25 +0200 Subject: [PATCH] First commit --- client.go | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ client_test.go | 32 +++++++++++++++ go.mod | 5 +++ go.sum | 10 +++++ 4 files changed, 156 insertions(+) create mode 100644 client.go create mode 100644 client_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/client.go b/client.go new file mode 100644 index 0000000..381b4ed --- /dev/null +++ b/client.go @@ -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 +} diff --git a/client_test.go b/client_test.go new file mode 100644 index 0000000..000e775 --- /dev/null +++ b/client_test.go @@ -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) + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6d1f6e5 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module mbtcpclient + +go 1.15 + +require github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..56d62e7 --- /dev/null +++ b/go.sum @@ -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=