From 620d904921c76b6b29d1b69787da7a82121d4c31 Mon Sep 17 00:00:00 2001 From: zhangqian <zhangqian@123.com> Date: 星期四, 26 十月 2023 21:15:35 +0800 Subject: [PATCH] 读写plc支持modbusRTU --- service/plc.go | 99 ++++++++++++------- test/plc_test.go | 2 model/common/common.go | 8 + pkg/plc/apacheplc4x/modbus.go | 4 go.mod | 2 pkg/plc/apacheplc4x/modbusrtu.go | 85 +++++++++++++++++ pkg/plc/modbusx/modbusrtu.go | 72 ++++++++++++++ 7 files changed, 232 insertions(+), 40 deletions(-) diff --git a/go.mod b/go.mod index f87ef64..24c5e68 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ github.com/gofrs/uuid v4.4.0+incompatible github.com/gogo/protobuf v1.3.2 github.com/gorilla/websocket v1.5.0 - github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4 github.com/jinzhu/gorm v1.9.16 github.com/mitchellh/mapstructure v1.5.0 github.com/mojocn/base64Captcha v1.3.1 @@ -66,6 +65,7 @@ github.com/gopacket/gopacket v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/icza/bitio v1.1.0 // indirect + github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/model/common/common.go b/model/common/common.go index 3a8781f..d616014 100644 --- a/model/common/common.go +++ b/model/common/common.go @@ -157,3 +157,11 @@ DataType PullDataType `json:"dataType"` //瑕佹媺鍙栫殑鏁版嵁绫诲瀷 Data interface{} //杩斿洖鐨勬暟鎹� } + +type RTUConfig struct { + BaudRate int `json:"baudRate"` //涓插彛娉㈢壒鐜囷紝 method = serial鏃� 鐢� + SerialName string `json:"serialName"` //涓插彛鍚嶇О锛宮ethod = serial鏃� 鐢� + DataBit int `gorm:"type:int(11)" json:"dataBit"` //鏁版嵁浣嶏紝method = modbusRTU 鐢� + StopBit int `gorm:"type:int(11)" json:"stopBit"` //鍋滄浣嶏紝method = modbusRTU 鐢� + Parity constvar.Parity `gorm:"type:int(11)" json:"parity"` //鏍¢獙鏂瑰紡锛宮ethod = modbusRTU 鐢� +} diff --git a/pkg/plc/apacheplc4x/modbus.go b/pkg/plc/apacheplc4x/modbus.go index 3876010..dd3621f 100644 --- a/pkg/plc/apacheplc4x/modbus.go +++ b/pkg/plc/apacheplc4x/modbus.go @@ -19,7 +19,7 @@ // 鍒涘缓涓�涓笂涓嬫枃锛屽苟璁剧疆 3 绉掕秴鏃� ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - conn, err := newGetModbusConnection(ctx, ipAddr) + conn, err := newModbusConnection(ctx, ipAddr) if err != nil { logx.Errorf("new modbus connection err: %v", err.Error()) return nil, err @@ -27,7 +27,7 @@ return conn, nil } -func newGetModbusConnection(ctx context.Context, ipAddr string) (plc4go.PlcConnection, error) { +func newModbusConnection(ctx context.Context, ipAddr string) (plc4go.PlcConnection, error) { // 鍒涘缓椹卞姩绠$悊鍣� driverManager := plc4go.NewPlcDriverManager() // 娉ㄥ唽TCP浼犺緭 diff --git a/pkg/plc/apacheplc4x/modbusrtu.go b/pkg/plc/apacheplc4x/modbusrtu.go new file mode 100644 index 0000000..8c50ff5 --- /dev/null +++ b/pkg/plc/apacheplc4x/modbusrtu.go @@ -0,0 +1,85 @@ +package apacheplc4x + +import ( + "apsClient/model/common" + "apsClient/pkg/logx" + "context" + "errors" + "fmt" + plc4go "github.com/apache/plc4x/plc4go/pkg/api" + apiModel "github.com/apache/plc4x/plc4go/pkg/api/model" + "github.com/spf13/cast" + "time" +) + +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) + // 绛夊緟杩炴帴鍝嶅簲锛屽悓鏃惰�冭檻涓婁笅鏂囩殑瓒呮椂 + select { + case connectionResult := <-connectionRequestChanel: + cancel() + if err := connectionResult.GetErr(); err != nil { + return nil, err + } + return connectionResult.GetConnection(), nil + case <-ctx.Done(): + cancel() + return nil, errors.New("connect plc by modbusRTU timeout") + } +} + +func ReadHoldingRegisterByRTU(c *common.RTUConfig, address, length int) ([]byte, error) { + connection, err := newModbusRTUConnection(c) + if err != nil { + return nil, err + } + defer connection.Close() + if length > 1 { + return readHoldingRegisterList(connection, address, length) + } + + return readHoldingRegisterSingle(connection, address) +} + +func WriteHoldingRegisterByRTU(c *common.RTUConfig, address int, value any) (string, error) { + connection, err := newModbusRTUConnection(c) + if err != nil { + return "", err + } + defer connection.Close() + 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() + if err != nil { + logx.Errorf("plc4x preparing read-request:%s\n", err.Error()) + return "", err + } + + // 鎵ц + writeResult := <-writeRequest.Execute() + if err := writeResult.GetErr(); err != nil { + logx.Errorf("plc4x execute write-request:%s\n", err.Error()) + return "", err + } + + // 鍒ゆ柇鍝嶅簲鐮佹槸鍚︽纭� + 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 +} diff --git a/pkg/plc/modbusx/modbusrtu.go b/pkg/plc/modbusx/modbusrtu.go new file mode 100644 index 0000000..46348cf --- /dev/null +++ b/pkg/plc/modbusx/modbusrtu.go @@ -0,0 +1,72 @@ +package modbusx + +import ( + "apsClient/constvar" + "apsClient/model/common" + "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 + } + data, err = cli.ReadHoldingRegisters(address, quantity) + if err != nil { + cli, err = getModbusRTUConnection(c) + if err != nil { + return nil, err + } + data, err = cli.ReadHoldingRegisters(address, quantity) + } + 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 + } + _, err = cli.WriteMultipleRegisters(address, uint16(len(bytesVal)), bytesVal) + if err != nil { + cli, err = getModbusRTUConnection(c) + if err != nil { + return err + } + _, err = cli.WriteMultipleRegisters(address, uint16(len(bytesVal)), bytesVal) + } + 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() + if err != nil { + return nil, err + } + defer h.Close() + cli = modbus.NewClient(h) + return +} diff --git a/service/plc.go b/service/plc.go index 183cb00..68258b8 100644 --- a/service/plc.go +++ b/service/plc.go @@ -4,6 +4,7 @@ "apsClient/conf" "apsClient/constvar" "apsClient/model" + "apsClient/model/common" "apsClient/pkg/logx" "apsClient/pkg/plc" "apsClient/pkg/plc/apacheplc4x" @@ -19,7 +20,6 @@ func PlcWrite(plcConfig *model.DevicePlc, fieldType constvar.PlcStartAddressType, channel int32, value interface{}) (err error) { var ( startAddress int - ipAddr string ) if plcConfig.CurrentTryTimes > plcConfig.MaxTryTimes { @@ -32,26 +32,7 @@ startAddress = pc.StartAddress } } - - if plcConfig.Method == constvar.PlcMethodModbusTCP { - ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port) - err = 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 - return PlcWrite(plcConfig, fieldType, channel, value) - } - logx.Infof("plc write ok, address: %v, value: %v", startAddress, value) - } else if plcConfig.Method == constvar.PlcMethodSerial { - ipAddr = conf.Conf.Services.Serial - if ipAddr == "" { - return errors.New("conf.Conf.Services.Serial config not set yet") - } - label := fmt.Sprintf("D%d", startAddress) - return plccom.WritePLC(plccom.DeviceTypeMitsubishi, ipAddr, label, cast.ToInt(value)) - } - return + return PlcWriteDirect(plcConfig, startAddress, value) } func PlcWriteDirect(plcConfig *model.DevicePlc, address int, value interface{}) (err error) { @@ -61,7 +42,16 @@ defer dealErr(err) if plcConfig.Method == constvar.PlcMethodModbusTCP { ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port) - err = WriteHoldingRegister(ipAddr, address, value) + err = WriteByModbusTCP(ipAddr, address, value) + + if err != nil { + logx.Errorf("plc write failed, address: %v, value: %v, err: %v", address, value, err.Error()) + return err + } + logx.Infof("plc write ok, address: %v, value: %v", address, value) + } else if plcConfig.Method == constvar.PlcMethodModbusRTU { + ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port) + err = WriteByModbusRTU(plcConfig, address, value) if err != nil { logx.Errorf("plc write failed, address: %v, value: %v, err: %v", address, value, err.Error()) @@ -84,12 +74,21 @@ ipAddr string ) defer dealErr(err) - if plcConfig.Method == constvar.PlcMethodModbusTCP { - ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port) - value, err := ReadHoldingRegister(ipAddr, address, dataLength) - if err != nil { - return nil, err + if plcConfig.Method == constvar.PlcMethodModbusTCP || plcConfig.Method == constvar.PlcMethodModbusRTU { + var value []byte + if plcConfig.Method == constvar.PlcMethodModbusTCP { + ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port) + value, err = ReadByModbusTCP(ipAddr, address, dataLength) + if err != nil { + return nil, err + } + } else { + value, err = ReadByModbusRTU(plcConfig, address, dataLength) + if err != nil { + return nil, err + } } + switch valueType { case constvar.PlcStartAddressValueTypeString: return string(value), nil @@ -118,7 +117,7 @@ return } -func ReadHoldingRegister(ipAddr string, address, length int) ([]byte, error) { +func ReadByModbusTCP(ipAddr string, address, length int) ([]byte, error) { if conf.Conf.PLC.Package == constvar.PlcPackageApache { newLength := length / 2 if newLength == 0 { @@ -140,7 +139,7 @@ } } -func WriteHoldingRegister(ipAddr string, address int, value any) (err error) { +func WriteByModbusTCP(ipAddr string, address int, value any) (err error) { if conf.Conf.PLC.Package == constvar.PlcPackageApache { _, err = apacheplc4x.WriteHoldingRegister(ipAddr, address, value) return err @@ -156,15 +155,43 @@ } } +func ReadByModbusRTU(plcConfig *model.DevicePlc, address, length int) ([]byte, error) { + rtuConfig := &common.RTUConfig{ + BaudRate: plcConfig.BaudRate, + SerialName: plcConfig.SerialName, + DataBit: plcConfig.DataBit, + StopBit: plcConfig.StopBit, + Parity: plcConfig.Parity, + } + if conf.Conf.PLC.Package == constvar.PlcPackageApache { + newLength := length / 2 + if newLength == 0 { + newLength = 1 + } + return apacheplc4x.ReadHoldingRegisterByRTU(rtuConfig, address, newLength) + } else { + return modbusx.ReadByRTU(rtuConfig, uint16(address), uint16(length)) + } +} + +func WriteByModbusRTU(plcConfig *model.DevicePlc, address int, value any) (err error) { + rtuConfig := &common.RTUConfig{ + BaudRate: plcConfig.BaudRate, + SerialName: plcConfig.SerialName, + DataBit: plcConfig.DataBit, + StopBit: plcConfig.StopBit, + Parity: plcConfig.Parity, + } + if conf.Conf.PLC.Package == constvar.PlcPackageApache { + _, err = apacheplc4x.WriteHoldingRegisterByRTU(rtuConfig, address, value) + return err + } else { + return modbusx.WriteByRTU(rtuConfig, uint16(address), cast.ToInt(value)) + } +} + func PlcIsConnect() bool { return IsConnect() - //if conf.Conf.PLC.Package == constvar.PlcPackageApache { - // return apacheplc4x.IsConnect() - //} else if conf.Conf.PLC.Package == constvar.PlcPackageApacheLongConnection { - // return false - //} else { - // return modbusx.IsConnect() - //} } func dealErr(err error) { diff --git a/test/plc_test.go b/test/plc_test.go index 92ea393..d0e2475 100644 --- a/test/plc_test.go +++ b/test/plc_test.go @@ -17,7 +17,7 @@ log.Fatal(err) } var readValue int - raw, err := service.ReadHoldingRegister(ipPort, address, 1) + raw, err := service.ReadByModbusTCP(ipPort, address, 1) if err != nil { log.Fatal(err) } -- Gitblit v1.8.0