initial commit

This commit is contained in:
Javier Peletier
2020-12-11 15:39:11 +01:00
commit 4662308fd0
7 changed files with 472 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
package watcher
import (
"bytes"
"errors"
)
type ReadRegister func(slaveID byte, address uint16, quantity uint16) (results []byte, err error)
type Config struct {
Address uint16
Quantity uint16
SlaveID byte
Read ReadRegister
RegisterSize int
}
type watcher struct {
Config
state []byte
callbacks map[uint16]func(address uint16)
}
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.")
func New(config *Config) *watcher {
return &watcher{
Config: *config,
callbacks: make(map[uint16]func(address uint16)),
}
}
func (w *watcher) RegisterCallback(address uint16, callback func(address uint16)) {
w.callbacks[address] = callback
}
func (w *watcher) Poll() error {
newState, err := w.Read(w.SlaveID, w.Address, w.Quantity)
if err != nil {
return err
}
if len(newState) != int(w.Quantity)*w.RegisterSize {
return ErrIncorrectRegisterSize
}
oldState := w.state
w.state = newState
first := len(oldState) != len(newState)
address := w.Address
for n := 0; n < len(newState); n += w.RegisterSize {
callback := w.callbacks[address]
if callback == nil {
address++
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 {
callback(address)
}
address++
}
return nil
}
func (w *watcher) ReadRegister(address uint16) (value []byte, err error) {
if address < w.Address || address > w.Address+uint16(w.Quantity) {
return nil, ErrAddressOutOfRange
}
if w.state == nil {
return nil, ErrUninitialized
}
registerOffset := int(address-w.Address) * w.RegisterSize
return w.state[registerOffset : registerOffset+w.RegisterSize], nil
}
func (w *watcher) TriggerCallbacks() {
for address, callback := range w.callbacks {
callback(address)
}
}
+87
View File
@@ -0,0 +1,87 @@
package watcher_test
import (
"errors"
"koolnova2mqtt/watcher"
"testing"
"github.com/epiclabs-io/ut"
)
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
}
w := watcher.New(&watcher.Config{
Address: 1000,
Quantity: 5,
RegisterSize: 2,
SlaveID: 1,
Read: readRegister,
})
var cbAddress uint16
var callbackCount int
w.RegisterCallback(1000, func(address uint16) {
cbAddress = address
callbackCount++
})
w.RegisterCallback(1004, 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)
err = w.Poll()
t.Ok(err)
t.Equals(2, callbackCount)
value, err = w.ReadRegister(1001)
t.Ok(err)
t.Equals([]byte{3, 4}, value)
_, err = w.ReadRegister(200)
t.MustFailWith(err, watcher.ErrAddressOutOfRange)
_, err = w.ReadRegister(5000)
t.MustFailWith(err, watcher.ErrAddressOutOfRange)
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.Poll()
t.Ok(err)
t.Equals(1, callbackCount)
t.Equals(uint16(1000), cbAddress)
cbNewValue, err := w.ReadRegister(cbAddress)
t.Ok(err)
t.Equals([]byte{79, 82}, cbNewValue)
r = []byte{1, 2}
err = w.Poll()
t.MustFailWith(err, watcher.ErrIncorrectRegisterSize)
readRegisterError = errors.New("error")
err = w.Poll()
t.MustFail(err, "expected Poll() to fail if readRegister returns error")
}