evcc-io/util/modbus/functions.go

108 lines
2.2 KiB
Go

package modbus
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"slices"
"strconv"
"strings"
"time"
"github.com/cenkalti/backoff/v4"
)
func Backoff() *backoff.ExponentialBackOff {
return backoff.NewExponentialBackOff(backoff.WithInitialInterval(20*time.Millisecond), backoff.WithMaxElapsedTime(10*time.Second))
}
// DecodeMask converts a bit mask in decimal or hex format to uint64
func DecodeMask(mask string) (uint64, error) {
mask = strings.ToLower(mask)
if mask == "" {
return 0, errors.New("mask is required")
}
if strings.HasPrefix(mask, "0x") {
if len(mask) < 3 {
return 0, fmt.Errorf("invalid mask: %s", mask)
}
b, err := hex.DecodeString(mask[2:])
if err != nil {
return 0, fmt.Errorf("invalid mask: %w", err)
}
var u uint64
for _, v := range b {
u = u<<8 | uint64(v)
}
return u, nil
}
return strconv.ParseUint(mask, 10, 64)
}
// decodeBool8 converts a masked uint1 to a bool
func decodeBool8(b []byte) float64 {
u := b[0]
if u > 0 {
return 1
}
return 0
}
// decodeBool16 converts a masked uint16 to a bool
func decodeBool16(mask uint64) func(b []byte) float64 {
return func(b []byte) float64 {
u := binary.BigEndian.Uint16(b)
if mask > 0 {
u &= uint16(mask)
}
if u > 0 {
return 1
}
return 0
}
}
// uint64LswFirst decodes a uint64 stored in little-endian word order (LSW first)
func uint64LswFirst(b []byte) uint64 {
return uint64(binary.BigEndian.Uint16(b)) |
uint64(binary.BigEndian.Uint16(b[2:]))<<16 |
uint64(binary.BigEndian.Uint16(b[4:]))<<32 |
uint64(binary.BigEndian.Uint16(b[6:]))<<48
}
func decodeNaN16(f func(b []byte) float64, nan ...uint16) func(b []byte) float64 {
return func(b []byte) float64 {
u := binary.BigEndian.Uint16(b)
if slices.Contains(nan, u) {
return 0
}
return f(b)
}
}
func decodeNaN32(f func(b []byte) float64, nan ...uint32) func(b []byte) float64 {
return func(b []byte) float64 {
u := binary.BigEndian.Uint32(b)
if slices.Contains(nan, u) {
return 0
}
return f(b)
}
}
func decodeNaN64(f func(b []byte) float64, nan ...uint64) func(b []byte) float64 {
return func(b []byte) float64 {
u := binary.BigEndian.Uint64(b)
if slices.Contains(nan, u) {
return 0
}
return f(b)
}
}