mirror of
https://github.com/jpeletier/koolnova2mqtt.git
synced 2026-01-12 03:24:23 +00:00
112 lines
2.4 KiB
Go
112 lines
2.4 KiB
Go
package modbus
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
gmodbus "github.com/wz2b/modbus"
|
|
)
|
|
|
|
type Config struct {
|
|
Port string
|
|
BaudRate int
|
|
DataBits int
|
|
Parity string
|
|
StopBits int
|
|
Timeout time.Duration
|
|
}
|
|
|
|
type Modbus struct {
|
|
handler *gmodbus.RTUClientHandler
|
|
client gmodbus.Client
|
|
lock sync.RWMutex
|
|
}
|
|
|
|
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
|
|
handler.DataBits = config.DataBits
|
|
handler.Parity = config.Parity
|
|
handler.StopBits = config.StopBits
|
|
handler.Timeout = config.Timeout
|
|
|
|
return &Modbus{
|
|
handler: handler,
|
|
client: gmodbus.NewClient(handler),
|
|
}, handler.Connect()
|
|
}
|
|
|
|
func (mb *Modbus) Close() error {
|
|
return mb.handler.Close()
|
|
}
|
|
|
|
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) {
|
|
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 []uint16, err error) {
|
|
err = mb.try(slaveID, func() (err error) {
|
|
r, err := mb.client.WriteSingleRegister(address-1, value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
results, err = parseResults(r, 1)
|
|
return err
|
|
})
|
|
return results, err
|
|
}
|
|
|
|
func (mb *Modbus) try(slaveID byte, f func() error) (err error) {
|
|
mb.lock.Lock()
|
|
defer mb.lock.Unlock()
|
|
defer throttle(100)
|
|
mb.handler.SlaveId = slaveID
|
|
retries := 5
|
|
delay := 100
|
|
for retries > 0 {
|
|
err = f()
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
log.Printf("Retried modbus operation due to %s. %d retries left\n", err, retries)
|
|
mb.handler.Close()
|
|
throttle(100)
|
|
connectErr := mb.handler.Connect()
|
|
if connectErr != nil {
|
|
return connectErr
|
|
}
|
|
retries--
|
|
throttle(delay)
|
|
delay *= 2
|
|
}
|
|
return err
|
|
}
|