zhangqian
2023-12-19 be40c6bbad18cee0444c50069d98967cd26b6c3b
plc读写参数调整,读写方式调整
7个文件已修改
383 ■■■■ 已修改文件
api/v1/task.go 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
crontask/cron_task.go 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/plc/apacheplc4x/modbus.go 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/plc/apacheplc4x/modbusrtu.go 165 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/plc/modbusx/modbus.go 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/plc/modbusx/modbusrtu.go 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/plc.go 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/v1/task.go
@@ -430,7 +430,9 @@
            logx.Errorf("miss param address, k:%v, v:%v", k, v)
            continue
        }
        err := service.PlcWriteDirect(plcConfig, address, v)
        err := service.PlcWriteDirect(plcConfig, v, &model.DevicePlcAddress{
            StartAddress: address,
        })
        if err != nil {
            plcConfig.CurrentErr = err
            failedNumbers++
crontask/cron_task.go
@@ -38,7 +38,7 @@
        }
        for _, addressItem := range plcConfig.Details {
            if addressItem.FieldName == constvar.PlcStartAddressTypeFinishNumber {
                value, err := service.PlcReadDirect(plcConfig, addressItem.StartAddress, addressItem.Length, addressItem.Type)
                value, err := service.PlcReadDirect(plcConfig, addressItem)
                if err != nil {
                    logx.Infof("plc read finish number err: %v", err)
                    continue
@@ -64,7 +64,7 @@
        }
        for _, addressItem := range plcConfig.Details {
            if addressItem.FieldName == constvar.PlcStartAddressTypeTotalNumber {
                value, err := service.PlcReadDirect(plcConfig, addressItem.StartAddress, addressItem.Length, addressItem.Type)
                value, err := service.PlcReadDirect(plcConfig, addressItem)
                if err != nil {
                    logx.Infof("plc read total number err: %v", err)
                    continue
pkg/plc/apacheplc4x/modbus.go
@@ -1,7 +1,6 @@
package apacheplc4x
import (
    "apsClient/conf"
    "apsClient/pkg/logx"
    "context"
    "errors"
@@ -10,7 +9,6 @@
    "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"
    "github.com/spf13/cast"
    "sync/atomic"
    "time"
)
@@ -56,9 +54,9 @@
    }
}
func readHoldingRegisterSingle(connection plc4go.PlcConnection, address int) ([]byte, error) {
func readHoldingRegisterSingle(connection plc4go.PlcConnection, address int, intType string) ([]byte, error) {
    tag := fmt.Sprintf("tag:%v", address)
    tagAddress := getTagAddress(address, 1)
    tagAddress := getTagAddress(address, 1, intType)
    // 读模式
    readRequest, err := connection.ReadRequestBuilder().AddTagAddress(tag, tagAddress).Build()
@@ -86,9 +84,9 @@
}
func readHoldingRegisterList(connection plc4go.PlcConnection, address, length int) ([]byte, error) {
func readHoldingRegisterList(connection plc4go.PlcConnection, address, length int, intType string) ([]byte, error) {
    tag := fmt.Sprintf("tag:%v:%v", address, length)
    tagAddress := getTagAddress(address, length)
    tagAddress := getTagAddress(address, length, intType)
    // 读模式
    readRequest, err := connection.ReadRequestBuilder().AddTagAddress(tag, tagAddress).Build()
@@ -121,7 +119,7 @@
    return result, nil
}
func ReadHoldingRegister(ipAddr string, address, length int) ([]byte, error) {
func ReadHoldingRegister(ipAddr string, address, length int, intType string) ([]byte, error) {
    connection, err := GetModbusConnection(ipAddr)
    dealErr(err, ipAddr)
    if err != nil {
@@ -129,14 +127,13 @@
    }
    defer connection.Close()
    if length > 1 {
        return readHoldingRegisterList(connection, address, length)
        return readHoldingRegisterList(connection, address, length, intType)
    }
    return readHoldingRegisterSingle(connection, address)
    return readHoldingRegisterSingle(connection, address, intType)
}
func getTagAddress(address int, length int) string {
    intType := conf.Conf.PLC.ModbusIntType
func getTagAddress(address int, length int, intType string) string {
    if intType == "" {
        intType = "DINT"
    }
@@ -147,7 +144,7 @@
    }
}
func WriteHoldingRegister(ipAddr string, address int, value any) (string, error) {
func WriteHoldingRegister(ipAddr string, address int, value any, intType string) (string, error) {
    connection, err := GetModbusConnection(ipAddr)
    dealErr(err, ipAddr)
    if err != nil {
@@ -156,11 +153,7 @@
    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)
    }
    tagAddress = getTagAddress(address, 1, intType)
    // 写模式
    writeRequest, err := connection.WriteRequestBuilder().AddTagAddress(tag, tagAddress, value).Build()
pkg/plc/apacheplc4x/modbusrtu.go
@@ -1,84 +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 连接
    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.ToString())
    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
}
//
//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 连接
//    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.ToString())
//    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
//}
pkg/plc/modbusx/modbus.go
@@ -17,23 +17,43 @@
    return
}
func Write(ipAddr string, address uint16, value int) (err error) {
func Write(ipAddr string, address uint16, value int, endian string, length int) (err error) {
    address--
    var bytesVal []byte
    bytesVal = intToBytes(value)
    bytesVal = intToBytes(value, endian, length)
    cli := getModbusConnection(ipAddr)
    _, err = cli.WriteMultipleRegisters(address, uint16(len(bytesVal)), bytesVal)
    dealErr(err, ipAddr)
    return err
}
func intToBytes(value int) (data []byte) {
    if value <= 1<<16 {
        data = make([]byte, 2)
        binary.BigEndian.PutUint16(data, uint16(value))
func intToBytes(value int, endian string, length int) (data []byte) {
    if endian == "mix" {
        data = make([]byte, 0, 4)
        highVal := value / (1 << 16)
        highBts := make([]byte, 2)
        binary.BigEndian.PutUint16(highBts, uint16(highVal))
        lowValue := value % (1 << 16)
        lowBts := make([]byte, 2)
        binary.BigEndian.PutUint16(lowBts, uint16(lowValue))
        data = append(data, lowBts...)
        data = append(data, highBts...)
    } else {
        data = make([]byte, 4)
        binary.BigEndian.PutUint32(data, uint32(value))
        if length == 1 {
            data = make([]byte, 2)
            if endian == "big" {
                binary.BigEndian.PutUint16(data, uint16(value))
            } else {
                binary.LittleEndian.PutUint16(data, uint16(value))
            }
        } else {
            data = make([]byte, 4)
            if endian == "big" {
                binary.BigEndian.PutUint32(data, uint32(value))
            } else {
                binary.LittleEndian.PutUint32(data, uint32(value))
            }
        }
    }
    return
}
pkg/plc/modbusx/modbusrtu.go
@@ -16,9 +16,9 @@
    return
}
func WriteByRTU(c *common.RTUConfig, address uint16, value int) (err error) {
func WriteByRTU(c *common.RTUConfig, address uint16, value int, endian string, length int) (err error) {
    var bytesVal []byte
    bytesVal = intToBytes(value)
    bytesVal = intToBytes(value, endian, length)
    cli := getModbusRTUConnection(c)
    _, err = cli.WriteMultipleRegisters(address, uint16(len(bytesVal)), bytesVal)
    dealRTUErr(err, c.SerialName)
service/plc.go
@@ -21,24 +21,20 @@
var lock sync.Mutex
func PlcWrite(plcConfig *model.DevicePlc, fieldType constvar.PlcStartAddressType, channel int32, value interface{}) (err error) {
    var (
        startAddress int
    )
    if plcConfig.CurrentTryTimes > plcConfig.MaxTryTimes {
        return plcConfig.CurrentErr
    }
    plcConfig.CurrentTryTimes++
    var targetConfig *model.DevicePlcAddress
    for _, pc := range plcConfig.Details {
        if pc.FieldName == fieldType && pc.Channel == channel {
            startAddress = pc.StartAddress
            targetConfig = pc
        }
    }
    return PlcWriteDirect(plcConfig, startAddress, value)
    return PlcWriteDirect(plcConfig, value, targetConfig)
}
func PlcWriteDirect(plcConfig *model.DevicePlc, address int, value interface{}) (err error) {
func PlcWriteDirect(plcConfig *model.DevicePlc, value interface{}, c *model.DevicePlcAddress) (err error) {
    lock.Lock()
    defer lock.Unlock()
    var (
@@ -49,34 +45,35 @@
    }()
    if plcConfig.Method == constvar.PlcMethodModbusTCP {
        ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
        err = WriteByModbusTCP(ipAddr, address, value)
        err = WriteByModbusTCP(ipAddr, c.StartAddress, value, c.Endian, c.Length)
        if err != nil {
            logx.Errorf("plc write failed, address: %v, value: %v, err: %v", address, value, err.Error())
            logx.Errorf("plc write failed, address: %v, value: %v, err: %v", c.StartAddress, value, err.Error())
            return err
        }
        logx.Infof("plc write ok, address: %v, value: %v", address, value)
        logx.Infof("plc write ok, address: %v, value: %v", c.StartAddress, value)
    } else if plcConfig.Method == constvar.PlcMethodModbusRTU {
        ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
        err = WriteByModbusRTU(plcConfig, address, value)
        err = WriteByModbusRTU(plcConfig, c.StartAddress, value, c.Endian, c.Length)
        if err != nil {
            logx.Errorf("plc write failed, address: %v, value: %v, err: %v", address, value, err.Error())
            logx.Errorf("plc write failed, address: %v, value: %v, err: %v", c.StartAddress, value, err.Error())
            return err
        }
        logx.Infof("plc write ok, address: %v, value: %v", address, value)
        logx.Infof("plc write ok, address: %v, value: %v", c.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", address)
        label := fmt.Sprintf("D%d", c.StartAddress)
        return plccom.WritePLC(plccom.DeviceTypeMitsubishi, ipAddr, label, cast.ToInt(value))
    }
    return
}
func PlcReadDirect(plcConfig *model.DevicePlc, address int, dataLength int, valueType constvar.PlcStartAddressValueType) (val interface{}, err error) {
func PlcReadDirect(plcConfig *model.DevicePlc, addressConfig *model.DevicePlcAddress) (val interface{}, err error) {
    address, dataLength, endian := addressConfig.StartAddress, addressConfig.Length, addressConfig.Endian
    lock.Lock()
    defer lock.Unlock()
    var (
@@ -89,7 +86,7 @@
        var value []byte
        if plcConfig.Method == constvar.PlcMethodModbusTCP {
            ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
            value, err = ReadByModbusTCP(ipAddr, address, dataLength)
            value, err = ReadByModbusTCP(ipAddr, address, dataLength, endian)
            if err != nil {
                return nil, err
            }
@@ -99,33 +96,7 @@
                return nil, err
            }
        }
        switch valueType {
        case constvar.PlcStartAddressValueTypeString:
            return string(value), nil
        case constvar.PlcStartAddressValueTypeInt32:
            if len(value) == 2 {
                val = binary.BigEndian.Uint16(value)
            } else if len(value) == 4 {
                val = binary.BigEndian.Uint32(value)
            } else {
                logx.Errorf("plc read get an unknown int value: %v, address:%v", value, address)
                return nil, errors.New(fmt.Sprintf("unknown int value:%v", value))
            }
        default:
            if len(value) == 2 {
                val = binary.BigEndian.Uint16(value)
            } else if len(value) == 4 {
                low16Val := uint32(binary.BigEndian.Uint16(value[:2]))
                high16 := uint32(binary.BigEndian.Uint16(value[2:])) << 16
                val = low16Val + high16
            } else {
                logx.Errorf("plc read get an unknown int value: %v, address:%v", value, address)
                return nil, errors.New(fmt.Sprintf("unknown int value:%v", value))
            }
        }
        logx.Infof("plc read ok, address: %v, result: %v, dataLength: %v, value: %v", address, val, dataLength, value)
        return val, nil
        return Convert(value, addressConfig)
    } else if plcConfig.Method == constvar.PlcMethodSerial {
        ipAddr = conf.Conf.Services.Serial
        if ipAddr == "" {
@@ -137,9 +108,15 @@
    return
}
func ReadByModbusTCP(ipAddr string, address, length int) ([]byte, error) {
func ReadByModbusTCP(ipAddr string, address, length int, endian constvar.EndianType) ([]byte, error) {
    if conf.Conf.PLC.Package == constvar.PlcPackageApache {
        return apacheplc4x.ReadHoldingRegister(ipAddr, address, length)
        var intType string
        if endian == constvar.EndianTypeMix {
            intType = "UINT"
        } else {
            intType = "DINT"
        }
        return apacheplc4x.ReadHoldingRegister(ipAddr, address, length, intType)
    } else if conf.Conf.PLC.Package == constvar.PlcPackageApacheLongConnection {
        conn, err := plc.GetModbusConnection(ipAddr)
        if err != nil {
@@ -151,9 +128,23 @@
    }
}
func WriteByModbusTCP(ipAddr string, address int, value any) (err error) {
func WriteByModbusTCP(ipAddr string, address int, value any, endian constvar.EndianType, length int) (err error) {
    if conf.Conf.PLC.Package == constvar.PlcPackageApache {
        _, err = apacheplc4x.WriteHoldingRegister(ipAddr, address, value)
        var intType string
        if endian == constvar.EndianTypeMix {
            intType = "UINT"
            int32Value := cast.ToInt32(value)
            highVal := int32Value / (1 << 16)
            lowValue := int32Value % (1 << 16)
            _, err = apacheplc4x.WriteHoldingRegister(ipAddr, address, lowValue, intType)
            if err != nil {
                return err
            }
            _, err = apacheplc4x.WriteHoldingRegister(ipAddr, address+1, highVal, intType)
        } else {
            intType = "DINT"
            _, err = apacheplc4x.WriteHoldingRegister(ipAddr, address, value, intType)
        }
        return err
    } else if conf.Conf.PLC.Package == constvar.PlcPackageApacheLongConnection {
        conn, err := plc.GetModbusConnection(ipAddr)
@@ -163,7 +154,7 @@
        _, err = plc.WriteHoldingRegister(conn, address, value)
        return err
    } else {
        return modbusx.Write(ipAddr, uint16(address), cast.ToInt(value))
        return modbusx.Write(ipAddr, uint16(address), cast.ToInt(value), string(endian), length)
    }
}
@@ -175,14 +166,10 @@
        StopBit:    plcConfig.StopBit,
        Parity:     plcConfig.Parity,
    }
    if conf.Conf.PLC.Package == constvar.PlcPackageApache {
        return apacheplc4x.ReadHoldingRegisterByRTU(rtuConfig, address, length)
    } else {
        return modbusx.ReadByRTU(rtuConfig, uint16(address), uint16(length))
    }
    return modbusx.ReadByRTU(rtuConfig, uint16(address), uint16(length))
}
func WriteByModbusRTU(plcConfig *model.DevicePlc, address int, value any) (err error) {
func WriteByModbusRTU(plcConfig *model.DevicePlc, address int, value any, endian constvar.EndianType, length int) (err error) {
    rtuConfig := &common.RTUConfig{
        BaudRate:   plcConfig.BaudRate,
        SerialName: plcConfig.SerialName,
@@ -190,12 +177,7 @@
        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))
    }
    return modbusx.WriteByRTU(rtuConfig, uint16(address), cast.ToInt(value), string(endian), length)
}
func PlcIsConnect() bool {
@@ -246,3 +228,42 @@
    }
    return
}
func Convert(rawValue []byte, c *model.DevicePlcAddress) (value interface{}, err error) {
    switch c.Type {
    case constvar.PlcStartAddressValueTypeString:
        return string(rawValue), nil
    case constvar.PlcStartAddressValueTypeInt16:
        if c.Endian == constvar.EndianTypeLittle {
            value = binary.LittleEndian.Uint16(rawValue)
        } else {
            value = binary.BigEndian.Uint16(rawValue)
        }
    default:
        if len(rawValue) == 2 {
            if c.Endian == constvar.EndianTypeLittle {
                value = binary.LittleEndian.Uint16(rawValue)
            } else {
                value = binary.BigEndian.Uint16(rawValue)
            }
        } else if len(rawValue) == 4 {
            if c.Endian == constvar.EndianTypeBig {
                value = binary.BigEndian.Uint32(rawValue)
            } else if c.Endian == constvar.EndianTypeLittle {
                value = binary.LittleEndian.Uint32(rawValue)
            } else {
                highBts := rawValue[2:]
                lowBts := rawValue[:2]
                highVal := uint32(binary.BigEndian.Uint16(highBts)) << 16
                lowVal := uint32(binary.BigEndian.Uint16(lowBts))
                value = highVal + lowVal
            }
        } else {
            logx.Errorf("plc read get an unknown int value: %v, address:%v", rawValue, c.StartAddress)
            return nil, errors.New(fmt.Sprintf("unknown value:%v", rawValue))
        }
    }
    logx.Infof("plc read ok, address: %v, result: %v, value: %v, c:%+v", c.StartAddress, rawValue, value, c)
    return
}