mirror of
https://github.com/jpeletier/koolnova2mqtt.git
synced 2026-07-16 04:16:56 +00:00
fixed watcher tests
This commit is contained in:
+22
-11
@@ -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)
|
||||
}
|
||||
|
||||
+53
-31
@@ -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