using LB_SmartVisionCommon;
|
using S7.Net;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Text.RegularExpressions;
|
using System.Threading;
|
|
namespace LB_VisionProcesses.Communicators.SiemensS7
|
{
|
public class SiemensLBS7 : BaseCommunicator
|
{
|
private Plc plc;
|
// 默认变量地址
|
public string variable = "DB1.DBD0";
|
// 数据类型
|
private string dataType = "String";
|
|
// 缓存连接参数
|
private string ip = "127.0.0.1";
|
private short rack = 0;
|
private short slot = 1;
|
private CpuType cpuType = CpuType.S71500;
|
|
public SiemensLBS7(string name = "西门子S7") : base(name)
|
{
|
CommunicatorName = name;
|
CommunicatorBrand = CommunicatorBrand.SiemensS7;
|
|
// 初始化默认参数
|
if (!CommunicatorConnections.Contains("地址")) CommunicatorConnections.Add("地址", "192.168.0.1");
|
if (!CommunicatorConnections.Contains("机架号")) CommunicatorConnections.Add("机架号", "0");
|
if (!CommunicatorConnections.Contains("插槽号")) CommunicatorConnections.Add("插槽号", "1");
|
if (!CommunicatorConnections.Contains("型号")) CommunicatorConnections.Add("型号", CpuType.S71500);
|
if (!CommunicatorConnections.Contains("变量地址")) CommunicatorConnections.Add("变量地址", "DB1.DBD0");
|
if (!CommunicatorConnections.Contains("数据类型")) CommunicatorConnections.Add("数据类型", "String");
|
|
// 兼容旧配置 "端口"
|
if (CommunicatorConnections.Contains("端口"))
|
{
|
CommunicatorConnections["插槽号"] = CommunicatorConnections["端口"];
|
}
|
|
// 设置默认心跳消息
|
strHeartbeat = "HEARTBEAT";
|
}
|
|
public override bool Connect()
|
{
|
try
|
{
|
// 更新参数
|
if (CommunicatorConnections.Contains("地址")) ip = CommunicatorConnections["地址"].ToString();
|
|
if (CommunicatorConnections.Contains("机架号"))
|
short.TryParse(CommunicatorConnections["机架号"].ToString(), out rack);
|
|
if (CommunicatorConnections.Contains("插槽号"))
|
short.TryParse(CommunicatorConnections["插槽号"].ToString(), out slot);
|
else if (CommunicatorConnections.Contains("端口"))
|
short.TryParse(CommunicatorConnections["端口"].ToString(), out slot);
|
|
if (CommunicatorConnections.Contains("型号"))
|
{
|
if (CommunicatorConnections["型号"] is CpuType type)
|
cpuType = type;
|
else
|
Enum.TryParse(CommunicatorConnections["型号"].ToString(), out cpuType);
|
}
|
|
if (CommunicatorConnections.Contains("变量地址"))
|
{
|
variable = CommunicatorConnections["变量地址"].ToString();
|
}
|
|
if (CommunicatorConnections.Contains("数据类型"))
|
{
|
dataType = CommunicatorConnections["数据类型"].ToString();
|
}
|
|
// 关闭旧连接
|
plc?.Close();
|
|
plc = new Plc(cpuType, ip, rack, slot);
|
plc.Open();
|
|
if (plc.IsConnected)
|
{
|
bConnected = true;
|
AsyncLogHelper.Info($"Device:[{CommunicatorName}] 已连接到 {ip} 机架:{rack} 插槽:{slot}");
|
return true;
|
}
|
else
|
{
|
bConnected = false;
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] 连接失败: IsConnected 为 false");
|
return false;
|
}
|
}
|
catch (Exception ex)
|
{
|
bConnected = false;
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] 连接错误: {ex.Message}");
|
return false;
|
}
|
}
|
|
public override bool Disconnect()
|
{
|
try
|
{
|
if (plc != null)
|
{
|
plc.Close();
|
bConnected = false;
|
AsyncLogHelper.Info($"Device:[{CommunicatorName}] 已断开连接");
|
}
|
return true;
|
}
|
catch (Exception ex)
|
{
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] 断开连接错误: {ex.Message}");
|
return false;
|
}
|
}
|
|
public override bool SendMessage(string message)
|
{
|
if (plc == null || !plc.IsConnected)
|
{
|
Msg = "连接未开启";
|
return false;
|
}
|
|
if (message == strHeartbeat) return plc.IsConnected;
|
|
try
|
{
|
string targetVar = variable;
|
string strValue = message;
|
|
// 简单的协议解析:地址:值
|
if (message.Contains(":"))
|
{
|
var parts = message.Split(new char[] { ':' }, 2);
|
if (parts.Length == 2 && !string.IsNullOrWhiteSpace(parts[0]))
|
{
|
targetVar = parts[0];
|
strValue = parts[1];
|
}
|
}
|
|
object valueToWrite = strValue;
|
// 获取当前数据类型配置
|
string currentDataType = CommunicatorConnections.Contains("数据类型") ? CommunicatorConnections["数据类型"].ToString() : "String";
|
|
// 根据配置的数据类型进行转换
|
try
|
{
|
switch (currentDataType)
|
{
|
case "Bool":
|
{
|
if (strValue == "1")
|
{
|
valueToWrite = true;
|
}
|
else if (strValue == "0")
|
{
|
valueToWrite = false;
|
}
|
else
|
{
|
valueToWrite = bool.Parse(strValue);
|
}
|
break;
|
}
|
case "Byte":
|
{
|
valueToWrite = byte.Parse(strValue);
|
break;
|
}
|
case "Int": // 16-bit
|
{
|
valueToWrite = short.Parse(strValue);
|
break;
|
}
|
case "DInt": // 32-bit
|
{
|
valueToWrite = int.Parse(strValue);
|
break;
|
}
|
case "Word": // 16-bit unsigned
|
{
|
valueToWrite = ushort.Parse(strValue);
|
break;
|
}
|
case "DWord": // 32-bit unsigned
|
{
|
valueToWrite = uint.Parse(strValue);
|
break;
|
}
|
case "Real": // Float
|
valueToWrite = float.Parse(strValue);
|
break;
|
case "Double": // LReal
|
{
|
valueToWrite = double.Parse(strValue);
|
break;
|
}
|
case "String":
|
default:
|
{
|
valueToWrite = strValue;
|
break;
|
}
|
}
|
}
|
catch (FormatException)
|
{
|
Msg = $"无效的{currentDataType}值,请输入正确格式。";
|
if (currentDataType == "Bool") Msg += " (true/false 或 1/0)";
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] {Msg}");
|
return false;
|
}
|
catch (Exception castEx)
|
{
|
Msg = $"数据转换错误({currentDataType}): {castEx.Message}";
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] {Msg}");
|
return false;
|
}
|
|
// 尝试写入
|
plc.Write(targetVar, valueToWrite);
|
AsyncLogHelper.Info($"Device:[{CommunicatorName}] 写入({currentDataType}) {targetVar} = {valueToWrite}");
|
return true;
|
}
|
catch (Exception ex)
|
{
|
Msg = $"发送消息错误: {ex.Message}";
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] {Msg}");
|
return false;
|
}
|
}
|
|
public override string ReceiveMsg()
|
{
|
if (plc == null || !plc.IsConnected) return string.Empty;
|
|
try
|
{
|
// 获取当前数据类型配置
|
string currentDataType = CommunicatorConnections.Contains("数据类型") ? CommunicatorConnections["数据类型"].ToString() : "String";
|
|
if (currentDataType == "String")
|
{
|
var match = Regex.Match(variable, @"DB(\d+)\.DB[B|W|D|X]?(\d+)", RegexOptions.IgnoreCase);
|
if (match.Success)
|
{
|
try
|
{
|
int db = int.Parse(match.Groups[1].Value);
|
int startByte = int.Parse(match.Groups[2].Value);
|
|
// 读取头部 (MaxLen, ActLen)
|
byte[] header = plc.ReadBytes(DataType.DataBlock, db, startByte, 2);
|
if (header != null && header.Length >= 2)
|
{
|
int actLen = header[1];
|
if (actLen > 0)
|
{
|
// 读取实际字符数据
|
byte[] strBytes = plc.ReadBytes(DataType.DataBlock, db, startByte + 2, actLen);
|
strReceiveMsg = Encoding.ASCII.GetString(strBytes);
|
return strReceiveMsg;
|
}
|
else
|
{
|
strReceiveMsg = string.Empty;
|
return strReceiveMsg;
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] 读取S7String失败: {ex.Message}");
|
}
|
}
|
// 如果正则不匹配或读取失败,回退到默认读取
|
}
|
|
var result = plc.Read(variable);
|
if (result != null)
|
{
|
// 尝试根据 dataType 格式化输出 (S7.Net 读出来的类型可能与预期不符,特别是 DWord/Real)
|
// 例如 DBD0 默认读出来是 UInt32,如果 dataType 是 Real,需要转换
|
if (currentDataType == "Real" && (result is uint || result is int))
|
{
|
byte[] bytes = BitConverter.GetBytes(Convert.ToUInt32(result));
|
float f = BitConverter.ToSingle(bytes, 0);
|
strReceiveMsg = f.ToString();
|
}
|
else
|
{
|
strReceiveMsg = result.ToString();
|
}
|
return strReceiveMsg;
|
}
|
}
|
catch (Exception ex)
|
{
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}] 接收消息错误: {ex.Message}");
|
}
|
return string.Empty;
|
}
|
|
/// <summary>
|
/// 带填充的 S7 字符串写入,防止残留数据
|
/// </summary>
|
private bool WriteS7StringWithPadding(string address, string value)
|
{
|
try
|
{
|
// 解析地址,例如 DB1.DBB0, DB1.DBD0
|
var match = Regex.Match(address, @"DB(\d+)\.DB[B|W|D|X]?(\d+)", RegexOptions.IgnoreCase);
|
if (!match.Success) return false;
|
|
int db = int.Parse(match.Groups[1].Value);
|
int startByte = int.Parse(match.Groups[2].Value);
|
|
byte maxLen = 254; // 默认最大值
|
try
|
{
|
var header = plc.ReadBytes(DataType.DataBlock, db, startByte, 1);
|
if (header != null && header.Length > 0 && header[0] > 0)
|
{
|
maxLen = header[0];
|
}
|
}
|
catch { }
|
|
byte[] buffer = new byte[maxLen + 2];
|
buffer[0] = maxLen;
|
int currentLen = Math.Min(value.Length, maxLen);
|
buffer[1] = (byte)currentLen;
|
|
if (currentLen > 0)
|
{
|
byte[] strBytes = Encoding.ASCII.GetBytes(value);
|
Array.Copy(strBytes, 0, buffer, 2, Math.Min(strBytes.Length, currentLen));
|
}
|
|
plc.WriteBytes(DataType.DataBlock, db, startByte, buffer);
|
return true;
|
}
|
catch
|
{
|
return false;
|
}
|
}
|
|
public object Read(string address)
|
{
|
if (plc == null || !plc.IsConnected)
|
{
|
return null;
|
}
|
return plc.Read(address);
|
}
|
|
public void Write(string address, object value)
|
{
|
if (plc != null && plc.IsConnected)
|
{
|
plc.Write(address, value);
|
}
|
}
|
|
public override void Dispose()
|
{
|
try
|
{
|
AsyncLogHelper.Info($"Device:[{CommunicatorName}],释放资源(Dispose)");
|
plc?.Close();
|
plc = null;
|
GC.SuppressFinalize(this);
|
}
|
catch (Exception ex)
|
{
|
AsyncLogHelper.Error($"Device:[{CommunicatorName}],释放资源(Dispose)错误: " + ex.Message);
|
}
|
}
|
|
public override bool SendMessage(byte[] message)
|
{
|
throw new NotImplementedException();
|
}
|
}
|
}
|