Add support for reading input registers #7
24
client.go
24
client.go
@@ -19,9 +19,10 @@ type Mbclient struct {
|
||||
keepAliveDuration time.Duration
|
||||
timeOut time.Duration
|
||||
wg sync.WaitGroup
|
||||
registerOffset uint16
|
||||
}
|
||||
|
||||
func New(Address string, Unit uint8, KeepAlive, TimeOut time.Duration) (*Mbclient, error) {
|
||||
func New(Address string, Unit uint8, KeepAlive, TimeOut time.Duration, registerOffset uint16) (*Mbclient, error) {
|
||||
|
||||
c := new(Mbclient)
|
||||
c.address = Address
|
||||
@@ -30,6 +31,7 @@ func New(Address string, Unit uint8, KeepAlive, TimeOut time.Duration) (*Mbclien
|
||||
<-c.t.C
|
||||
c.keepAliveDuration = KeepAlive
|
||||
c.timeOut = TimeOut
|
||||
c.registerOffset = registerOffset
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -49,7 +51,17 @@ func (m *Mbclient) closeConn() {
|
||||
m.wg.Wait()
|
||||
}
|
||||
|
||||
func (m *Mbclient) ReadRegisters(first uint16, numRegs uint16) ([]uint16, error) {
|
||||
// Reads one or many holding registers (Function code 03)
|
||||
func (m *Mbclient) ReadHoldingRegisters(first uint16, numRegs uint16) ([]uint16, error) {
|
||||
return m.readRegisters(3, first, numRegs)
|
||||
}
|
||||
|
||||
// Reads one or many input registers (Function code 04)
|
||||
func (m *Mbclient) ReadInputRegisters(first uint16, numRegs uint16) ([]uint16, error) {
|
||||
return m.readRegisters(4, first, numRegs)
|
||||
}
|
||||
|
||||
func (m *Mbclient) readRegisters(functionCode uint8, first uint16, numRegs uint16) ([]uint16, error) {
|
||||
var err error
|
||||
// If The timer is expired, conn is closed and needs to be reopened
|
||||
if !m.t.Stop() {
|
||||
@@ -71,11 +83,11 @@ func (m *Mbclient) ReadRegisters(first uint16, numRegs uint16) ([]uint16, error)
|
||||
var mbpayload mbPDU
|
||||
|
||||
binary.BigEndian.PutUint16(req[0:2], m.transactionCounter)
|
||||
binary.BigEndian.PutUint16(req[2:4], 0)
|
||||
binary.BigEndian.PutUint16(req[2:4], 0) // Protocol identifier
|
||||
binary.BigEndian.PutUint16(req[4:6], 6) // Length
|
||||
req[6] = m.unit
|
||||
req[7] = 3 //FunctionCode
|
||||
binary.BigEndian.PutUint16(req[8:10], first-1)
|
||||
req[7] = functionCode
|
||||
binary.BigEndian.PutUint16(req[8:10], first-m.registerOffset)
|
||||
binary.BigEndian.PutUint16(req[10:12], numRegs)
|
||||
m.conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
|
||||
@@ -114,7 +126,7 @@ func (m *Mbclient) ReadRegisters(first uint16, numRegs uint16) ([]uint16, error)
|
||||
}
|
||||
|
||||
err = mbpayload.unMarshal(response)
|
||||
if mbpayload.functionCode != 3 {
|
||||
if mbpayload.functionCode != functionCode {
|
||||
m.t.Reset(m.keepAliveDuration)
|
||||
return nil, fmt.Errorf("modbus exception %v req: %x", mbpayload.functionCode&0x7F, req)
|
||||
}
|
||||
|
||||
@@ -7,58 +7,91 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testNibeHost = "NIBE-06543922346009.solver.nu:502"
|
||||
const testIAMhost = "IAM_248000012514.solver.nu:502"
|
||||
|
||||
func TestNibeInputRegsters(t *testing.T) {
|
||||
inputRegs := []uint16{1, 5, 7 /*8,*/, 9, 10, 11, 12, 13, 14, 16 /*26,*/ /*39,*/, 86, 1046, 1083, 1100, 1102, 1104, // S1155 specific
|
||||
40 /*46, 48, 50*/ /* 396, 398,*/, 1017 /*1567,*/, 1025, 1028, 1029, 1083, 1087, 1575, 1577 /*1581,*/, 1583, 1585, 1975} // common registers
|
||||
c, err := New(testNibeHost, 1, 100*time.Millisecond, 5*time.Second, 0)
|
||||
t.Log("Connect")
|
||||
assert.NoError(t, err)
|
||||
for _, reg := range inputRegs {
|
||||
|
||||
res, err := c.ReadInputRegisters(uint16(reg), 1)
|
||||
assert.NoError(t, err, "Failed to read reg %v", reg)
|
||||
|
||||
if err == nil {
|
||||
t.Logf("reg: %v res: %v \n", reg, res)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
func TestNibeHoldingRegsters(t *testing.T) {
|
||||
inputRegs := []uint16{18, 20, 22, 26, 30, 34, 38, 39, 40, 41, 45, 43, 44, 45, 56 /*97, 159,*/, 196, 197, 237}
|
||||
c, err := New(testNibeHost, 1, 100*time.Millisecond, 5*time.Second, 0)
|
||||
t.Log("Connect")
|
||||
assert.NoError(t, err)
|
||||
for _, reg := range inputRegs {
|
||||
|
||||
res, err := c.ReadHoldingRegisters(uint16(reg), 1)
|
||||
assert.NoError(t, err, "Failed to read reg %v", reg)
|
||||
|
||||
if err == nil {
|
||||
t.Logf("reg: %v res: %v \n", reg, res)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
func TestReadOneRegisterKeepAlive(t *testing.T) {
|
||||
c, err := New("IAM_248000012514.solver.nu:502", 1, 100*time.Millisecond, 5*time.Second)
|
||||
c, err := New(testIAMhost, 1, 100*time.Millisecond, 5*time.Second, 1)
|
||||
t.Log("Connect")
|
||||
assert.NoError(t, err)
|
||||
for n := 0; n < 5; n++ {
|
||||
res, err := c.ReadRegisters(12401, 2)
|
||||
res, err := c.ReadHoldingRegisters(12401, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 2)
|
||||
t.Log(res)
|
||||
|
||||
res, err = c.ReadRegisters(12102, 2)
|
||||
res, err = c.ReadHoldingRegisters(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)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
func TestReadOneRegisterShortKeepAlive(t *testing.T) {
|
||||
c, err := New("IAM_248000012514.solver.nu:502", 1, 10*time.Nanosecond, 5*time.Second)
|
||||
c, err := New(testIAMhost, 1, 10*time.Nanosecond, 5*time.Second, 1)
|
||||
t.Log("Connect")
|
||||
assert.NoError(t, err)
|
||||
for n := 0; n < 5; n++ {
|
||||
res, err := c.ReadRegisters(12401, 2)
|
||||
res, err := c.ReadHoldingRegisters(12401, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 2)
|
||||
t.Log(res)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
res, err = c.ReadRegisters(12102, 2)
|
||||
res, err = c.ReadHoldingRegisters(12102, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 2)
|
||||
t.Log(res)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
res, err = c.ReadRegisters(12544, 1)
|
||||
res, err = c.ReadHoldingRegisters(12544, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 1)
|
||||
t.Log(float32(res[0]) / 10)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
res, err = c.ReadRegisters(12136, 1)
|
||||
res, err = c.ReadHoldingRegisters(12136, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 1)
|
||||
t.Log(res)
|
||||
@@ -68,25 +101,25 @@ func TestReadOneRegisterShortKeepAlive(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadALot(t *testing.T) {
|
||||
c, err := New("IAM_248000012514.solver.nu:502", 1, 100*time.Millisecond, 5*time.Second)
|
||||
c, err := New(testIAMhost, 1, 100*time.Millisecond, 5*time.Second, 1)
|
||||
t.Log("Connect")
|
||||
assert.NoError(t, err)
|
||||
for n := 0; n < 500; n++ {
|
||||
t.Log(n)
|
||||
_, err := c.ReadRegisters(12401, 2)
|
||||
_, err := c.ReadHoldingRegisters(12401, 2)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
_, err = c.ReadRegisters(12102, 2)
|
||||
_, err = c.ReadHoldingRegisters(12102, 2)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
_, err = c.ReadRegisters(12544, 1)
|
||||
_, err = c.ReadHoldingRegisters(12544, 1)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
_, err = c.ReadRegisters(12136, 1)
|
||||
_, err = c.ReadHoldingRegisters(12136, 1)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user