| | |
| | | ParityOdd Parity = 2 //偶校验 |
| | | ParityNull Parity = 3 //无校验 |
| | | ) |
| | | |
| | | func (p Parity) String() string { |
| | | switch p { |
| | | case ParityEven: |
| | | return "E" |
| | | case ParityOdd: |
| | | return "O" |
| | | case ParityNull: |
| | | return "N" |
| | | } |
| | | return "" |
| | | } |
| | |
| | | "time" |
| | | ) |
| | | |
| | | var driverManager plc4go.PlcDriverManager |
| | | |
| | | func init() { |
| | | // 创建驱动管理器 |
| | | driverManager = plc4go.NewPlcDriverManager() |
| | | // 注册TCP传输 |
| | | transports.RegisterTcpTransport(driverManager) |
| | | // 注册串口传输 |
| | | transports.RegisterSerialTransport(driverManager) |
| | | // 注册驱动 |
| | | drivers.RegisterModbusTcpDriver(driverManager) |
| | | drivers.RegisterModbusRtuDriver(driverManager) |
| | | } |
| | | |
| | | func GetModbusConnection(ipAddr string) (plc4go.PlcConnection, error) { |
| | | // 创建一个上下文,并设置 3 秒超时 |
| | | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) |
| | | defer cancel() |
| | | conn, err := newModbusConnection(ctx, ipAddr) |
| | | conn, err := newModbusTCPConnection(ctx, ipAddr) |
| | | if err != nil { |
| | | logx.Errorf("new modbus connection err: %v", err.Error()) |
| | | return nil, err |
| | | } |
| | | return conn, nil |
| | | } |
| | | |
| | | func newModbusConnection(ctx context.Context, ipAddr string) (plc4go.PlcConnection, error) { |
| | | // 创建驱动管理器 |
| | | driverManager := plc4go.NewPlcDriverManager() |
| | | // 注册TCP传输 |
| | | transports.RegisterTcpTransport(driverManager) |
| | | // 注册驱动 |
| | | //drivers.RegisterKnxDriver(driverManager) |
| | | drivers.RegisterModbusTcpDriver(driverManager) |
| | | |
| | | func newModbusTCPConnection(ctx context.Context, ipAddr string) (plc4go.PlcConnection, error) { |
| | | // 通过TCP连接PLC设备 |
| | | connectionString := fmt.Sprintf("modbus-tcp://%s", ipAddr) |
| | | connectionRequestChanel := driverManager.GetConnection(connectionString) |
| | | |
| | | // 等待连接响应,同时考虑上下文的超时 |
| | | select { |
| | | case connectionResult := <-connectionRequestChanel: |
| | |
| | | func newModbusRTUConnection(c *common.RTUConfig) (plc4go.PlcConnection, error) { |
| | | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) |
| | | // 创建一个新的 PLC 连接 |
| | | driverManager := plc4go.NewPlcDriverManager() |
| | | connectionString := fmt.Sprintf("modbus-rtu:serial://%s?baudrate=%d&databits=%d&stopbits=%d&parity=%s", c.SerialName, c.BaudRate, c.DataBit, c.StopBit, c.Parity) |
| | | connectionRequestChanel := driverManager.GetConnection(connectionString) |
| | | // 等待连接响应,同时考虑上下文的超时 |
| | |
| | | |
| | | import ( |
| | | "apsClient/conf" |
| | | "apsClient/model/common" |
| | | "apsClient/pkg/logx" |
| | | "github.com/goburrow/modbus" |
| | | "sync" |
| | | "time" |
| | |
| | | if conn, ok := connectionManager.GetConnection(ipAddr); ok { |
| | | return conn |
| | | } |
| | | conn := newGetModbusConnection(ipAddr) |
| | | conn := newModbusConnection(ipAddr) |
| | | connectionManager.AddConnection(ipAddr, conn) |
| | | return conn |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | func newGetModbusConnection(ipAddr string) modbus.Client { |
| | | func newModbusConnection(ipAddr string) modbus.Client { |
| | | handler = modbus.NewTCPClientHandler(ipAddr) |
| | | handler.Timeout = 10 * time.Second |
| | | handler.SlaveId = byte(conf.Conf.PLC.SlaveId) |
| | | return modbus.NewClient(handler) |
| | | } |
| | | |
| | | var rtuHandler *modbus.RTUClientHandler |
| | | |
| | | func getModbusRTUConnection(c *common.RTUConfig) modbus.Client { |
| | | if conn, ok := connectionManager.GetConnection(c.SerialName); ok { |
| | | return conn |
| | | } |
| | | conn := newModbusRTUConnection(c) |
| | | connectionManager.AddConnection(c.SerialName, conn) |
| | | return conn |
| | | } |
| | | |
| | | func unsetModbusRTUConnection(serialName string) { |
| | | _, ok := connectionManager.GetConnection(serialName) |
| | | if !ok { |
| | | return |
| | | } |
| | | connectionManager.RemoveConnection(serialName) |
| | | if handler != nil { |
| | | handler.Close() |
| | | } |
| | | } |
| | | |
| | | func newModbusRTUConnection(c *common.RTUConfig) modbus.Client { |
| | | // Modbus RTU/ASCII |
| | | rtuHandler = modbus.NewRTUClientHandler(c.SerialName) |
| | | rtuHandler.BaudRate = c.BaudRate |
| | | rtuHandler.DataBits = c.DataBit |
| | | rtuHandler.Parity = c.Parity.String() |
| | | rtuHandler.StopBits = c.StopBit |
| | | rtuHandler.SlaveId = 1 |
| | | rtuHandler.Timeout = 5 * time.Second |
| | | |
| | | err := rtuHandler.Connect() |
| | | if err != nil { |
| | | logx.Errorf("getModbusRTUConnection Connect err:%v, config: %+v", err, c) |
| | | return nil |
| | | } |
| | | return modbus.NewClient(rtuHandler) |
| | | } |
| | |
| | | |
| | | import ( |
| | | "encoding/binary" |
| | | "sync/atomic" |
| | | ) |
| | | |
| | | func Read(ipAddr string, address uint16, quantity uint16) (data []byte, err error) { |
| | |
| | | |
| | | func dealErr(err error, ipAddr string) { |
| | | if err != nil { |
| | | unsetModbusConnection(ipAddr) //失败则删除缓存的连接 |
| | | FailureRemainingOpportunitiesDecr() //减少失败剩余机会 |
| | | } else { |
| | | FailureRemainingOpportunitiesReset() //重置失败剩余机会 |
| | | unsetModbusConnection(ipAddr) //失败则删除缓存的连接 |
| | | } |
| | | } |
| | | |
| | | var connectionStatus atomic.Bool |
| | | |
| | | var failureRemainingOpportunities atomic.Int64 |
| | | |
| | | const ( |
| | | defaultFailureRemainingOpportunities = 20 |
| | | ) |
| | | |
| | | func IsConnect() bool { |
| | | return connectionStatus.Load() |
| | | } |
| | | |
| | | func FailureRemainingOpportunitiesDecr() { |
| | | newValue := failureRemainingOpportunities.Add(-1) |
| | | if newValue <= 0 { |
| | | connectionStatus.Store(false) |
| | | } |
| | | return |
| | | } |
| | | |
| | | func FailureRemainingOpportunitiesReset() { |
| | | if failureRemainingOpportunities.Load() < defaultFailureRemainingOpportunities { |
| | | failureRemainingOpportunities.Store(defaultFailureRemainingOpportunities) |
| | | } |
| | | if connectionStatus.Load() == false { |
| | | connectionStatus.Store(true) |
| | | } |
| | | return |
| | | } |
| | |
| | | package modbusx |
| | | |
| | | import ( |
| | | "apsClient/constvar" |
| | | "apsClient/model/common" |
| | | "apsClient/pkg/logx" |
| | | "github.com/goburrow/modbus" |
| | | "time" |
| | | ) |
| | | |
| | | func ReadByRTU(c *common.RTUConfig, address uint16, quantity uint16) (data []byte, err error) { |
| | | address-- |
| | | cli, err := getModbusRTUConnection(c) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | cli := getModbusRTUConnection(c) |
| | | data, err = cli.ReadHoldingRegisters(address, quantity) |
| | | dealRTUErr(err, c.SerialName) |
| | | if err != nil { |
| | | cli, err = getModbusRTUConnection(c) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | cli = getModbusRTUConnection(c) |
| | | data, err = cli.ReadHoldingRegisters(address, quantity) |
| | | } |
| | | if err != nil { |
| | | logx.Errorf("ReadByRTU ReadHoldingRegisters err:%v", err) |
| | | dealRTUErr(err, c.SerialName) |
| | | } |
| | | return |
| | | } |
| | | |
| | | func WriteByRTU(c *common.RTUConfig, address uint16, value int) (err error) { |
| | | address-- |
| | | var bytesVal []byte |
| | | bytesVal = intToBytes(value) |
| | | |
| | | cli, err := getModbusRTUConnection(c) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | cli := getModbusRTUConnection(c) |
| | | _, err = cli.WriteMultipleRegisters(address, uint16(len(bytesVal)), bytesVal) |
| | | dealRTUErr(err, c.SerialName) |
| | | if err != nil { |
| | | cli, err = getModbusRTUConnection(c) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | cli = getModbusRTUConnection(c) |
| | | _, err = cli.WriteMultipleRegisters(address, uint16(len(bytesVal)), bytesVal) |
| | | } |
| | | if err != nil { |
| | | logx.Errorf("WriteByRTU WriteMultipleRegisters err:%v", err) |
| | | dealRTUErr(err, c.SerialName) |
| | | } |
| | | return err |
| | | } |
| | | |
| | | func getModbusRTUConnection(c *common.RTUConfig) (cli modbus.Client, err error) { |
| | | // Modbus RTU/ASCII |
| | | h := modbus.NewRTUClientHandler(c.SerialName) |
| | | h.BaudRate = c.BaudRate |
| | | h.DataBits = c.DataBit |
| | | switch c.Parity { |
| | | case constvar.ParityEven: |
| | | h.Parity = "E" |
| | | case constvar.ParityOdd: |
| | | h.Parity = "O" |
| | | case constvar.ParityNull: |
| | | h.Parity = "N" |
| | | } |
| | | |
| | | h.StopBits = c.StopBit |
| | | h.SlaveId = 1 |
| | | h.Timeout = 5 * time.Second |
| | | |
| | | err = h.Connect() |
| | | func dealRTUErr(err error, serialName string) { |
| | | if err != nil { |
| | | logx.Errorf("getModbusRTUConnection Connect err:%v, config: %+v", err, c) |
| | | return nil, err |
| | | unsetModbusRTUConnection(serialName) //失败则删除缓存的连接 |
| | | } |
| | | defer h.Close() |
| | | cli = modbus.NewClient(h) |
| | | return |
| | | } |