using DocumentFormat.OpenXml.Bibliography;
|
using DocumentFormat.OpenXml.Drawing;
|
using Elastic.Clients.Elasticsearch;
|
using Furion.Logging;
|
using IoTClient;
|
using IoTClient.Clients.Modbus;
|
using IoTClient.Clients.PLC;
|
using IoTClient.Common.Enums;
|
using IoTClient.Enums;
|
using IoTClient.Interfaces;
|
using System.Text;
|
using System.Text.RegularExpressions;
|
|
namespace WCS.Application;
|
public class PLCUtil
|
{
|
public readonly long PlcId;
|
public readonly string PlcIP;
|
private SiemensClient _client;
|
private WcsPlc _modPlc;
|
private readonly object OLock = new object();
|
public PLCUtil(WcsPlc modPlc)
|
{
|
PlcId = modPlc.Id;
|
PlcIP = modPlc.IP;
|
_modPlc = modPlc;
|
_client = new SiemensClient((SiemensVersion)modPlc.PLCType, modPlc.IP, modPlc.Port);
|
_client.Open();
|
}
|
public bool Connected
|
{
|
get { return _client.Connected; }
|
}
|
public IoTClient.Result Open()
|
{
|
return _client.Open();
|
}
|
public IoTClient.Result Close()
|
{
|
return _client.Close();
|
}
|
/// <summary>
|
/// 读取PLC值
|
/// </summary>
|
/// <param name="DbNumber">DB区指定值</param>
|
/// <param name="PosType">字符类型</param>
|
/// <param name="Pos">偏移量/地址</param>
|
/// <param name="Length">长度(字符串)</param>
|
/// <returns></returns>
|
public (IoTClient.Result, dynamic value) GetPlcDBValue(PLCDataTypeEnum PosType, string DbNumber, string Pos, int? Length = 0)
|
{
|
lock (OLock)
|
{
|
string address;
|
if (DbNumber.StartsWith("DB") || PosType == PLCDataTypeEnum.Bit)
|
address = DbNumber + "." + Pos;
|
else
|
address = DbNumber + Pos;
|
return this.GetPlcDBValue(PosType, address, Length);
|
}
|
}
|
/// <summary>
|
/// 读取PLC值
|
/// </summary>
|
/// <param name="PosType">字符类型</param>
|
/// <param name="Pos">偏移量/地址</param>
|
/// <param name="Length">长度(字符串)</param>
|
/// <returns></returns>
|
public (IoTClient.Result, dynamic value) GetPlcDBValue(PLCDataTypeEnum PosType, string Pos, int? Length = 0)
|
{
|
lock (OLock)
|
{
|
dynamic result = null;
|
switch (PosType)
|
{
|
case PLCDataTypeEnum.Bit:
|
result = _client.ReadBoolean(Pos);
|
break;
|
case PLCDataTypeEnum.Byte:
|
result = _client.ReadByte(Pos);
|
break;
|
case PLCDataTypeEnum.Short:
|
result = _client.ReadInt16(Pos);
|
break;
|
case PLCDataTypeEnum.UShort:
|
result = _client.ReadUInt16(Pos);
|
break;
|
case PLCDataTypeEnum.Int:
|
result = _client.ReadInt32(Pos);
|
break;
|
case PLCDataTypeEnum.UInt:
|
result = _client.ReadUInt32(Pos);
|
break;
|
case PLCDataTypeEnum.Long:
|
result = _client.ReadInt64(Pos);
|
break;
|
case PLCDataTypeEnum.ULong:
|
result = _client.ReadUInt64(Pos);
|
break;
|
case PLCDataTypeEnum.Float:
|
result = _client.ReadFloat(Pos);
|
break;
|
case PLCDataTypeEnum.Double:
|
result = _client.ReadDouble(Pos);
|
break;
|
case PLCDataTypeEnum.String:
|
{
|
string dpos = IncrementCode(Pos);
|
Result<byte[]> result1 = _client.ReadString(Pos, 1);
|
if (result1.IsSucceed)
|
{
|
Result<byte[]> result2 = _client.ReadString(dpos, (ushort)(result1.Value[0]));
|
Result<string> result3 = new Result<string>(result2);
|
if (result3.IsSucceed)
|
{
|
result3.Value = Encoding.ASCII.GetString(result2.Value, 0, result1.Value[0]).Replace("\0", "");
|
result = result3;
|
}
|
}
|
else
|
{
|
result = result1;
|
}
|
}
|
break;
|
default:
|
result = new IoTClient.Result<object>();
|
break;
|
}
|
return (result, result.Value);
|
}
|
}
|
public static string IncrementCode(string code, int index = 2)
|
{
|
// 使用正则表达式解析字符串
|
var match = Regex.Match(code, @"([A-Za-z]+)(\d+)(?:\.(\d+))?");
|
|
if (match.Success)
|
{
|
// 提取字母部分
|
string prefix = match.Groups[1].Value;
|
// 提取数字部分
|
int number1 = int.Parse(match.Groups[2].Value);
|
int number2 = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 0;
|
|
// 处理规则
|
if (match.Groups[3].Success)
|
{
|
// 如果有小数点分隔部分,增加2
|
number2 += index;
|
return $"{prefix}{number1}.{number2:D3}";
|
}
|
else
|
{
|
// 没有小数点分隔部分的,增加2
|
number1 += index;
|
return $"{prefix}{number1}";
|
}
|
}
|
|
// 如果不匹配,返回原字符串
|
return code;
|
}
|
/// <summary>
|
/// 批量读取PLC值
|
/// </summary>
|
/// <returns></returns>
|
public Result<Dictionary<string, object>> GetPlcBatchDBValue(Dictionary<string, PLCDataTypeEnum> listaddress)
|
{
|
lock (OLock)
|
{
|
Dictionary<string, DataTypeEnum> addresses = new Dictionary<string, DataTypeEnum>();
|
foreach (var address in listaddress)
|
{
|
switch (address.Value)
|
{
|
case PLCDataTypeEnum.Bit:
|
addresses.Add(address.Key, DataTypeEnum.Bool);
|
break;
|
case PLCDataTypeEnum.Byte:
|
addresses.Add(address.Key, DataTypeEnum.Byte);
|
break;
|
case PLCDataTypeEnum.Short:
|
addresses.Add(address.Key, DataTypeEnum.Int16);
|
break;
|
case PLCDataTypeEnum.UShort:
|
addresses.Add(address.Key, DataTypeEnum.UInt16);
|
break;
|
case PLCDataTypeEnum.Int:
|
addresses.Add(address.Key, DataTypeEnum.Int32);
|
break;
|
case PLCDataTypeEnum.UInt:
|
addresses.Add(address.Key, DataTypeEnum.UInt32);
|
break;
|
case PLCDataTypeEnum.Long:
|
addresses.Add(address.Key, DataTypeEnum.Int64);
|
break;
|
case PLCDataTypeEnum.ULong:
|
addresses.Add(address.Key, DataTypeEnum.UInt64);
|
break;
|
case PLCDataTypeEnum.Float:
|
addresses.Add(address.Key, DataTypeEnum.Float);
|
break;
|
case PLCDataTypeEnum.Double:
|
addresses.Add(address.Key, DataTypeEnum.Double);
|
break;
|
case PLCDataTypeEnum.String:
|
addresses.Add(address.Key, DataTypeEnum.String);
|
break;
|
default:
|
break;
|
}
|
}
|
return _client.BatchRead(addresses);
|
}
|
}
|
/// <summary>
|
/// 写入PLC值
|
/// </summary>
|
public IoTClient.Result SetPlcDBValue(PLCDataTypeEnum PosType, string DbNumber, string Pos, string Value)
|
{
|
string address;
|
if (DbNumber.StartsWith("DB") || PosType == PLCDataTypeEnum.Bit)
|
address = DbNumber + "." + Pos;
|
else
|
address = DbNumber + Pos;
|
return this.SetPlcDBValue(PosType, address, Value);
|
}
|
/// <summary>
|
/// 写入PLC值
|
/// </summary>
|
public IoTClient.Result SetPlcDBValue(PLCDataTypeEnum PosType, string Pos, string Value)
|
{
|
switch (PosType)
|
{
|
case PLCDataTypeEnum.Bit:
|
if (!bool.TryParse(Value, out bool bit))
|
{
|
if (Value == "0")
|
bit = false;
|
else if (Value == "1")
|
bit = true;
|
else
|
{
|
throw new Exception("写入值错误");
|
}
|
}
|
return _client.Write(Pos, bit);
|
case PLCDataTypeEnum.Byte:
|
return _client.Write(Pos, byte.Parse(Value));
|
case PLCDataTypeEnum.Short:
|
return _client.Write(Pos, short.Parse(Value));
|
case PLCDataTypeEnum.UShort:
|
return _client.Write(Pos, ushort.Parse(Value));
|
case PLCDataTypeEnum.Int:
|
return _client.Write(Pos, int.Parse(Value));
|
case PLCDataTypeEnum.UInt:
|
return _client.Write(Pos, uint.Parse(Value));
|
case PLCDataTypeEnum.Long:
|
return _client.Write(Pos, long.Parse(Value));
|
case PLCDataTypeEnum.ULong:
|
return _client.Write(Pos, ulong.Parse(Value));
|
case PLCDataTypeEnum.Float:
|
return _client.Write(Pos, float.Parse(Value));
|
case PLCDataTypeEnum.Double:
|
return _client.Write(Pos, float.Parse(Value));
|
case PLCDataTypeEnum.String:
|
{
|
string dpos = IncrementCode(Pos, 1);
|
return _client.Write(dpos, Value);
|
}
|
default:
|
return new IoTClient.Result();
|
}
|
}
|
/// <summary>
|
/// 写入PLC值
|
/// </summary>
|
public IoTClient.Result SetPlcBatchDBValue(Dictionary<string, KeyValuePair<string, PLCDataTypeEnum>> listaddress)
|
{
|
Dictionary<string, object> addresses = new Dictionary<string, object>();
|
foreach (var address in listaddress)
|
{
|
switch (address.Value.Value)
|
{
|
case PLCDataTypeEnum.Bit:
|
if (!bool.TryParse(address.Value.Key, out bool bit))
|
{
|
if (address.Value.Key == "0")
|
bit = false;
|
else if (address.Value.Key == "1")
|
bit = true;
|
else
|
{
|
throw new Exception("写入值错误");
|
}
|
}
|
addresses.Add(address.Key, bit);
|
break;
|
case PLCDataTypeEnum.Byte:
|
addresses.Add(address.Key, byte.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.Short:
|
addresses.Add(address.Key, short.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.UShort:
|
addresses.Add(address.Key, ushort.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.Int:
|
addresses.Add(address.Key, int.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.UInt:
|
addresses.Add(address.Key, uint.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.Long:
|
addresses.Add(address.Key, long.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.ULong:
|
addresses.Add(address.Key, ulong.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.Float:
|
addresses.Add(address.Key, float.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.Double:
|
addresses.Add(address.Key, double.Parse(address.Value.Key));
|
break;
|
case PLCDataTypeEnum.String:
|
addresses.Add(address.Key, address.Value.Key);
|
break;
|
default:
|
break;
|
}
|
}
|
return _client.BatchWrite(addresses);
|
}
|
|
/// <summary>
|
/// 循环写入PLC值(并读取值判断和写入的值是否一致,写入控制字不能用此方法)
|
/// </summary>
|
public IoTClient.Result SetPlcDBValueRepeat(PLCDataTypeEnum PosType, string DbNumber, string Pos, string Value)
|
{
|
IoTClient.Result _result = new IoTClient.Result();
|
|
int setCount = 0;//写入次数
|
while (setCount < 5)
|
{
|
string address;
|
if (DbNumber.StartsWith("DB"))
|
address = DbNumber + "." + Pos;
|
else
|
address = DbNumber + Pos;
|
_result = this.SetPlcDBValue(PosType, address, Value);//写入值
|
|
//累计写入次数
|
setCount++;
|
|
if (_result.IsSucceed)
|
{
|
//读取写入的值
|
var (res, val) = GetPlcDBValue(PosType, DbNumber, Pos);
|
if (val.ToString() == Value)
|
{
|
//读取的值和写入的值一致,直接跳出循环
|
break;
|
}
|
}
|
}
|
//写入日志
|
Log.Information($"66666666写入PLC,DbNumber:{DbNumber},Pos:{Pos},Value:{Value},setCount:{setCount}");
|
|
return _result;
|
}
|
|
|
}
|