mirror of
https://github.com/jpeletier/koolnova2mqtt.git
synced 2026-01-11 15:11:43 +00:00
fixed watcher tests
This commit is contained in:
4
go.mod
4
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
|
||||
)
|
||||
|
||||
14
go.sum
14
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=
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user