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/atomic"
|
)
|
|
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++
|
|
for _, pc := range plcConfig.Details {
|
if pc.FieldName == fieldType && pc.Channel == channel {
|
startAddress = pc.StartAddress
|
}
|
}
|
return PlcWriteDirect(plcConfig, startAddress, value)
|
}
|
|
func PlcWriteDirect(plcConfig *model.DevicePlc, address int, value interface{}) (err error) {
|
var (
|
ipAddr string
|
)
|
defer dealErr(err)
|
if plcConfig.Method == constvar.PlcMethodModbusTCP {
|
ipAddr = fmt.Sprintf("%s:%v", plcConfig.Address, plcConfig.Port)
|
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())
|
return err
|
}
|
logx.Infof("plc write ok, address: %v, value: %v", address, 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)
|
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) {
|
var (
|
ipAddr string
|
)
|
defer 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)
|
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
|
case constvar.PlcStartAddressValueTypeInt:
|
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, valueLength: %v", address, val, dataLength, len(value))
|
return val, nil
|
} 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)
|
}
|
return
|
}
|
|
func ReadByModbusTCP(ipAddr string, address, length int) ([]byte, error) {
|
if conf.Conf.PLC.Package == constvar.PlcPackageApache {
|
newLength := length / 2
|
if newLength == 0 {
|
newLength = 1
|
}
|
return apacheplc4x.ReadHoldingRegister(ipAddr, address, newLength)
|
} else if conf.Conf.PLC.Package == constvar.PlcPackageApacheLongConnection {
|
conn, err := plc.GetModbusConnection(ipAddr)
|
if err != nil {
|
return nil, err
|
}
|
newLength := length / 2
|
if newLength == 0 {
|
newLength = 1
|
}
|
return plc.ReadHoldingRegister(conn, address, newLength)
|
} else {
|
return modbusx.Read(ipAddr, uint16(address), uint16(length))
|
}
|
}
|
|
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
|
} 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))
|
}
|
}
|
|
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()
|
}
|
|
func dealErr(err error) {
|
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
|
}
|