using HslCommunication; using HslCommunication.LogNet; using HslCommunication.Profinet.Siemens; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading; namespace WIDESEA_WCS.WCSClient.Siemens { /// /// 西门子PLC /// public class SiemensPLCClient { ILogNet logNet; /// /// 西门子PLC连接 /// public SiemensS7Net SiemensS7NetClient { get; set; } /// /// PLC连接对应的地址 /// public List PLCDBItems { get; set; } /// /// 连接名称 /// public string PLCName { get; set; } /// /// IP地址 /// public string PLCIPAddress { get; set; } /// /// 连接说明 /// public string PLCDescroption { get; set; } /// /// 端口号(默认102) /// public int Port { get; set; } = 102; /// /// /// public byte Rack { get; set; } = 0; /// /// /// public byte Slot { get; set; } = 0; /// /// 是否已连接 /// public bool IsConnected { get; set; } = false; /// /// 连接信息 /// public string ConnectionMessage { get; set; } /// /// 构造方法,实例化连接对象及PLC连接对应的地址 /// /// public SiemensPLCClient(SiemensPLCS siemensPLCS = SiemensPLCS.S1200) { this.SiemensS7NetClient = new SiemensS7Net(siemensPLCS); PLCDBItems = new List(); } /// /// PLC连接内置线程 /// Thread Thread { get; set; } /// /// 内置线程是否运行 /// bool isRun { get; set; } #region 内置线程 PING IP /// /// 内置线程 PING IP /// public void Start(Action action) { Thread = new Thread(() => { while (isRun) { if (isRun) { //IPStatus status = this.SiemensS7NetClient.IpAddressPing(); IPStatus status = IPStatus.Success; if (status == IPStatus.Success) { ConnectionMessage = status.ToString(); if (action != null) action(); } //if (false) //{ //} else { IsConnected = false; //WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}连接连接掉线,200ms后自动重连", PLCName); OperateResult result = this.SiemensS7NetClient.ConnectServer(); if (result.IsSuccess) IsConnected = true; } Thread.Sleep(100); } } }); Thread.Start(); } #endregion #region 连接PLC,将短连接切换成长连接模式 /// /// 连接PLC /// public string Connect() { this.SiemensS7NetClient.IpAddress = PLCIPAddress; this.SiemensS7NetClient.Port = Port; this.SiemensS7NetClient.Rack = Rack; this.SiemensS7NetClient.Slot = Slot; this.SiemensS7NetClient.ConnectTimeOut = 2000; this.SiemensS7NetClient.ReceiveTimeOut = 2000; this.SiemensS7NetClient?.ConnectClose(); logNet = new LogNetFileSize(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log2"), 2 * 1024 * 1024); OperateResult result = this.SiemensS7NetClient.ConnectServer(); isRun = true; if (!result.IsSuccess) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}连接失败---{DateTime.Now}{Environment.NewLine}错误信息:{result.Message}", PLCName); IsConnected = false; logNet.WriteInfo($"{PLCIPAddress}连接失败,错误信息:{result.Message}"); return $"连接失败,错误信息:{result.Message}"; } else { IsConnected = true; WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{PLCName}连接成功!---{DateTime.Now}", PLCName); logNet.WriteInfo($"{PLCIPAddress}连接成功"); return $"连接成功"; } } #endregion #region 断开与PLC的连接,将长连接切换成短连接模式 /// /// 断开与PLC的连接 /// public void Disconnect() { this.SiemensS7NetClient?.ConnectClose(); isRun = false; WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:已断开与{PLCName}的连接!", PLCName); } #endregion #region 根据名称/描述读取PLC数据 /// /// 根据名称/描述读取PLC数据 /// /// 名称/描述(数据库中plcdetail_name列) /// public virtual object ReadValue(string itemName) { lock (this.SiemensS7NetClient) { object result = null; DBItemGroup item = this.PLCDBItems.Where(x => x.ItemName == itemName).FirstOrDefault(); if (item == null) throw new Exception($"未定义读取指令{itemName}"); string itemAddress = item.ItemAddress; string itemDatatype = item.ItemDataType; try { result = Read(itemAddress, itemDatatype); } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{Environment.NewLine}{PLCName}读取数据失败{Environment.NewLine}数据类型:{item.ItemDataType},{item.Remark}({itemAddress}):{Environment.NewLine}错误信息:{ex.Message}", PLCName); throw new Exception("读取数据失败"); } //try //{ // WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{Environment.NewLine}{PLCName}读取数据{Environment.NewLine}数据类型:{item.ItemDataType}{Environment.NewLine}{item.Remark}({itemAddress}):" + result, PLCName); //} //catch //{ //} return result; } } /// /// 根据名称/描述读取PLC数据 /// /// 名称/描述(数据库中plcdetail_name列) /// public virtual object ReadDBValue(string address, int len) { lock (this.SiemensS7NetClient) { object result = null; string itemAddress = address; string itemDatatype = "byte"; try { result = Read(itemAddress, itemDatatype, len); } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{Environment.NewLine}{PLCName}读取数据失败{Environment.NewLine}数据类型:{address},{address}({itemAddress}):{Environment.NewLine}错误信息:{ex.Message}", PLCName); throw new Exception("读取数据失败"); } return result; } } #endregion #region 根据PLCDBItem读取PLC数据 /// /// 根据PLCDBItem读取PLC数据 /// /// /// public virtual object ReadValue(DBItemGroup item) { lock (this.SiemensS7NetClient) { object result = null; if (item == null) return -1; string itemAddress = item.ItemAddress; string itemDatatype = item.ItemDataType; try { result = Read(itemAddress, itemDatatype); } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{Environment.NewLine}{PLCName}读取数据失败{Environment.NewLine}数据类型:{item.ItemDataType},{item.Remark}({itemAddress}):{Environment.NewLine}错误信息:{ex.Message}", PLCName); } try { //WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{Environment.NewLine}{PLCName}读取数据{Environment.NewLine}数据类型:{item.ItemDataType}{Environment.NewLine}{item.Remark}({itemAddress}):" + result, PLCName); } catch { } return result; } } #endregion #region 根据设备编号读取PLC数据 /// /// 根据设备编号读取PLC数据 /// /// 设备编号 /// json字符串 public object ReadValues(string equipNum) { lock (this.SiemensS7NetClient) { object result = null; Dictionary keyValuePairs = new Dictionary(); List items = this.PLCDBItems.Where(x => x.EquipNum == equipNum).ToList(); for (int i = 0; i < items.Count(); i++) { keyValuePairs.Add(items[i].ItemName, ReadValue(items[i].ItemName)); } result = JsonConvert.SerializeObject(keyValuePairs); return result; } } #endregion #region 根据名称/描述读取PLC数据返回设备编号和值的JSON字符串 /// /// 根据名称/描述读取PLC数据返回设备编号和值的JSON字符串 /// /// 名称/描述(数据库中plcdetail_name列) /// json字符串 public string ReadValuesByItemName(string itemName) { lock (this.SiemensS7NetClient) { string result = null; Dictionary keyValuePairs = new Dictionary(); List items = this.PLCDBItems.Where(x => x.ItemName == itemName).ToList(); for (int i = 0; i < items.Count(); i++) { keyValuePairs.Add(items[i].EquipNum, ReadValue(items[i])); } result = JsonConvert.SerializeObject(keyValuePairs); return result; } } #endregion #region 根据名称/描述和设备编号读取PLC数据 /// /// 根据名称/描述和设备编号读取PLC数据 /// /// 名称/描述(数据库中plcdetail_name列) /// 设备编号 /// public virtual object ReadValue(string itemName, string equipNum) { lock (this.SiemensS7NetClient) { object result = null; DBItemGroup item = this.PLCDBItems.Where(x => x.ItemName == itemName && x.EquipNum == equipNum).FirstOrDefault(); if (item == null) { try { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{Environment.NewLine}{PLCName}读取数据{Environment.NewLine}{Environment.NewLine}({equipNum}):未在协议找到编号{equipNum}的地址", PLCName); } catch { } return -1; } string itemAddress = item.ItemAddress; string itemDatatype = item.ItemDataType; try { result = Read(itemAddress, itemDatatype); } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{Environment.NewLine}{PLCName}读取数据失败{Environment.NewLine}数据类型:{item.ItemDataType},{item.Remark}({itemAddress}):{Environment.NewLine}错误信息:{ex.Message}", PLCName); } try { //WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{Environment.NewLine}{PLCName}读取数据{Environment.NewLine}数据类型:{item.ItemDataType}{Environment.NewLine}{item.Remark}({itemAddress}):" + result, PLCName); } catch { } return result; } } #endregion #region 将数据写入到PLC中 /// /// 将数据写入到PLC中 /// /// 名称/描述(数据库中plcdetail_name列) /// 写入的值 /// public virtual bool WriteValue(string itemName, object value) { lock (this.SiemensS7NetClient) { OperateResult operateResult = new OperateResult(); ; bool result = false; string writeResult = ""; DBItemGroup item = this.PLCDBItems.Where(x => x.ItemName == itemName).FirstOrDefault(); if (item == null) throw new Exception($"未定义写入指令{itemName}"); string itemAddress = item.ItemAddress; string itemDatatype = item.ItemDataType; try { operateResult = Write(itemAddress, itemDatatype, value); result = operateResult.IsSuccess; writeResult = result ? "成功" : $"失败:{operateResult.Message}"; if (!result) { throw new Exception(operateResult.Message); } } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}写入数据失败{Environment.NewLine}数据类型:{item.ItemDataType}{Environment.NewLine}{item.Remark}({itemAddress}):{Environment.NewLine}错误信息:{ex.Message}", PLCName); } return result; } } public virtual bool WriteDBValue(string address, object value) { lock (this.SiemensS7NetClient) { OperateResult operateResult = new OperateResult(); ; bool result = false; string writeResult = ""; string itemAddress = address; string itemDatatype = "byte"; try { operateResult = Write(itemAddress, itemDatatype, value); result = operateResult.IsSuccess; writeResult = result ? "成功" : $"失败:{operateResult.Message}"; if (!result) { throw new Exception(operateResult.Message); } } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}写入数据失败{Environment.NewLine}{address}({itemAddress}):{Environment.NewLine}错误信息:{ex.Message}", PLCName); } return result; } } #endregion #region 将数据写入到PLC中 /// /// 将数据写入到PLC中 /// /// 名称/描述(数据库中plcdetail_name列) /// 写入的值 /// public virtual bool WriteValue(string itemName, string equipNum, object value) { lock (this.SiemensS7NetClient) { OperateResult operateResult = new OperateResult(); ; bool result = false; string writeResult = ""; DBItemGroup item = this.PLCDBItems.Where(x => x.ItemName == itemName && x.EquipNum == equipNum).FirstOrDefault(); if (item == null) return false; string itemAddress = item.ItemAddress; string itemDatatype = item.ItemDataType; try { operateResult = Write(itemAddress, itemDatatype, value); result = operateResult.IsSuccess; writeResult = result ? "成功" : $"失败:{operateResult.Message}"; } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}写入数据失败{Environment.NewLine}数据类型:{item.ItemDataType}{Environment.NewLine}{item.Remark}({itemAddress}):{Environment.NewLine}错误信息:{ex.Message}", PLCName); } try { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{PLCName}写入数据{writeResult}{Environment.NewLine}数据类型:{item.ItemDataType}{Environment.NewLine}{item.Remark}({itemAddress}):" + value, PLCName); } catch { } return result; } } #endregion #region 批量写入数据 /// /// 批量写入数据(数据类型必须一致) /// /// DB块起始地址 /// 数据类型 /// 写入的值 /// public OperateResult WriteArray(string adress, string type, object value) { OperateResult result = new OperateResult(); try { switch (type.ToLower()) { case "int": result = this.SiemensS7NetClient.Write(adress, value as ushort[]); break; case "dint": result = this.SiemensS7NetClient.Write(adress, value as int[]); break; case "char": string temp = value.ToString(); byte[] writeData = Encoding.Default.GetBytes(temp); result = this.SiemensS7NetClient.Write(adress, writeData); break; case "bool": result = this.SiemensS7NetClient.Write(adress, value as bool[]); break; } } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}写入数据失败{Environment.NewLine}数据类型:{type}{Environment.NewLine}{adress}:{Environment.NewLine}错误信息:{ex.Message}", PLCName); } return result; } #endregion #region 批量读取数据 /// /// 批量读取数据(数据类型必须一致) /// /// DB块起始地址 /// 数据类型 /// 读取长度 /// public object ReadArray(string adress, string type, int length) { object result = -1; ; try { switch (type.ToLower()) { case "int": result = this.SiemensS7NetClient.ReadUInt16(adress, Convert.ToUInt16(length)).Content; break; case "dint": result = this.SiemensS7NetClient.ReadUInt32(adress, Convert.ToUInt16(length)).Content; break; case "char": byte[] temps = this.SiemensS7NetClient.Read(adress, Convert.ToUInt16(length)).Content; result = Encoding.Default.GetString(temps).ToCharArray(); break; case "bool": result = this.SiemensS7NetClient.ReadBool(adress, Convert.ToUInt16(length)).Content; break; case "byte": break; } } catch (Exception ex) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{Environment.NewLine}{PLCName}读取数据失败{Environment.NewLine}数据类型:{type},{adress}:{Environment.NewLine}错误信息:{ex.Message}", PLCName); } return result; } #endregion #region 根据DB地址及类型读取数据,最根本读取方法 public T Read(string address, int len = 1) { var Type = typeof(T); if (Type == typeof(bool)) { return (T)GetContent(SiemensS7NetClient.ReadBool(address)); } else if (Type == typeof(ushort)) { return (T)GetContent(SiemensS7NetClient.ReadUInt16(address)); } else if (Type == typeof(byte[])) { return (T)GetContent(SiemensS7NetClient.Read(address, (ushort)len)); } else if (Type == typeof(float)) { return (T)GetContent(SiemensS7NetClient.ReadFloat(address)); } else if (Type == typeof(string)) { return (T)GetContent(SiemensS7NetClient.ReadString(address, (ushort)len)); } else { throw new Exception($"类型{typeof(T)},未定义!"); } } /// /// 根据DB地址及类型读取数据 /// /// DB地址(DB块+.+偏移量) /// 数据类型(int/dint/string/bool) /// public object Read(string adress, string type, int len = 1) { object result = -1; try { switch (type.ToLower()) { case "int": result = GetContent(this.SiemensS7NetClient.ReadUInt16(adress)); break; case "dint": result = GetContent(this.SiemensS7NetClient.ReadUInt32(adress)); break; case "string": OperateResult operateResult = this.SiemensS7NetClient.ReadString(adress); operateResult.Content = operateResult.Content?.Replace("\\", "")?.Replace("\0", "")?.Replace("\u0004", "")?.Replace("\u0003", "")?.Replace("\u0014", "")?.Replace("\u001e", "")?.Replace("\u0006", "")?.Replace("emptyID", "")?.Replace("mptyID", "")?.Replace("ptyID", "")?.Replace("tyID", "")?.Replace("yID", "")?.Replace("ID", "")?.Replace("\a", ""); result = operateResult.Content == null ? "" : operateResult.Content.Trim(); break; case "bool": result = GetContent(this.SiemensS7NetClient.ReadBool(adress)); break; case "byte": result = GetContent(this.SiemensS7NetClient.Read(adress, (ushort)len)); break; default: throw new Exception($"未找到该数据类型:{type}"); } } catch (Exception ex) { logNet.WriteInfo($"{PLCIPAddress}读取失败,错误信息:{ex.Message}"); WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{Environment.NewLine}{PLCName}读取数据失败{Environment.NewLine}数据类型:{type},{adress}:{Environment.NewLine}错误信息:{ex.Message}", PLCName); } return result; } /// /// 获取读取的内容 /// /// /// private object GetContent(OperateResult operateResult) { if (!operateResult.IsSuccess) { logNet.WriteInfo($"{PLCIPAddress}读取失败,错误信息:{operateResult.Message}"); throw new Exception(operateResult.Message); } return operateResult.Content; } #endregion #region 根据DB地址及类型写入数据,最根本写入方法 /// /// 根据DB地址及类型写入数据 /// /// DB地址(DB块+.+偏移量) /// 数据类型(int/dint/string/bool) /// 写入的值 /// 返回成功或失败 public OperateResult Write(string adress, string type, object value) { OperateResult result = new OperateResult(); switch (type.ToLower()) { case "int": result = this.SiemensS7NetClient.Write(adress, Convert.ToUInt16(value)); break; case "dint": result = this.SiemensS7NetClient.Write(adress, Convert.ToUInt32(value)); break; case "string": string[] adressTmp = adress.Split('.'); result = this.SiemensS7NetClient.Write(adressTmp[0] + "." + (Convert.ToInt32(adressTmp[1]) - 2), value.ToString()); break; case "bool": result = this.SiemensS7NetClient.Write(adress, Convert.ToBoolean(value)); break; case "byte": result = this.SiemensS7NetClient.Write(adress, (byte[])value); break; default: throw new Exception($"未找到该数据类型:{type}"); } return result; } #endregion } }