From d457b47a702857cdefb56a0a518201ba8ad2505f Mon Sep 17 00:00:00 2001 From: zhangqian <zhangqian@123.com> Date: 星期六, 16 九月 2023 19:07:01 +0800 Subject: [PATCH] apache plc4x短连接 --- api/v1/plc.go | 4 service/plc.go | 73 +++----------- pkg/plc/apacheplc4x/modbus.go | 203 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 60 deletions(-) diff --git a/api/v1/plc.go b/api/v1/plc.go index d244314..5f3b279 100644 --- a/api/v1/plc.go +++ b/api/v1/plc.go @@ -6,7 +6,7 @@ "apsClient/model/response" _ "apsClient/model/response" "apsClient/pkg/contextx" - "apsClient/pkg/plc/modbusx" + "apsClient/pkg/plc/apacheplc4x" "apsClient/service" "github.com/gin-gonic/gin" "github.com/spf13/cast" @@ -34,7 +34,7 @@ resp.TotalNumber = cast.ToInt(totalNumber) plcStatus := 1 //鏂紑杩炴帴 - isConnect := modbusx.IsConnect() + isConnect := apacheplc4x.IsConnect() if isConnect { if resp.FinishNumber > 0 { //鐢熶骇 plcStatus = 2 diff --git a/pkg/plc/apacheplc4x/modbus.go b/pkg/plc/apacheplc4x/modbus.go new file mode 100644 index 0000000..f44a92a --- /dev/null +++ b/pkg/plc/apacheplc4x/modbus.go @@ -0,0 +1,203 @@ +package apacheplc4x + +import ( + "apsClient/pkg/logx" + "context" + "errors" + "fmt" + plc4go "github.com/apache/plc4x/plc4go/pkg/api" + "github.com/apache/plc4x/plc4go/pkg/api/drivers" + apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" + "github.com/apache/plc4x/plc4go/pkg/api/transports" + "sync/atomic" + "time" +) + +func GetModbusConnection(ipAddr string) (plc4go.PlcConnection, error) { + // 鍒涘缓涓�涓笂涓嬫枃锛屽苟璁剧疆 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 + } + 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) + + // 閫氳繃TCP杩炴帴PLC璁惧 + connectionString := fmt.Sprintf("modbus-tcp://%s", ipAddr) + connectionRequestChanel := driverManager.GetConnection(connectionString) + + // 绛夊緟杩炴帴鍝嶅簲锛屽悓鏃惰�冭檻涓婁笅鏂囩殑瓒呮椂 + select { + case connectionResult := <-connectionRequestChanel: + if err := connectionResult.GetErr(); err != nil { + return nil, err + } + return connectionResult.GetConnection(), nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func readHoldingRegisterSingle(connection plc4go.PlcConnection, address int) ([]byte, error) { + tag := fmt.Sprintf("tag:%v", address) + tagAddress := fmt.Sprintf("holding-register:%d:UINT", address) + + // 璇绘ā寮� + readRequest, err := connection.ReadRequestBuilder().AddTagAddress(tag, tagAddress).Build() + if err != nil { + logx.Errorf("preparing read-request:%s\n", err.Error()) + return nil, err + } + + // 鎵ц + readResult := <-readRequest.Execute() + if err := readResult.GetErr(); err != nil { + logx.Errorf("execting read-request:%s\n", err.Error()) + return nil, err + } + + // 鍒ゆ柇鍝嶅簲鐮佹槸鍚︽纭� + 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) + + return value.GetRaw(), err + +} + +func readHoldingRegisterList(connection plc4go.PlcConnection, address, length int) ([]byte, error) { + tag := fmt.Sprintf("tag:%v:%v", address, length) + tagAddress := fmt.Sprintf("holding-register:%d:UINT[%d]", address, length) + + // 璇绘ā寮� + readRequest, err := connection.ReadRequestBuilder().AddTagAddress(tag, tagAddress).Build() + if err != nil { + logx.Errorf("preparing read-request:%s\n", err.Error()) + return nil, err + } + + // 鎵ц + readResult := <-readRequest.Execute() + if err := readResult.GetErr(); err != nil { + logx.Errorf("execting read-request:%s\n", err.Error()) + return nil, err + } + + // 鍒ゆ柇鍝嶅簲鐮佹槸鍚︽纭� + if readResult.GetResponse().GetResponseCode(tag) != apiModel.PlcResponseCode_OK { + logx.Errorf("error an non-ok return code: %s", readResult.GetResponse().GetResponseCode(tag).GetName()) + return nil, errors.New("error code: " + readResult.GetResponse().GetResponseCode(tag).GetName()) + } + + value := readResult.GetResponse().GetValue(tag) + + var result []byte + + for _, val := range value.GetList() { + result = append(result, val.GetRaw()...) + } + + return result, nil +} + +func ReadHoldingRegister(ipAddr string, address, length int) ([]byte, error) { + connection, err := GetModbusConnection(ipAddr) + if err != nil { + dealErr(err, ipAddr) + return nil, err + } + defer connection.Close() + if length > 1 { + return readHoldingRegisterList(connection, address, length) + } + + return readHoldingRegisterSingle(connection, address) +} + +func WriteHoldingRegister(ipAddr string, address int, value any) (string, error) { + connection, err := GetModbusConnection(ipAddr) + if err != nil { + dealErr(err, ipAddr) + return "", err + } + defer connection.Close() + tag := fmt.Sprintf("tag:%v:w", address) + tagAddress := fmt.Sprintf("holding-register:%d:UINT", address) + + // 鍐欐ā寮� + writeRequest, err := connection.WriteRequestBuilder().AddTagAddress(tag, tagAddress, value).Build() + if err != nil { + logx.Errorf("preparing read-request:%s\n", err.Error()) + return "", err + } + + // 鎵ц + writeResult := <-writeRequest.Execute() + if err := writeResult.GetErr(); err != nil { + logx.Errorf("execting read-request:%s\n", err.Error()) + return "", err + } + + // 鍒ゆ柇鍝嶅簲鐮佹槸鍚︽纭� + if writeResult.GetResponse().GetResponseCode(tag) != apiModel.PlcResponseCode_OK { + logx.Errorf("error an non-ok return 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 +} diff --git a/service/plc.go b/service/plc.go index 8b00c7d..e2a3990 100644 --- a/service/plc.go +++ b/service/plc.go @@ -5,67 +5,13 @@ "apsClient/constvar" "apsClient/model" "apsClient/pkg/logx" - "apsClient/pkg/plc" - "apsClient/pkg/plc/modbusx" + "apsClient/pkg/plc/apacheplc4x" "apsClient/pkg/plccom" "encoding/binary" "errors" "fmt" "github.com/spf13/cast" ) - -func PlcRead(plcConfig *model.DevicePlc, fieldType constvar.PlcStartAddressType, position int) (val interface{}, err error) { - var ( - startAddress int - valueType constvar.PlcStartAddressValueType - dataLength int - ipAddr string - ) - - for _, pc := range plcConfig.Details { - if pc.FieldName == fieldType && pc.Position == position { - startAddress = pc.StartAddress - valueType = pc.Type - dataLength = pc.Length - } - } - - if plcConfig.Method == constvar.PlcMethodModbusTCP { - ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port) - - conn, err := plc.GetModbusConnection(ipAddr) - if err != nil { - logx.Errorf("PlcRead 杩炴帴plc澶辫触: %v", err.Error()) - return nil, err - } - - rawData, err := plc.ReadHoldingRegister(conn, startAddress, dataLength) - if err != nil { - logx.Errorf("PlcRead 鑾峰彇plc鏁版嵁澶辫触: %v", err.Error()) - return nil, err - } - switch valueType { - case constvar.PlcStartAddressValueTypeString: - return string(rawData), nil - case constvar.PlcStartAddressValueTypeInt: - if len(rawData) == 2 { - return int(binary.BigEndian.Uint16(rawData)), nil - } else { - logx.Errorf("plc read get an unknown int value: %v, address:%v", rawData, startAddress) - return nil, errors.New(fmt.Sprintf("unknown int value锛�%v", rawData)) - } - } - return nil, errors.New("undefined value type") - } else if plcConfig.Method == constvar.PlcMethodSerial { - ipAddr = conf.Conf.Services.Serial - if ipAddr == "" { - return nil, errors.New("conf.Conf.Services.Serial config not set yet") - } - label := fmt.Sprintf("D%d", startAddress) - return plccom.ReadPLC(plccom.DeviceTypeMitsubishi, ipAddr, label) - } - return nil, errors.New("interface type not support") -} func PlcWrite(plcConfig *model.DevicePlc, fieldType constvar.PlcStartAddressType, position int, value interface{}) (err error) { var ( @@ -94,7 +40,11 @@ //} // //result, err := plc.WriteHoldingRegister(conn, startAddress, value) - err = modbusx.Write(ipAddr, uint16(startAddress), value) + + //err = modbusx.Write(ipAddr, uint16(startAddress), value) + + _, err = apacheplc4x.WriteHoldingRegister(ipAddr, startAddress, value) + if err != nil { logx.Errorf("plc write failed, address: %v, value: %v, err: %v", startAddress, value, err.Error()) plcConfig.CurrentErr = err @@ -126,7 +76,10 @@ //} //result, err := plc.WriteHoldingRegister(conn, address, value) - err = modbusx.Write(ipAddr, uint16(address), value) + //err = modbusx.Write(ipAddr, uint16(address), value) + + _, err = apacheplc4x.WriteHoldingRegister(ipAddr, address, value) + if err != nil { logx.Errorf("plc write failed, address: %v, value: %v, err: %v", address, value, err.Error()) return err @@ -155,7 +108,11 @@ // return PlcReadDirect(plcConfig, address, dataLength, valueType) //} //value, err := plc.ReadHoldingRegister(conn, address, dataLength) - value, err := modbusx.Read(ipAddr, uint16(address), uint16(dataLength)) + + //value, err := modbusx.Read(ipAddr, uint16(address), uint16(dataLength)) + + value, err := apacheplc4x.ReadHoldingRegister(ipAddr, address, dataLength) + if err != nil { return nil, err } -- Gitblit v1.8.0