From 96b3f00f291e074666d87a1a4cb4964f3d27ffd9 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Mon, 21 Dec 2020 11:22:31 +0100 Subject: [PATCH] fixed watcher tests --- go.mod | 4 +- go.sum | 14 +------ watcher/watcher.go | 33 ++++++++++------ watcher/watcher_test.go | 84 ++++++++++++++++++++++++++--------------- 4 files changed, 78 insertions(+), 57 deletions(-) diff --git a/go.mod b/go.mod index 3ca9bb5..ef91005 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,7 @@ require ( github.com/RobinUS2/golang-moving-average v1.0.0 github.com/eclipse/paho.mqtt.golang v1.3.0 github.com/epiclabs-io/diff3 v0.0.0-20181217103619-05282cece609 // indirect - github.com/epiclabs-io/ut v0.0.0-20190416122157-8da7fe4b4947 - github.com/goburrow/modbus v0.1.0 + github.com/epiclabs-io/ut v0.0.0-20201221095005-a2a4f565f0d0 github.com/goburrow/serial v0.1.0 // indirect - github.com/stretchr/testify v1.6.1 github.com/wz2b/modbus v0.1.1 ) diff --git a/go.sum b/go.sum index cf84fb4..1d056d0 100644 --- a/go.sum +++ b/go.sum @@ -1,24 +1,17 @@ github.com/RobinUS2/golang-moving-average v1.0.0 h1:PD7DDZNt+UFb9XlsBbTIu/DtXqqaD/MD86DYnk3mwvA= github.com/RobinUS2/golang-moving-average v1.0.0/go.mod h1:MdzhY+KoEvi+OBygTPH0OSaKrOJzvILWN2SPQzaKVsY= -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/eclipse/paho.mqtt.golang v1.3.0 h1:MU79lqr3FKNKbSrGN7d7bNYqh8MwWW7Zcx0iG+VIw9I= github.com/eclipse/paho.mqtt.golang v1.3.0/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc= github.com/epiclabs-io/diff3 v0.0.0-20181217103619-05282cece609 h1:KHcpmcC/8cnCDXDm6SaCTajWF/vyUbBE1ovA27xYYEY= github.com/epiclabs-io/diff3 v0.0.0-20181217103619-05282cece609/go.mod h1:tM499ZoH5jQRF3wlMnl59SJQwVYXIBdJRZa/K71p0IM= github.com/epiclabs-io/ut v0.0.0-20190416122157-8da7fe4b4947 h1:5jyZq+mwwE90FnIyzAorlWF0Nrg8AB48KsDxofSAyBw= github.com/epiclabs-io/ut v0.0.0-20190416122157-8da7fe4b4947/go.mod h1:Sm6PW7b/nLOHEn3XxuUOXFYA4xFkLUnyAWUOcTGcRZ4= -github.com/goburrow/modbus v0.1.0 h1:DejRZY73nEM6+bt5JSP6IsFolJ9dVcqxsYbpLbeW/ro= -github.com/goburrow/modbus v0.1.0/go.mod h1:Kx552D5rLIS8E7TyUwQ/UdHEqvX5T8tyiGBTlzMcZBg= +github.com/epiclabs-io/ut v0.0.0-20201221095005-a2a4f565f0d0 h1:mM/cqhrPWaJ3xUF4Sp4H+Zfg4kQ4/lwb1AYTg3Ig9QQ= +github.com/epiclabs-io/ut v0.0.0-20201221095005-a2a4f565f0d0/go.mod h1:Sm6PW7b/nLOHEn3XxuUOXFYA4xFkLUnyAWUOcTGcRZ4= github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA= github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -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= github.com/wz2b/modbus v0.1.1 h1:GB9KEI8r9TDKJLFoEg59nj9FcR5Q6Glg+7EoDG8OFaE= github.com/wz2b/modbus v0.1.1/go.mod h1:p74iuj8ZYGmZpurWLgFN/dw5MAOlQDTCM8veQhH+7GU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -27,6 +20,3 @@ golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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= diff --git a/watcher/watcher.go b/watcher/watcher.go index 4e04812..200b7b0 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -1,3 +1,5 @@ +// Package watcher represents a cache of a range of modbus registers in a device +// Can fire events if a watched register changes package watcher import ( @@ -7,25 +9,29 @@ import ( "sync" ) +// Config contains the configuration parameters for a new Watcher instance type Config struct { - Address uint16 - Quantity uint16 - SlaveID byte - Modbus modbus.Modbus - RegisterSize int + 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 } +// Watcher represents a cache of modbus registers in a device type Watcher struct { Config - state []byte - callbacks map[uint16]func(address uint16) + state []byte // 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") +// New returns a new Watcher instance func New(config *Config) *Watcher { return &Watcher{ Config: *config, @@ -34,10 +40,12 @@ func New(config *Config) *Watcher { } } +// RegisterCallback registers a new callback that will be fired when the specific register address changes values func (w *Watcher) RegisterCallback(address uint16, callback func(address uint16)) { w.callbacks[address] = callback } +// Poll refreshes the cache by reading the watched register range from the slave device func (w *Watcher) Poll() error { w.lock.Lock() newState, err := w.Modbus.ReadRegister(w.SlaveID, w.Address, w.Quantity) @@ -81,6 +89,7 @@ func (w *Watcher) Poll() error { return nil } +// ReadRegister reads one register from the cache func (w *Watcher) ReadRegister(address uint16) (value []byte) { w.lock.Lock() defer w.lock.Unlock() @@ -95,6 +104,7 @@ func (w *Watcher) ReadRegister(address uint16) (value []byte) { } +// WriteRegister writes the value to the slave device and updates the cache if successful func (w *Watcher) WriteRegister(address uint16, value uint16) error { w.lock.Lock() results, err := w.Modbus.WriteRegister(w.SlaveID, address, value) @@ -112,25 +122,26 @@ func (w *Watcher) WriteRegister(address uint16, value uint16) error { return nil } +// TriggerCallbacks calls all callbacks func (w *Watcher) TriggerCallbacks() { for address, callback := range w.callbacks { callback(address) } } +// Resize reduces the watched range func (w *Watcher) Resize(newQuantity int) { w.lock.Lock() defer w.lock.Unlock() - if newQuantity < int(w.Quantity) { + if newQuantity <= int(w.Quantity) { w.state = w.state[:newQuantity*w.RegisterSize] - for address, _ := range w.callbacks { + for address := range w.callbacks { if address > w.Address+uint16(newQuantity)-1 { delete(w.callbacks, address) } } } else { - w.state = nil + panic(ErrCannotIncreaseRange) } - w.Quantity = uint16(newQuantity) } diff --git a/watcher/watcher_test.go b/watcher/watcher_test.go index a2ab4ee..8193cb8 100644 --- a/watcher/watcher_test.go +++ b/watcher/watcher_test.go @@ -2,84 +2,106 @@ package watcher_test import ( "errors" + "koolnova2mqtt/modbus" "koolnova2mqtt/watcher" "testing" "github.com/epiclabs-io/ut" ) +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) WriteRegister(slaveID byte, address uint16, value uint16) (results []byte, err error) { + return nil, modbusError +} + +func (ms *BuggyModbus) Close() error { return nil } + func TestWatcher(tx *testing.T) { t := ut.BeginTest(tx, false) defer t.FinishTest() - - var r []byte - var readRegisterError error = nil - readRegister := func(slaveID byte, address uint16, quantity uint16) (results []byte, err error) { - return r, readRegisterError - } + var err error w := watcher.New(&watcher.Config{ - Address: 1000, + Address: 1, Quantity: 5, RegisterSize: 2, - SlaveID: 1, - Read: readRegister, + SlaveID: 49, + Modbus: modbus.NewMock(), }) var cbAddress uint16 var callbackCount int - w.RegisterCallback(1000, func(address uint16) { + w.RegisterCallback(3, func(address uint16) { cbAddress = address callbackCount++ }) - w.RegisterCallback(1004, func(address uint16) { + w.RegisterCallback(4, func(address uint16) { callbackCount++ }) - r = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - - value, err := w.ReadRegister(1001) - t.MustFailWith(err, watcher.ErrUninitialized) - t.Equals([]byte(nil), value) + t.MustPanicWith(watcher.ErrUninitialized, func() { + w.ReadRegister(1) + }) err = w.Poll() t.Ok(err) t.Equals(2, callbackCount) - value, err = w.ReadRegister(1001) + value := w.ReadRegister(1) t.Ok(err) - t.Equals([]byte{3, 4}, value) + t.Equals([]byte{0, 3}, value) - _, err = w.ReadRegister(200) - t.MustFailWith(err, watcher.ErrAddressOutOfRange) + t.MustPanicWith(watcher.ErrAddressOutOfRange, func() { + w.ReadRegister(200) + }) - _, err = w.ReadRegister(5000) - t.MustFailWith(err, watcher.ErrAddressOutOfRange) + t.MustPanicWith(watcher.ErrAddressOutOfRange, func() { + w.ReadRegister(5000) + }) callbackCount = 0 err = w.Poll() t.Ok(err) - t.Equals(callbackCount, 0) - r = []byte{79, 82, 3, 4, 5, 6, 7, 8, 9, 10} + callbackCount = 0 + err = w.WriteRegister(3, 0x1234) + t.Ok(err) + t.Equals(1, callbackCount) + t.Equals(uint16(3), cbAddress) + + cbNewValue := w.ReadRegister(3) + t.Equals([]byte{0x12, 0x34}, cbNewValue) + callbackCount = 0 err = w.Poll() t.Ok(err) + t.Equals(0, callbackCount) - t.Equals(1, callbackCount) - t.Equals(uint16(1000), cbAddress) + w.TriggerCallbacks() + t.Equals(2, callbackCount) - cbNewValue, err := w.ReadRegister(cbAddress) - t.Ok(err) - t.Equals([]byte{79, 82}, cbNewValue) + w = watcher.New(&watcher.Config{ + Address: 1, + Quantity: 5, + RegisterSize: 2, + SlaveID: 1, + Modbus: &BuggyModbus{}, + }) - r = []byte{1, 2} err = w.Poll() t.MustFailWith(err, watcher.ErrIncorrectRegisterSize) - readRegisterError = errors.New("error") + modbusError = errors.New("error") err = w.Poll() t.MustFail(err, "expected Poll() to fail if readRegister returns error")