mirror of
https://github.com/jpeletier/koolnova2mqtt.git
synced 2026-07-16 04:16:56 +00:00
refactored to uint16
fixed panic
This commit is contained in:
+8
-10
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-4
@@ -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 {
|
||||
|
||||
+4
-9
@@ -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 }
|
||||
|
||||
+29
-6
@@ -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
|
||||
|
||||
+12
-29
@@ -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)
|
||||
|
||||
+13
-18
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user