diff --git a/kn/bridge.go b/kn/bridge.go index 9b1a23e..ced8112 100644 --- a/kn/bridge.go +++ b/kn/bridge.go @@ -57,19 +57,17 @@ func NewBridge(config *Config) *Bridge { func (b *Bridge) Start() error { zw := watcher.New(&watcher.Config{ - Address: FIRST_ZONE_REGISTER, - Quantity: TOTAL_ZONE_REGISTERS, - RegisterSize: 2, - SlaveID: b.SlaveID, - Modbus: b.Modbus, + Address: FIRST_ZONE_REGISTER, + Quantity: TOTAL_ZONE_REGISTERS, + SlaveID: b.SlaveID, + Modbus: b.Modbus, }) sysw := watcher.New(&watcher.Config{ - Address: FIRST_SYS_REGISTER, - Quantity: TOTAL_SYS_REGISTERS, - RegisterSize: 2, - SlaveID: b.SlaveID, - Modbus: b.Modbus, + Address: FIRST_SYS_REGISTER, + Quantity: TOTAL_SYS_REGISTERS, + SlaveID: b.SlaveID, + Modbus: b.Modbus, }) b.zw = zw b.sysw = sysw diff --git a/kn/sys.go b/kn/sys.go index e246317..9519ca1 100644 --- a/kn/sys.go +++ b/kn/sys.go @@ -65,7 +65,7 @@ func NewSys(config *SysConfig) *Sys { func (s *Sys) ReadRegister(n int) int { r := s.Watcher.ReadRegister(uint16(n)) - return int(r[1]) + return int(r) } func (s *Sys) WriteRegister(n int, value uint16) error { diff --git a/kn/zone.go b/kn/zone.go index 54825fa..39bfaee 100644 --- a/kn/zone.go +++ b/kn/zone.go @@ -1,14 +1,13 @@ package kn import ( - "encoding/binary" "math" average "github.com/RobinUS2/golang-moving-average" ) type Watcher interface { - ReadRegister(address uint16) (value []byte) + ReadRegister(address uint16) (value uint16) WriteRegister(address uint16, value uint16) error RegisterCallback(address uint16, callback func(address uint16)) } @@ -67,8 +66,7 @@ func (z *Zone) RegisterCallback(num int, f func()) { } func (z *Zone) ReadRegister(num int) uint16 { - b := z.Watcher.ReadRegister(uint16((z.ZoneNumber-1)*REG_PER_ZONE + num)) - return binary.BigEndian.Uint16(b) + return z.Watcher.ReadRegister(uint16((z.ZoneNumber-1)*REG_PER_ZONE + num)) } func (z *Zone) WriteRegister(num int, value uint16) error { diff --git a/modbus/mock.go b/modbus/mock.go index 21c38c5..991f48c 100644 --- a/modbus/mock.go +++ b/modbus/mock.go @@ -1,7 +1,6 @@ package modbus import ( - "encoding/binary" "errors" ) @@ -18,28 +17,24 @@ func NewMock() Modbus { } } -func (ms *Mock) ReadRegister(slaveID byte, address uint16, quantity uint16) (results []byte, err error) { +func (ms *Mock) ReadRegister(slaveID byte, address uint16, quantity uint16) (results []uint16, err error) { state, ok := ms.State[slaveID] if !ok { return nil, errors.New("Unknown slave") } address-- for a := address; a < address+quantity; a++ { - b := make([]byte, 2) - binary.BigEndian.PutUint16(b, state[a]) - results = append(results, b...) + results = append(results, state[a]) } return results, nil } -func (ms *Mock) WriteRegister(slaveID byte, address uint16, value uint16) (results []byte, err error) { +func (ms *Mock) WriteRegister(slaveID byte, address uint16, value uint16) (results []uint16, err error) { state, ok := ms.State[slaveID] if !ok { return nil, errors.New("Unknown slave") } state[address-1] = value - results = make([]byte, 2) - binary.BigEndian.PutUint16(results, value) - return results, nil + return []uint16{value}, nil } func (ms *Mock) Close() error { return nil } diff --git a/modbus/modbus.go b/modbus/modbus.go index 2898501..0add1c3 100644 --- a/modbus/modbus.go +++ b/modbus/modbus.go @@ -1,6 +1,8 @@ package modbus import ( + "encoding/binary" + "errors" "log" "sync" "time" @@ -9,8 +11,8 @@ import ( ) type Modbus interface { - ReadRegister(slaveID byte, address uint16, quantity uint16) (results []byte, err error) - WriteRegister(slaveID byte, address uint16, value uint16) (results []byte, err error) + ReadRegister(slaveID byte, address uint16, quantity uint16) (results []uint16, err error) + WriteRegister(slaveID byte, address uint16, value uint16) (results []uint16, err error) Close() error } @@ -33,6 +35,8 @@ func throttle(ms int) { time.Sleep(time.Duration(ms) * time.Millisecond) } +var ErrIncorrectResultSize = errors.New("Incorrect number of results returned") + func New(config *Config) (Modbus, error) { handler := gmodbus.NewRTUClientHandler(config.Port) handler.BaudRate = config.BaudRate @@ -51,17 +55,36 @@ func (mb *modbus) Close() error { return mb.handler.Close() } -func (mb *modbus) ReadRegister(slaveID byte, address uint16, quantity uint16) (results []byte, err error) { +func parseResults(r []byte, quantity uint16) ([]uint16, error) { + if len(r) != int(quantity*2) { + return nil, ErrIncorrectResultSize + } + results := make([]uint16, quantity) + for n := uint16(0); n < quantity; n++ { + results[n] = binary.BigEndian.Uint16(r[n*2 : n*2+2]) + } + return results, nil +} + +func (mb *modbus) ReadRegister(slaveID byte, address uint16, quantity uint16) (results []uint16, err error) { err = mb.try(slaveID, func() (err error) { - results, err = mb.client.ReadHoldingRegisters(address-1, quantity) + r, err := mb.client.ReadHoldingRegisters(address-1, quantity) + if err != nil { + return err + } + results, err = parseResults(r, quantity) return err }) return results, err } -func (mb *modbus) WriteRegister(slaveID byte, address uint16, value uint16) (results []byte, err error) { +func (mb *modbus) WriteRegister(slaveID byte, address uint16, value uint16) (results []uint16, err error) { err = mb.try(slaveID, func() (err error) { - results, err = mb.client.WriteSingleRegister(address-1, value) + r, err := mb.client.WriteSingleRegister(address-1, value) + if err != nil { + return err + } + results, err = parseResults(r, 1) return err }) return results, err diff --git a/watcher/watcher.go b/watcher/watcher.go index 200b7b0..5e55e0f 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -3,7 +3,6 @@ package watcher import ( - "bytes" "errors" "koolnova2mqtt/modbus" "sync" @@ -11,22 +10,20 @@ import ( // Config contains the configuration parameters for a new Watcher instance type Config struct { - Address uint16 // Start address - Quantity uint16 // Number of registers to watch - SlaveID byte // SlaveID to watch - Modbus modbus.Modbus // Modbus interface - RegisterSize int // size of each register + Address uint16 // Start address + Quantity uint16 // Number of registers to watch + SlaveID byte // SlaveID to watch + Modbus modbus.Modbus // Modbus interface } // Watcher represents a cache of modbus registers in a device type Watcher struct { Config - state []byte // current view of the modbus register states + state []uint16 // current view of the modbus register states callbacks map[uint16]func(address uint16) // set of callbacks lock *sync.RWMutex } -var ErrIncorrectRegisterSize = errors.New("Incorrect register size") var ErrAddressOutOfRange = errors.New("Register address out of range") var ErrUninitialized = errors.New("State uninitialized. Call Poll() first.") var ErrCannotIncreaseRange = errors.New("Cannot increase range") @@ -54,30 +51,18 @@ func (w *Watcher) Poll() error { return err } - if len(newState) != int(w.Quantity)*w.RegisterSize { - w.lock.Unlock() - return ErrIncorrectRegisterSize - } - oldState := w.state w.state = newState var callbackAddresses []uint16 first := len(oldState) != len(newState) - for n := 0; n < len(newState); n += w.RegisterSize { - address := uint16(n/w.RegisterSize) + w.Address + for n := 0; n < len(newState); n++ { + address := uint16(n) + w.Address callback := w.callbacks[address] if callback == nil { continue } - var oldValue []byte - newValue := newState[n : n+w.RegisterSize] - if first { - oldValue = nil - } else { - oldValue = oldState[n : n+w.RegisterSize] - } - if bytes.Compare(oldValue, newValue) != 0 { + if first || oldState[n] != newState[n] { callbackAddresses = append(callbackAddresses, address) } } @@ -90,7 +75,7 @@ func (w *Watcher) Poll() error { } // ReadRegister reads one register from the cache -func (w *Watcher) ReadRegister(address uint16) (value []byte) { +func (w *Watcher) ReadRegister(address uint16) (value uint16) { w.lock.Lock() defer w.lock.Unlock() if address < w.Address || address > w.Address+uint16(w.Quantity) { @@ -99,8 +84,7 @@ func (w *Watcher) ReadRegister(address uint16) (value []byte) { if w.state == nil { panic(ErrUninitialized) } - registerOffset := int(address-w.Address) * w.RegisterSize - return w.state[registerOffset : registerOffset+w.RegisterSize] + return w.state[int(address-w.Address)] } @@ -112,8 +96,7 @@ func (w *Watcher) WriteRegister(address uint16, value uint16) error { w.lock.Unlock() return err } - registerOffset := int(address-w.Address) * w.RegisterSize - copy(w.state[registerOffset:registerOffset+w.RegisterSize], results) + w.state[int(address-w.Address)] = results[0] callback := w.callbacks[address] w.lock.Unlock() if callback != nil { @@ -134,7 +117,7 @@ func (w *Watcher) Resize(newQuantity int) { w.lock.Lock() defer w.lock.Unlock() if newQuantity <= int(w.Quantity) { - w.state = w.state[:newQuantity*w.RegisterSize] + w.state = w.state[:newQuantity] for address := range w.callbacks { if address > w.Address+uint16(newQuantity)-1 { delete(w.callbacks, address) diff --git a/watcher/watcher_test.go b/watcher/watcher_test.go index 8193cb8..d2b0446 100644 --- a/watcher/watcher_test.go +++ b/watcher/watcher_test.go @@ -14,11 +14,11 @@ var modbusError error type BuggyModbus struct { } -func (ms *BuggyModbus) ReadRegister(slaveID byte, address uint16, quantity uint16) (results []byte, err error) { - return []byte{1, 2}, modbusError +func (ms *BuggyModbus) ReadRegister(slaveID byte, address uint16, quantity uint16) (results []uint16, err error) { + return []uint16{0x0102}, modbusError } -func (ms *BuggyModbus) WriteRegister(slaveID byte, address uint16, value uint16) (results []byte, err error) { +func (ms *BuggyModbus) WriteRegister(slaveID byte, address uint16, value uint16) (results []uint16, err error) { return nil, modbusError } @@ -30,11 +30,10 @@ func TestWatcher(tx *testing.T) { var err error w := watcher.New(&watcher.Config{ - Address: 1, - Quantity: 5, - RegisterSize: 2, - SlaveID: 49, - Modbus: modbus.NewMock(), + Address: 1, + Quantity: 5, + SlaveID: 49, + Modbus: modbus.NewMock(), }) var cbAddress uint16 @@ -58,7 +57,7 @@ func TestWatcher(tx *testing.T) { value := w.ReadRegister(1) t.Ok(err) - t.Equals([]byte{0, 3}, value) + t.Equals(uint16(0x0003), value) t.MustPanicWith(watcher.ErrAddressOutOfRange, func() { w.ReadRegister(200) @@ -80,7 +79,7 @@ func TestWatcher(tx *testing.T) { t.Equals(uint16(3), cbAddress) cbNewValue := w.ReadRegister(3) - t.Equals([]byte{0x12, 0x34}, cbNewValue) + t.Equals(uint16(0x1234), cbNewValue) callbackCount = 0 err = w.Poll() @@ -91,16 +90,12 @@ func TestWatcher(tx *testing.T) { t.Equals(2, callbackCount) w = watcher.New(&watcher.Config{ - Address: 1, - Quantity: 5, - RegisterSize: 2, - SlaveID: 1, - Modbus: &BuggyModbus{}, + Address: 1, + Quantity: 5, + SlaveID: 1, + Modbus: &BuggyModbus{}, }) - err = w.Poll() - t.MustFailWith(err, watcher.ErrIncorrectRegisterSize) - modbusError = errors.New("error") err = w.Poll()