jl-iot/modbus/client.go

195 lines
4.6 KiB
Go
Raw Normal View History

package modbus
import (
"context"
"fmt"
"log/slog"
"net"
"os"
"time"
"github.com/simonvetter/modbus"
)
type ConnectState int
const ()
type client struct {
url string
cli *modbus.ModbusClient
started bool // 是否启动
connected bool // 是否连接
cancel context.CancelFunc
}
func newClient(url string) (MasterClient, error) {
cli, err := modbus.NewClient(&modbus.ClientConfiguration{
URL: url,
Timeout: 1 * time.Second,
})
if err != nil {
return nil, err
}
c := &client{
url: url,
cli: cli,
connected: false,
}
ctx, cancel := context.WithCancel(context.Background())
go c.connManage(ctx)
c.cancel = cancel
return c, err
}
func (c *client) Start() error {
c.started = true
return nil
}
func (c *client) Stop() error {
c.started = false
return nil
}
// 客户端连接管理
func (c *client) connManage(ctx context.Context) {
for {
select {
case <-ctx.Done():
slog.Info("modbus客户端连接管理线程退出", "url", c.url)
return
default:
}
if c.started && !c.connected { // 已经启动, 尝试重连
err := c.cli.Open()
if err != nil {
slog.Error("modbus客户端尝试连接失败", "url", c.url, "err", err)
} else {
c.connected = true
slog.Info("modbus客户端连接成功", "url", c.url)
}
} else if !c.started && c.connected { // 未启动,尝试关闭
err := c.cli.Close()
if err != nil {
slog.Error("modbus客户端关闭错误", "url", c.url, "err", err)
}
c.connected = false
slog.Info("modbus客户端关闭", "url", c.url)
}
time.Sleep(time.Second)
}
}
func (c *client) IsConnected() bool {
return c.connected
}
// Close implements MasterClient.
func (c *client) Close() error {
c.cancel()
if c.connected {
return c.cli.Close()
}
return nil
}
// 读请求执行
func readExecute[T any](c *client, req func() (T, error)) (T, error) {
if !c.started {
return *new(T), fmt.Errorf("modbus客户端未启动")
}
if !c.connected {
return *new(T), fmt.Errorf("modbus客户端未连接或连接断开")
}
res, err := req()
if err != nil {
if newErr, ok := err.(*net.OpError); ok {
if se, ok := newErr.Err.(*os.SyscallError); ok {
c.connected = false
return res, se
}
}
}
return res, err
}
// 写请求执行
func writeExecute(c *client, req func() error) error {
if !c.started {
return fmt.Errorf("modbus客户端未启动")
}
if !c.connected {
return fmt.Errorf("modbus客户端未连接或连接断开")
}
return req()
}
// ReadCoil implements MasterClient.
func (c *client) ReadCoil(addr uint16, quantity uint16) ([]bool, error) {
return readExecute[[]bool](c, func() ([]bool, error) {
return c.cli.ReadCoils(addr, quantity)
})
}
func (c *client) ReadCoilBit(addr uint16) (bool, error) {
return readExecute[bool](c, func() (bool, error) {
return c.cli.ReadCoil(addr)
})
}
// ReadDiscreteInput implements MasterClient.
func (c *client) ReadDiscreteInput(addr uint16, quantity uint16) ([]bool, error) {
return readExecute[[]bool](c, func() ([]bool, error) {
return c.cli.ReadDiscreteInputs(addr, quantity)
})
}
// ReadHoldingRegister implements MasterClient.
func (c *client) ReadHoldingRegister(addr uint16, quantity uint16) ([]uint16, error) {
return readExecute[[]uint16](c, func() ([]uint16, error) {
return c.cli.ReadRegisters(addr, quantity, modbus.HOLDING_REGISTER)
})
}
// ReadHoldingRegisterUint16 implements MasterClient.
func (c *client) ReadHoldingRegisterUint16(addr uint16) (uint16, error) {
return readExecute[uint16](c, func() (uint16, error) {
return c.cli.ReadRegister(addr, modbus.HOLDING_REGISTER)
})
}
// ReadInputRegister implements MasterClient.
func (c *client) ReadInputRegister(addr uint16, quantity uint16) ([]uint16, error) {
return readExecute[[]uint16](c, func() ([]uint16, error) {
return c.cli.ReadRegisters(addr, quantity, modbus.INPUT_REGISTER)
})
}
// WriteCoil implements MasterClient.
func (c *client) WriteCoil(addr uint16, value bool) error {
return writeExecute(c, func() error {
return c.cli.WriteCoil(addr, value)
})
}
// WriteCoils implements MasterClient.
func (c *client) WriteCoils(addr uint16, values []bool) error {
return writeExecute(c, func() error {
return c.cli.WriteCoils(addr, values)
})
}
// WriteRegister implements MasterClient.
func (c *client) WriteRegister(addr uint16, value uint16) error {
return writeExecute(c, func() error {
return c.cli.WriteRegister(addr, value)
})
}
// WriteRegisters implements MasterClient.
func (c *client) WriteRegisters(addr uint16, values []uint16) error {
return writeExecute(c, func() error {
return c.cli.WriteRegisters(addr, values)
})
}