You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
596 lines
23 KiB
596 lines
23 KiB
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<string, bool> _ifConnect = new Dictionary<string, bool>();
|
|
public static Dictionary<string, bool> 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<string, S7Client> _snap7Clients = new Dictionary<string, S7Client>();
|
|
internal static Dictionary<string, S7Client> Snap7Clients { get => _snap7Clients; set => _snap7Clients = value; }
|
|
|
|
// 存放每个 PLC DB2 的所有数据,记录的是最近一次读取的 PLC DB2 数据
|
|
static Dictionary<string, byte[]> _DB2DeviceHisBuff = new Dictionary<string, byte[]>();
|
|
public static Dictionary<string, byte[]> DB2DeviceHisBuff { get => _DB2DeviceHisBuff; set => _DB2DeviceHisBuff = value; }
|
|
|
|
// 存放每个 PLC 本次读取的数据
|
|
static Dictionary<string, byte[]> _PLCReadBuff = new Dictionary<string, byte[]>();
|
|
public static Dictionary<string, byte[]> PLCReadBuff { get => _PLCReadBuff; set => _PLCReadBuff = value; }
|
|
|
|
// 在每个 PLC 的异步读写前,判断是否有其他异步操作未完成,有未完成的则值为 false;没有则值为 true,可以进行异步
|
|
static Dictionary<string, bool> _plcIsAsyncDone = new Dictionary<string, bool>();
|
|
public static Dictionary<string, bool> 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<Tuple<Type, string>>();
|
|
var nodesToWrite = new List<String>();
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|