using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Data; using DBFactory; using CommonLib; using Snap7; using System.Runtime.InteropServices; using System.Linq; using System.Collections; using System.Reflection; namespace Snap7Client { public static class CCommonSnap7Client { private static Object obj = new Object(); public static event CDataSourceChangeEventHandler DataChange; public static event CUpdateDBEventHandler UpdateDB; public static event CWriteDarkCasketEventHandler WriteDarkCasket; public static event CSendDeviceOrderEventHandler SendDeviceOrder; public static void OnSendDeviceOrder(CSendDeviceOrderEventArgs e) { if (SendDeviceOrder != null) { SendDeviceOrder(null, e); } } public static void OnWriteDarkCasket(CWriteDarkCasketEventArgs e) { if (WriteDarkCasket != null) { WriteDarkCasket(null, e); } } public static void OnDataChange(CDataChangeEventArgs e) { if (DataChange != null) { DataChange(null, e); } } public static void OnUpdateDB(object sender, CUpdateDBChangeEventArgs e) { if (UpdateDB != null) { UpdateDB(sender, e); } } public static event RefreshMonitorEventHandler RefreshMonitor; public static void OnRefreshMonitor(RefreshMonitorEventArgs e) { if (RefreshMonitor != null) { RefreshMonitor(e); } } public static DBOperator dbo = new DBOperator();//20130926 static StringBuilder sql = new StringBuilder(); // 存放每个 PLC 的 client 是否连接 static Dictionary _ifConnect = new Dictionary(); public static Dictionary IfConnect { get { return CCommonSnap7Client._ifConnect; } set { DataView dv = dbo.ExceSQL("SELECT DISTINCT F_RemoteIP, F_Slot, F_Rack FROM T_Base_Device WHERE (F_CommType = 'Snap7Client' and F_RemoteIP is not null)").Tables[0].DefaultView; for (int i = 0; i < dv.Count; i++) { if (value.ContainsKey(dv[i][0].ToString()) == false) { value.Add(dv[i][0].ToString(), false); } } CCommonSnap7Client._ifConnect = value; } } // 存放每个 PLC 的 s7.client static Dictionary _snap7Clients = new Dictionary(); internal static Dictionary Snap7Clients { get => _snap7Clients; set => _snap7Clients = value; } // 存放每个 PLC DB2 的所有数据,记录的是最近一次读取的 PLC DB2 数据 static Dictionary _DB2DeviceHisBuff = new Dictionary(); public static Dictionary DB2DeviceHisBuff { get => _DB2DeviceHisBuff; set => _DB2DeviceHisBuff = value; } // 存放每个 PLC 本次读取的数据 static Dictionary _PLCReadBuff = new Dictionary(); public static Dictionary PLCReadBuff { get => _PLCReadBuff; set => _PLCReadBuff = value; } // 在每个 PLC 的异步读写前,判断是否有其他异步操作未完成,有未完成的则值为 false;没有则值为 true,可以进行异步 static Dictionary _plcIsAsyncDone = new Dictionary(); public static Dictionary PlcIsAsyncDone { get => _plcIsAsyncDone; set => _plcIsAsyncDone = value; } static string _snap7Error; public static string Snap7Error { get { return _snap7Error; } set { _snap7Error = value; RefreshMonitorEventArgs rea = new RefreshMonitorEventArgs("tsStatus", string.Format("Snap7:{0}", _snap7Error)); OnRefreshMonitor(rea); } } private static S7Client.S7CliCompletion CallBack = new S7Client.S7CliCompletion(CompletionProc); public static void EndConnect() { try { foreach (var ip in Snap7Clients.Keys) { IfConnect[ip] = false; Snap7Clients[ip].Disconnect(); } temp.Enabled = false; //是否执行System.Timers.Timer.Elapsed事件; }catch (Exception e) { return ; } } private static System.Timers.Timer temp = new System.Timers.Timer(); //初始化所有的PLC连接 public static void InitAllClientSnap7() { try { DataView dv = dbo.ExceSQL("SELECT DISTINCT F_RemoteIP FROM T_Base_Device WHERE ( F_RemoteIP is not null)").Tables[0].DefaultView; #region 将所有的 PLC 添加到 SnapyClients 中 for (int i = 0; i < dv.Count; i++) { InitClientSnap7(dv[i]["F_RemoteIP"].ToString()); } #endregion if (Snap7Clients.Count == 0) return; #region 设置定时器,定时去读取 PLC temp.Elapsed += new System.Timers.ElapsedEventHandler(Async_Read); //到达时间的时候执行事件; temp.Interval = 1000; temp.AutoReset = true; //设置是执行一次(false)还是一直执行(true); temp.Enabled = true; //是否执行System.Timers.Timer.Elapsed事件; #endregion } catch (Exception e) { Snap7Error = "Snap7Client.InitClientSnap7:" + e.StackTrace + e.Message; } } public static bool InitClientSnap7(string clientName) { if (IfConnect.ContainsKey(clientName) == false) { IfConnect.Add(clientName, false); } if (PlcIsAsyncDone.ContainsKey(clientName) == false) { PlcIsAsyncDone.Add(clientName, true); } if (IfConnect[clientName] == true) { return true; } if (Snap7Clients.ContainsKey(clientName) == true) { // 如果已存在客户端,则重新连接,连接成功,即可返回,否则需要进行 new client if (Snap7Clients[clientName].Connect() == 0) { IfConnect[clientName] = true; return true; } else { IfConnect[clientName] = false; return false; } } try { #region 为 S7Client 建立连接 // Connect to the PLC. string[] group = clientName.Split(':'); if (group.Length != 3) return false; string remoteIP = group[0]; int rack = Convert.ToInt32(group[1]); int slot = Convert.ToInt32(group[2]); string[] group1 = remoteIP.Split('.'); string temp = ""; foreach(string t in group1) { temp += t; } S7Client myClient = new S7Client(); myClient.SetAsCallBack(CallBack, new IntPtr(Convert.ToInt32(temp))); int result = myClient.ConnectTo(remoteIP, rack, slot); if (result != 0) { return false; } #endregion if (Snap7Clients.ContainsKey(clientName) == false) { Snap7Clients.Add(clientName, myClient); } IfConnect[clientName] = true; #region 初始化每个设备的历史缓存 DataView dv = dbo.ExceSQL("SELECT F_DeviceIndex, F_DBWGetLength FROM T_Base_Device WHERE ( F_RemoteIP = '" + clientName +"' )").Tables[0].DefaultView; for (int i = 0; i < dv.Count; i++) { DB2DeviceHisBuff.Add(dv[i]["F_DeviceIndex"].ToString(), new byte[Convert.ToInt32(Convert.ToDouble(dv[i]["F_DBWGetLength"]))]); } PLCReadBuff.Add(clientName, new byte[GetDB2Length(clientName)]); #endregion int[] chatnew; chatnew = new int[1]; CWriteDarkCasketEventArgs e0 = new CWriteDarkCasketEventArgs("CClientSnap7.InitClientSnap7", "Connect", clientName, "连接 PLC 成功", chatnew); OnWriteDarkCasket(e0); return IfConnect[clientName]; } catch (Exception e) { IfConnect[clientName] = false; Snap7Error = "CClientSnap7.InitClientSnap7:" + e.StackTrace + e.Message; return false; } } //异步读或写完成后,会进入此方法 static void CompletionProc(IntPtr usrPtr, int opCode, int opResult) { string remoteIP = ""; if (opResult == 0) //异步读写成功 { #region 确定是哪个 PLC 发起的回调函数 foreach (string ip in Snap7Clients.Keys) { string[] group = ip.Split(':')[0].Split('.'); string tt = ""; foreach (string t in group) { tt += t; } if (tt == usrPtr.ToString()) { remoteIP = ip; } } if (remoteIP == "") return; #endregion #region 异步读回调处理 if (opCode == 1) { try { foreach (string ip in Snap7Clients.Keys) { if (ip == remoteIP) { DataView dv = dbo.ExceSQL("SELECT F_DeviceIndex, F_DBW2Address, F_DBWGetLength FROM T_Base_Device WHERE(F_RemoteIP LIKE '" + remoteIP + "%') ORDER BY F_DeviceIndex ").Tables[0].DefaultView; //DataView dv = dbo.ExceSQL("SELECT F_DBW2Address, F_DBWGetLength FROM T_Base_Device WHERE (F_RemoteIP = '"+ remoteIP + "')").Tables[0].DefaultView; int length; int start; string device; for (int i = 0; i < dv.Count; i++) { device = dv[i]["F_DeviceIndex"].ToString(); if (dv[i]["F_DBW2Address"] == DBNull.Value) continue; start = Convert.ToInt32(dv[i]["F_DBW2Address"]); length = Convert.ToInt32(Convert.ToDouble(dv[i]["F_DBWGetLength"])); byte[] buff = new Byte[length]; Array.Copy(PLCReadBuff[remoteIP], start, buff, 0, length); //if (DB2DeviceHisBuff.ContainsKey(device) == false) //{ //} bool flag = Enumerable.SequenceEqual(buff, DB2DeviceHisBuff[device]); if (!flag) { Array.Copy(buff, DB2DeviceHisBuff[device], buff.Length); CUpdateDBChangeEventArgs udbe = new CUpdateDBChangeEventArgs(Convert.ToInt32(device), null, buff); CCommonSnap7Client.OnUpdateDB("Snap7Client.CCommonSnap7Client.OnDataChange", udbe); int[] chatnew; chatnew = new int[1]; CWriteDarkCasketEventArgs e0 = new CWriteDarkCasketEventArgs("Snap7Client.CCommonSnap7Client:", "Read", device.ToString(), "读取成功", chatnew); CCommonSnap7Client.OnWriteDarkCasket(e0); } } } } } catch (Exception e) { } finally { //AsyncDone = true; } } #endregion #region 异步写回调处理 else if (opCode == 2) { ; } #endregion else { // } } else { // 异步读取失败 #region 写入黑匣子 #endregion } //lock (obj) //{ PlcIsAsyncDone[remoteIP] = true; //} } public static bool AsyncWriteAllItemValue(StringBuilder[] itemnames, StringBuilder[] itemvalues) { try { #region 找到要写入的设备所在的 PLC string db1Address = itemnames[0].ToString().Split('.')[1].Split(',')[0] ; string clientName = ""; DataView dv = dbo.ExceSQL("SELECT F_RemoteIP FROM T_Base_Device WHERE (F_DBW1Address = '"+ db1Address + "' )").Tables[0].DefaultView; if (dv.Count <= 0) return false; for (int i = 0; i < dv.Count; i++) { clientName = dv[0]["F_RemoteIP"].ToString(); } #endregion byte[] tttt = new byte[GetDB1Length(clientName)]; var values = new List>(); var nodesToWrite = new List(); for (int i = 0; i < itemnames.Length; i++) { // Values to write. string item = itemnames[i].ToString(); String sValue = itemvalues[i].ToString(); // Leave current value if write value is empty. if (sValue.Length == 0) { continue; } #region 将要写入的数据通过 S7 进行转换成 BYTE 数组 object[] para = new object[3]; para[0] = tttt; para[1] = Convert.ToInt32(item.ToString().Split('.')[1].Split(',')[0]); string funcName = ""; if (item.ToString().IndexOf(",b") >= 0)//无符号字节型0--255 { funcName = "SetUSIntAt"; //SetByteAt para[2] = Convert.ToByte(sValue); } else if (item.ToString().IndexOf(",i") >= 0)//short:有符号两个字节的单字-32768至32767 { funcName = "SetIntAt"; para[2] = Convert.ToInt16(sValue); } else if (item.ToString().IndexOf(",w") >= 0)//ushort:无符号两个字节的单字0至65535 { funcName = "SetUIntAt"; para[2] = Convert.ToUInt16(sValue); } else if (item.ToString().IndexOf(",di") >= 0)//int:有符号四个字节的双字-2147483648至2147483647 { funcName = "SetDIntAt"; para[2] = Convert.ToInt32(sValue); } else if (item.ToString().IndexOf(",dw") >= 0)//uint:无符号四个字节的双字0至4294967295 { funcName = "SetUDIntAt"; para[2] = Convert.ToUInt32(sValue); } else if (item.ToString().IndexOf(",dt") >= 0)//DateTime:日期和时间类型:05/31/2016 08:21:10.123 PM { } Type t = typeof(S7); string[] _args = { "XXX" }; object result = t.InvokeMember(funcName, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, para); #endregion } if (IfConnect[clientName] == true) { byte[] buffer = new byte[GetDB1Length(clientName)]; S7Client client = Snap7Clients[clientName]; int num = 0; #region 和 AsRead 抢占资源,如果两秒中没有抢到则失败 while(PlcIsAsyncDone[clientName] == false) { System.Threading.Thread.Sleep(20); num++; if (num > 100) return false; } #endregion if (PlcIsAsyncDone[clientName] == true) { //lock (obj) //{ int res = client.AsDBWrite(1, 0, tttt.Length, tttt); if (res == 0) { PlcIsAsyncDone[clientName] = false; } else if (res == 0x00300000) { return false; } else { PlcIsAsyncDone[clientName] = true; IfConnect[clientName] = false; InitClientSnap7(clientName); } } } else { PlcIsAsyncDone[clientName] = true; IfConnect[clientName] = false; InitClientSnap7(clientName); } return true; } catch (Exception ex) { Snap7Error = "向Snap7数据存取服务器写数据时:" + ex.Message; //_IfConnectSnap7Server = false; return false; } } public static void Async_Read(object sender, System.Timers.ElapsedEventArgs e) { temp.Enabled = false; try { if (Snap7Clients.Count == 0) return; foreach (string ip in Snap7Clients.Keys) { if (IfConnect[ip] == true) { byte[] buffer = new byte[GetDB2Length(ip)]; S7Client client = Snap7Clients[ip]; if (PlcIsAsyncDone[ip] == true) { //lock (obj) //{ int result = client.AsDBRead(2, 0, PLCReadBuff[ip].Length, PLCReadBuff[ip]); if (result == 0) { PlcIsAsyncDone[ip] = false; } else if (result == 0x00300000) { return; } else { PlcIsAsyncDone[ip] = true; IfConnect[ip] = false; InitClientSnap7(ip); } } } else { PlcIsAsyncDone[ip] = true; IfConnect[ip] = false; InitClientSnap7(ip); } } } catch (Exception) { int i = 0; } finally { //myTimer.Enabled = true; } temp.Enabled = true; } public static int GetDB2Length(string remoteIP) { try { sql.Remove(0, sql.Length); sql.Append("SELECT MAX(F_DBW2Address) AS db_end, MIN(F_DBW2Address) AS db_start FROM T_Base_Device where F_RemoteIP = '" + remoteIP + "'"); DataView dv = dbo.ExceSQL(sql.ToString()).Tables[0].DefaultView; sql.Remove(0, sql.Length); sql.Append("SELECT F_DBWGetLength FROM T_Base_Device WHERE F_DBW2Address = " + dv[0]["db_end"]); DataView dvt = dbo.ExceSQL(sql.ToString()).Tables[0].DefaultView; int length = Convert.ToInt32(dvt[0]["F_DBWGetLength"]) + Convert.ToInt32(dv[0]["db_end"]) - Convert.ToInt32(dv[0]["db_start"]); return length; } catch(Exception e) { return 0; } } public static int GetDB1Length(string remoteIP) { try { sql.Remove(0, sql.Length); sql.Append("SELECT MAX(F_DBW1Address) AS db_end, MIN(F_DBW1Address) AS db_start FROM T_Base_Device where F_RemoteIP = '" + remoteIP + "'"); DataView dv = dbo.ExceSQL(sql.ToString()).Tables[0].DefaultView; sql.Remove(0, sql.Length); sql.Append("SELECT F_DBWSendLength FROM T_Base_Device WHERE F_DBW1Address = " + dv[0]["db_end"]); DataView dvt = dbo.ExceSQL(sql.ToString()).Tables[0].DefaultView; int length = Convert.ToInt32(dvt[0]["F_DBWSendLength"]) + Convert.ToInt32(dv[0]["db_end"]) - Convert.ToInt32(dv[0]["db_start"]); //DataView dv = dbo.ExceSQL("SELECT F_DBW1Address, F_DBWSendLength FROM T_Base_Device WHERE(F_RemoteIP LIKE '" + remoteIP + "%') ORDER BY F_DBW1Address desc").Tables[0].DefaultView; return length; } catch (Exception e) { return 0; } } } }