using HslCommunication.ModBus; using HslCommunication; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Net.NetworkInformation; namespace WIDESEA_WCS.WCSClient.Modbus { /// /// ModbusPLC /// public class ModbusPLCClient { /// /// ModbusPLC连接 /// public ModbusTcpNet ModbusTcpNetClient { get; set; } /// /// PLC连接对应的地址 /// public List PLCDBItems { get; set; } /// /// 连接名称 /// public string PLCName { get; set; } /// /// IP地址 /// public string PLCIPAddress { get; set; } /// /// 连接说明 /// public string PLCDescroption { get; set; } /// /// 端口号(默认502) /// public int Port { get; set; } = 502; /// /// /// public byte Rack { get; set; } = 0; /// /// /// public byte Slot { get; set; } = 2; /// /// 是否已连接 /// public bool IsConnected { get; set; } = false; /// /// 连接信息 /// public string ConnectionMessage { get; set; } /// /// 构造方法,实例化连接对象及PLC连接对应的地址 /// /// public ModbusPLCClient() { this.ModbusTcpNetClient = new ModbusTcpNet(); } /// /// PLC连接内置线程 /// Thread Thread { get; set; } /// /// 内置线程是否运行 /// bool isRun { get; set; } #region 内置线程 PING IP /// /// 内置线程 PING IP /// public void Start(Action action) { Thread = new Thread(() => { while (true) { if (isRun) { IPStatus status = this.ModbusTcpNetClient.IpAddressPing(); if (status == IPStatus.Success) { ConnectionMessage = status.ToString(); if (action != null) action(); } else { IsConnected = false; //WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}连接连接掉线,200ms后自动重连", PLCName); OperateResult result = this.ModbusTcpNetClient.ConnectServer(); if (result.IsSuccess) IsConnected = true; } Thread.Sleep(100); } } }); Thread.Start(); } #endregion #region 连接PLC,将短连接切换成长连接模式 /// /// 连接PLC /// public string Connect() { this.ModbusTcpNetClient.IpAddress = PLCIPAddress; this.ModbusTcpNetClient.Port = Port; this.ModbusTcpNetClient.DataFormat = HslCommunication.Core.DataFormat.CDAB; ; this.ModbusTcpNetClient.AddressStartWithZero = true; this.ModbusTcpNetClient.ConnectTimeOut = 2000; this.ModbusTcpNetClient.ReceiveTimeOut = 2000; this.ModbusTcpNetClient.IsCheckMessageId = true; this.ModbusTcpNetClient.IsStringReverse = false; this.ModbusTcpNetClient.Station = 2; this.ModbusTcpNetClient?.ConnectClose(); OperateResult result = this.ModbusTcpNetClient.ConnectServer(); isRun = true; if (!result.IsSuccess) { WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{PLCName}连接失败---{DateTime.Now}{Environment.NewLine}错误信息:{result.Message}", PLCName); IsConnected = false; return $"连接失败,错误信息:{result.Message}"; } else { IsConnected = true; WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{PLCName}连接成功!---{DateTime.Now}", PLCName); return $"连接成功"; } } #endregion #region 断开与PLC的连接,将长连接切换成短连接模式 /// /// 断开与PLC的连接 /// public void Disconnect() { this.ModbusTcpNetClient?.ConnectClose(); isRun = false; WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:已断开与{PLCName}的连接!", PLCName); } #endregion #region 断开与PLC的连接,释放资源 /// /// 断开与PLC的连接 /// public void Dispose() { this.ModbusTcpNetClient?.Dispose(); 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.ModbusTcpNetClient) { object result = null; DBItemGroup item = this.PLCDBItems.Where(x => x.ItemName == itemName).FirstOrDefault(); 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 根据PLCDBItem读取PLC数据 /// /// 根据PLCDBItem读取PLC数据 /// /// /// public virtual object ReadValue(DBItemGroup item) { lock (this.ModbusTcpNetClient) { 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.ModbusTcpNetClient) { 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.ModbusTcpNetClient) { 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.ModbusTcpNetClient) { 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.ModbusTcpNetClient) { OperateResult operateResult = new OperateResult(); ; bool result = false; string writeResult = ""; DBItemGroup item = this.PLCDBItems.Where(x => x.ItemName == itemName).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 将数据写入到PLC中 /// /// 将数据写入到PLC中 /// /// 名称/描述(数据库中plcdetail_name列) /// 写入的值 /// public virtual bool WriteValue(string itemName, string equipNum, object value) { lock (ModbusTcpNetClient) { 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.ModbusTcpNetClient.Write(adress, value as ushort[]); break; case "dint": result = this.ModbusTcpNetClient.Write(adress, value as int[]); break; case "char": string temp = value.ToString(); byte[] writeData = Encoding.Default.GetBytes(temp); result = this.ModbusTcpNetClient.Write(adress, writeData); break; case "bool": result = this.ModbusTcpNetClient.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.ModbusTcpNetClient.ReadUInt16(adress, Convert.ToUInt16(length)).Content; break; case "dint": result = this.ModbusTcpNetClient.ReadUInt32(adress, Convert.ToUInt16(length)).Content; break; case "char": byte[] temps = this.ModbusTcpNetClient.Read(adress, Convert.ToUInt16(length)).Content; result = Encoding.Default.GetString(temps).ToCharArray(); break; case "bool": result = this.ModbusTcpNetClient.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 根据名称/描述读取指定长度的bool数组 /// /// 根据名称/描述读取指定长度的bool数组 /// /// 起始地址名称/描述(数据库中plcdetail_name列) /// 读取的长度信息 /// 设备编号 /// //public OperateResult ReadBool(string itemName, ushort length, string equipNum) //{ // string address = this.PLCDBItems.Where(x => x.ItemName == itemName && x.EquipNum == equipNum).FirstOrDefault().ItemAddress; // OperateResult operateResult = S7AddressData.ParseFrom(address); // bool flag = !operateResult.IsSuccess; // OperateResult result; // if (flag) // { // result = OperateResult.CreateFailedResult(operateResult); // } // else // { // ushort length2 = (ushort)((operateResult.Content.AddressStart + (int)length - 1) / 8 - operateResult.Content.AddressStart / 8 + 1); // int num = operateResult.Content.AddressStart % 8; // operateResult.Content.AddressStart = operateResult.Content.AddressStart - num; // operateResult.Content.Length = length2; // OperateResult operateResult2 = this.ModbusTcpNetClient.Read(new S7AddressData[] // { // operateResult.Content // }); // bool flag2 = !operateResult2.IsSuccess; // if (flag2) // { // result = OperateResult.CreateFailedResult(operateResult2); // } // else // { // result = OperateResult.CreateFailedResult(operateResult2); // //result = OperateResult.CreateSuccessResult(operateResult2.Content.ToBoolArray().SelectMiddle(num, (int)length)); // //WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Info:{Environment.NewLine}{PLCName}读取数据{Environment.NewLine}数据类型:bool{Environment.NewLine}状态({address},长度:{length}):" + JsonConvert.SerializeObject(result.Content), PLCName); // } // } // return result; //} #endregion #region 根据DB地址及类型读取数据,最根本读取方法 /// /// 根据DB地址及类型读取数据 /// /// DB地址(DB块+.+偏移量) /// 数据类型(int/dint/string/bool) /// public object Read(string adress, string type) { object result = -1; ; try { switch (type.ToLower()) { case "int": result = this.ModbusTcpNetClient.ReadUInt16(adress).Content; break; case "dint": result = this.ModbusTcpNetClient.ReadUInt32(adress).Content; break; case "string": OperateResult operateResult = this.ModbusTcpNetClient.ReadString(adress, 10); 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 = this.ModbusTcpNetClient.ReadBool(adress).Content; break; default: throw new Exception($"未找到该数据类型:{type}"); //break; } } catch (Exception ex) { StackTrace stackTrace = new StackTrace(); StackFrame[] stackFrames = stackTrace.GetFrames(); string str = ""; foreach (var item in stackFrames) { str += "方法名:" + item.GetMethod().Name + ",行号:" + item.GetFileLineNumber() + ",文件名:" + item.GetFileName() + Environment.NewLine; } WIDESEA_Common.Tools.WriteLog.GetLog().Write($"Error:{Environment.NewLine}{PLCName}读取数据失败{Environment.NewLine}数据类型:{type},{adress}:{Environment.NewLine}错误信息:{ex.Message},{str}", PLCName); } return result; } #endregion #region 根据DB地址及类型写入数据,最根本写入方法 /// /// 根据DB地址及类型写入数据 /// /// DB地址(DB块+.+偏移量) /// 数据类型(int/dint/string/bool) /// 写入的值 /// 返回成功或失败 public OperateResult Write(string adress, string type, object value) { OperateResult result = new OperateResult(); try { switch (type.ToLower()) { case "int": result = this.ModbusTcpNetClient.Write(adress, Convert.ToUInt16(value)); break; case "dint": result = this.ModbusTcpNetClient.Write(adress, Convert.ToUInt32(value)); break; case "string": string[] adressTmp = adress.Split('.'); result = this.ModbusTcpNetClient.Write(adressTmp[0] + "." + (Convert.ToInt32(adressTmp[1]) - 2), value.ToString()); break; case "bool": result = this.ModbusTcpNetClient.Write(adress, Convert.ToBoolean(value)); break; default: throw new Exception($"未找到该数据类型:{type}"); } } 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 } }