package service
|
|
import (
|
"apsClient/conf"
|
"apsClient/constvar"
|
"apsClient/model"
|
"apsClient/model/common"
|
"apsClient/pkg/logx"
|
"apsClient/pkg/plc"
|
"apsClient/pkg/plc/apacheplc4x"
|
"apsClient/pkg/plc/modbusx"
|
"apsClient/pkg/plccom"
|
"encoding/binary"
|
"errors"
|
"fmt"
|
"github.com/spf13/cast"
|
"sync"
|
)
|
|
// 串口不支持并行读写,所以需要加个锁,防止timeout和资源不可用报错
|
var lock sync.Mutex
|
|
func PlcWrite(plcConfig *model.DevicePlc, fieldType constvar.PlcStartAddressType, channel int32, value interface{}) (err error) {
|
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 {
|
targetConfig = pc
|
}
|
}
|
return PlcWriteDirect(plcConfig, value, targetConfig)
|
}
|
|
func PlcWriteDirect(plcConfig *model.DevicePlc, value interface{}, c *model.DevicePlcAddress) (err error) {
|
lock.Lock()
|
defer lock.Unlock()
|
var (
|
ipAddr string
|
)
|
defer func() {
|
dealErr(err)
|
}()
|
if plcConfig.Method == constvar.PlcMethodModbusTCP {
|
ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
|
err = WriteByModbusTCP(ipAddr, c.StartAddress, value, c.Endian, c.Length)
|
|
if err != nil {
|
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", c.StartAddress, value)
|
} else if plcConfig.Method == constvar.PlcMethodModbusRTU {
|
ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
|
err = WriteByModbusRTU(plcConfig, c.StartAddress, value, c.Endian, c.Length)
|
|
if err != nil {
|
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", 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", c.StartAddress)
|
return plccom.WritePLC(plccom.DeviceTypeMitsubishi, ipAddr, label, cast.ToInt(value))
|
}
|
return
|
}
|
|
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 (
|
ipAddr string
|
)
|
defer func() {
|
dealErr(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, endian)
|
if err != nil {
|
return nil, err
|
}
|
} else {
|
value, err = ReadByModbusRTU(plcConfig, address, dataLength)
|
if err != nil {
|
return nil, err
|
}
|
}
|
return Convert(value, addressConfig)
|
} 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", address)
|
return plccom.ReadPLC(plccom.DeviceTypeMitsubishi, ipAddr, label, dataLength)
|
}
|
return
|
}
|
|
func ReadByModbusTCP(ipAddr string, address, length int, endian constvar.EndianType) ([]byte, error) {
|
if conf.Conf.PLC.Package == constvar.PlcPackageApache {
|
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 {
|
return nil, err
|
}
|
return plc.ReadHoldingRegister(conn, address, length)
|
} else {
|
return modbusx.Read(ipAddr, uint16(address), uint16(length))
|
}
|
}
|
|
func WriteByModbusTCP(ipAddr string, address int, value any, endian constvar.EndianType, length int) (err error) {
|
if conf.Conf.PLC.Package == constvar.PlcPackageApache {
|
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)
|
if err != nil {
|
return err
|
}
|
_, err = plc.WriteHoldingRegister(conn, address, value)
|
return err
|
} else {
|
return modbusx.Write(ipAddr, uint16(address), cast.ToInt(value), string(endian), length)
|
}
|
}
|
|
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,
|
}
|
return modbusx.ReadByRTU(rtuConfig, uint16(address), uint16(length))
|
}
|
|
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,
|
DataBit: plcConfig.DataBit,
|
StopBit: plcConfig.StopBit,
|
Parity: plcConfig.Parity,
|
}
|
return modbusx.WriteByRTU(rtuConfig, uint16(address), cast.ToInt(value), string(endian), length)
|
}
|
|
func PlcIsConnect() bool {
|
return IsConnect()
|
}
|
|
func dealErr(err error) {
|
if err != nil {
|
FailureRemainingOpportunitiesDecr() //减少失败剩余机会
|
} else {
|
FailureRemainingOpportunitiesReset() //重置失败剩余机会
|
}
|
}
|
|
var connectionStatus sync.Map
|
|
const (
|
defaultFailureRemainingOpportunities = 20
|
)
|
|
func IsConnect() bool {
|
val, ok := connectionStatus.Load(conf.Conf.CurrentDeviceID)
|
if !ok {
|
return false
|
}
|
failureRemainingOpportunities := val.(int)
|
return failureRemainingOpportunities > 0
|
}
|
|
func FailureRemainingOpportunitiesDecr() {
|
val, ok := connectionStatus.Load(conf.Conf.CurrentDeviceID)
|
if !ok {
|
return
|
}
|
failureRemainingOpportunities := val.(int)
|
if failureRemainingOpportunities > 0 {
|
failureRemainingOpportunities--
|
}
|
connectionStatus.Store(conf.Conf.CurrentDeviceID, failureRemainingOpportunities)
|
return
|
}
|
|
func FailureRemainingOpportunitiesReset() {
|
val, ok := connectionStatus.Load(conf.Conf.CurrentDeviceID)
|
if !ok || val.(int) < defaultFailureRemainingOpportunities {
|
connectionStatus.Store(conf.Conf.CurrentDeviceID, defaultFailureRemainingOpportunities)
|
return
|
}
|
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
|
}
|