From 8324f872ef3a4d0c978a9b1d062800c6a1701c12 Mon Sep 17 00:00:00 2001 From: zhangqian <zhangqian@123.com> Date: 星期五, 01 十二月 2023 09:58:17 +0800 Subject: [PATCH] fix --- pkg/plc/plc4x.go | 205 ++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 168 insertions(+), 37 deletions(-) diff --git a/pkg/plc/plc4x.go b/pkg/plc/plc4x.go index 3e6c4a6..a7f9d25 100644 --- a/pkg/plc/plc4x.go +++ b/pkg/plc/plc4x.go @@ -1,22 +1,93 @@ package plc import ( + "apsClient/conf" + "apsClient/pkg/logx" + "context" "errors" "fmt" apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" + "github.com/spf13/cast" + "sync" + "sync/atomic" + "time" plc4go "github.com/apache/plc4x/plc4go/pkg/api" "github.com/apache/plc4x/plc4go/pkg/api/drivers" "github.com/apache/plc4x/plc4go/pkg/api/transports" ) -func NewModbusConnection(ipAddr string) (plc4go.PlcConnection, error) { +type ConnectionManager struct { + connections map[string]plc4go.PlcConnection + mu sync.Mutex +} + +func newPlcConnectionManager() *ConnectionManager { + return &ConnectionManager{ + connections: make(map[string]plc4go.PlcConnection), + } +} + +func (cm *ConnectionManager) GetConnection(address string) (plc4go.PlcConnection, bool) { + cm.mu.Lock() + defer cm.mu.Unlock() + + conn, ok := cm.connections[address] + if !ok { + return nil, false + } + //if ok, _ := cm.CheckConnect(conn, time.Second); !ok { + // conn.Close() + //} + + return conn, true +} + +var connectionManager = newPlcConnectionManager() + +func (cm *ConnectionManager) AddConnection(address string, connection plc4go.PlcConnection) { + cm.mu.Lock() + defer cm.mu.Unlock() + + cm.connections[address] = connection +} + +func (cm *ConnectionManager) CheckConnect(conn plc4go.PlcConnection, timeout time.Duration) (bool, error) { + pingCh := conn.Ping() + timer := time.NewTimer(timeout) + + select { + case err := <-pingCh: + if err == nil { + return true, nil + } + return false, err.GetErr() + case <-timer.C: + return false, fmt.Errorf("connection timed out after %s", timeout) + } +} + +func GetModbusConnection(ipAddr string) (plc4go.PlcConnection, error) { + if conn, ok := connectionManager.GetConnection(ipAddr); ok { + return conn, nil + } + // 鍒涘缓涓�涓笂涓嬫枃锛屽苟璁剧疆 3 绉掕秴鏃� + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + conn, err := newGetModbusConnection(ctx, ipAddr) + if err != nil { + logx.Errorf("new modbus connection err: %v", err.Error()) + return nil, err + } + connectionManager.AddConnection(ipAddr, conn) + return conn, nil +} + +func newGetModbusConnection(ctx context.Context, ipAddr string) (plc4go.PlcConnection, error) { // 鍒涘缓椹卞姩绠$悊鍣� driverManager := plc4go.NewPlcDriverManager() - // 娉ㄥ唽TCP浼犺緭 transports.RegisterTcpTransport(driverManager) - // 娉ㄥ唽椹卞姩 //drivers.RegisterKnxDriver(driverManager) drivers.RegisterModbusTcpDriver(driverManager) @@ -25,69 +96,73 @@ connectionString := fmt.Sprintf("modbus-tcp://%s", ipAddr) connectionRequestChanel := driverManager.GetConnection(connectionString) - // 绛夊緟杩炴帴鍝嶅簲 - connectionResult := <-connectionRequestChanel - - // 鍒ゆ柇鏄惁杩炴帴鎴愬姛 - if err := connectionResult.GetErr(); err != nil { - return nil, err + // 绛夊緟杩炴帴鍝嶅簲锛屽悓鏃惰�冭檻涓婁笅鏂囩殑瓒呮椂 + select { + case connectionResult := <-connectionRequestChanel: + if err := connectionResult.GetErr(); err != nil { + return nil, err + } + return connectionResult.GetConnection(), nil + case <-ctx.Done(): + return nil, ctx.Err() } - - return connectionResult.GetConnection(), nil } -func ReadHoldingRegisterSingle(connection plc4go.PlcConnection, address int) ([]byte, error) { - tagAddress := fmt.Sprintf("holding-register:%d:UINT", address) + +func readHoldingRegisterSingle(connection plc4go.PlcConnection, address int) ([]byte, error) { + tag := fmt.Sprintf("tag:%v", address) + tagAddress := getTagAddress(address, 1) // 璇绘ā寮� - readRequest, err := connection.ReadRequestBuilder().AddTagAddress("tag", tagAddress).Build() + readRequest, err := connection.ReadRequestBuilder().AddTagAddress(tag, tagAddress).Build() if err != nil { - fmt.Printf("preparing read-request:%s\n", err.Error()) + logx.Errorf("preparing read-request:%s\n", err.Error()) return nil, err } // 鎵ц readResult := <-readRequest.Execute() if err := readResult.GetErr(); err != nil { - fmt.Printf("execting read-request:%s\n", err.Error()) + logx.Errorf("execting read-request:%s\n", err.Error()) return nil, err } // 鍒ゆ柇鍝嶅簲鐮佹槸鍚︽纭� - if readResult.GetResponse().GetResponseCode("tag") != apiModel.PlcResponseCode_OK { - fmt.Printf("error an non-ok return code: %s", readResult.GetResponse().GetResponseCode("tag").GetName()) + if readResult.GetResponse().GetResponseCode(tag) != apiModel.PlcResponseCode_OK { + logx.Errorf("error an non-ok return code: %s", readResult.GetResponse().GetResponseCode(tag).GetName()) return nil, nil } - value := readResult.GetResponse().GetValue("tag") + value := readResult.GetResponse().GetValue(tag) return value.GetRaw(), err } -func ReadHoldingRegisterList(connection plc4go.PlcConnection, address, length int) ([]byte, error) { - tagAddress := fmt.Sprintf("holding-register:%d:UINT[%d]", address, length) +func readHoldingRegisterList(connection plc4go.PlcConnection, address, length int) ([]byte, error) { + tag := fmt.Sprintf("tag:%v:%v", address, length) + tagAddress := getTagAddress(address, length) // 璇绘ā寮� - readRequest, err := connection.ReadRequestBuilder().AddTagAddress("tag", tagAddress).Build() + readRequest, err := connection.ReadRequestBuilder().AddTagAddress(tag, tagAddress).Build() if err != nil { - fmt.Printf("preparing read-request:%s\n", err.Error()) + logx.Errorf("preparing read-request:%s\n", err.Error()) return nil, err } // 鎵ц readResult := <-readRequest.Execute() if err := readResult.GetErr(); err != nil { - fmt.Printf("execting read-request:%s\n", err.Error()) + logx.Errorf("plc4x execute read-request:%s\n", err.Error()) return nil, err } // 鍒ゆ柇鍝嶅簲鐮佹槸鍚︽纭� - if readResult.GetResponse().GetResponseCode("tag") != apiModel.PlcResponseCode_OK { - fmt.Printf("error an non-ok return code: %s", readResult.GetResponse().GetResponseCode("tag").GetName()) - return nil, errors.New("error code: " + readResult.GetResponse().GetResponseCode("tag").GetName()) + if readResult.GetResponse().GetResponseCode(tag) != apiModel.PlcResponseCode_OK { + logx.Errorf("plc4x response error code: %s", readResult.GetResponse().GetResponseCode(tag).GetName()) + return nil, errors.New("error code: " + readResult.GetResponse().GetResponseCode(tag).GetName()) } - value := readResult.GetResponse().GetValue("tag") + value := readResult.GetResponse().GetValue(tag) var result []byte @@ -100,36 +175,92 @@ func ReadHoldingRegister(connection plc4go.PlcConnection, address, length int) ([]byte, error) { if length > 1 { - return ReadHoldingRegisterList(connection, address, length) + return readHoldingRegisterList(connection, address, length) } - return ReadHoldingRegisterSingle(connection, address) + return readHoldingRegisterSingle(connection, address) +} + +func getTagAddress(address int, length int) string { + intType := conf.Conf.PLC.ModbusIntType + if intType == "" { + intType = "DINT" + } + if length == 1 { + return fmt.Sprintf("holding-register:%d:%v", address, intType) + } else { + return fmt.Sprintf("holding-register:%d:%v[%d]", address, intType, length) + } } func WriteHoldingRegister(connection plc4go.PlcConnection, address int, value any) (string, error) { - tagAddress := fmt.Sprintf("holding-register:%d:UINT", address) + tag := fmt.Sprintf("tag:%v:w", address) + var tagAddress string + if cast.ToInt32(value) > 2<<16 { + tagAddress = getTagAddress(address, 2) + } else { + tagAddress = getTagAddress(address, 1) + } // 鍐欐ā寮� - writeRequest, err := connection.WriteRequestBuilder().AddTagAddress("tag", tagAddress, value).Build() + writeRequest, err := connection.WriteRequestBuilder().AddTagAddress(tag, tagAddress, value).Build() if err != nil { - fmt.Printf("preparing read-request:%s\n", err.Error()) + logx.Errorf("plc4x preparing read-request:%s\n", err.Error()) return "", err } // 鎵ц writeResult := <-writeRequest.Execute() if err := writeResult.GetErr(); err != nil { - fmt.Printf("execting read-request:%s\n", err.Error()) + logx.Errorf("plc4x execute write-request:%s\n", err.Error()) return "", err } // 鍒ゆ柇鍝嶅簲鐮佹槸鍚︽纭� - if writeResult.GetResponse().GetResponseCode("tag") != apiModel.PlcResponseCode_OK { - fmt.Printf("error an non-ok return code: %s", writeResult.GetResponse().GetResponseCode("tag").GetName()) - return "", errors.New("error code: " + writeResult.GetResponse().GetResponseCode("tag").GetName()) + if writeResult.GetResponse().GetResponseCode(tag) != apiModel.PlcResponseCode_OK { + logx.Errorf("plc4x response error code: %s", writeResult.GetResponse().GetResponseCode(tag).GetName()) + return "", errors.New("error code: " + writeResult.GetResponse().GetResponseCode(tag).GetName()) } result := writeResult.GetResponse().String() return result, nil } + +func dealErr(err error, ipAddr string) { + if err != nil { + FailureRemainingOpportunitiesDecr() //鍑忓皯澶辫触鍓╀綑鏈轰細 + } else { + FailureRemainingOpportunitiesReset() //閲嶇疆澶辫触鍓╀綑鏈轰細 + } +} + +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 +} -- Gitblit v1.8.0