| | |
| | | using System; |
| | | using LB_SmartVisionCommon; |
| | | using S7.Net; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using System.Text.RegularExpressions; |
| | | using System.Threading; |
| | | |
| | | namespace LB_VisionProcesses.Communicators.SiemensS7 |
| | | { |
| | | internal class SiemensLBS7 |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | } |