using System; using System.Collections.Generic; using System.Text; using Opc.Ua; using Opc.Ua.Client; using System.Threading; using System.Data; using DBFactory; using CommonLib; using Siemens.OpcUA; namespace OPCClient { public static class CCommonOPCClient { private static Model.MDevice devinfo; private static Object thisLock = new Object(); public static event CUpdateDBEventHandler UpdateDB; public static void OnUpdateDB(object sender,CUpdateDBChangeEventArgs e) { if (UpdateDB != null) { UpdateDB(sender, e); } } public static DBOperator dbo = CommonClassLib.AppSettings.dbo; //20130510 //20110726 private static Dictionary _connectCount = new Dictionary(); //20110726 public static Dictionary ConnectCount { get { return CCommonOPCClient._connectCount; } set { CCommonOPCClient._connectCount = value; } } private static int[] _plcStates=null ; public static int[] PlcStates { get { return CCommonOPCClient._plcStates; } //set { CCommonOPCClient._plcStates = value; } } private static Server m_Server = null; private static Siemens.OpcUA.Subscription m_Subscription; /// /// 20110309订阅的组内标签数据变化事件 /// /// 客户端句柄 /// 请求句柄 /// 标签值数组 public static void OnDataChange(object clientHandle, DataValue value) { lock (thisLock) { //根据subscriptionHandle对应设备索引,依据设备种类(不同类型设备通讯数据帧不同) //分别处理:完成,报警,光电开关,PLC发起的请求 int deviceindex = 0; try { if (clientHandle.ToString().IndexOf("split") >= 0) { deviceindex = System.Convert.ToInt32(clientHandle.ToString().Substring(5)); } else { deviceindex = System.Convert.ToInt32(clientHandle); } devinfo = Model.CGetInfo.GetDeviceInfo(deviceindex); if (!StatusCode.IsGood(value.StatusCode)) { if (devinfo == null) { OpcError = string.Format("设备索引{0}在数据库不存在!", clientHandle); return; } //if (value.StatusCode.ToString() == "BadNoCommunication") //{ // CCommonOPCClient.Hostname = CommonClassLib.AppSettings.GetValue("HostName");//20090922 devinfo.RemoteIP; // CCommonOPCClient.ProgID = CommonClassLib.AppSettings.GetValue("OPCProgID"); // CCommonOPCClient.PlcConnectionID = devinfo.S7Connection;//20110216 // StringBuilder[] itemnames = new StringBuilder[1] { new StringBuilder("") }; // itemnames[0].Append(Model.CGeneralFunction.DBGet).Append(".").Append(Convert.ToString(devinfo.Dbw2Address)).Append(",b"); // DataValueCollection dvc= SyncReadAllItemValue(itemnames); // if (dvc.Count > 0) // { // value = dvc[0]; // } // OpcError = string.Format("{0}所在的PLC已经离线,自动重新读取数据!订阅标签状态:" + value.StatusCode.ToString(), clientHandle); // //return; //} //触发一个特殊事件提示PLC断开连接 if (ConnectCount.ContainsKey(devinfo.S7Connection) == false)//20110726 { ConnectCount.Add(devinfo.S7Connection, 0); } ConnectCount[devinfo.S7Connection]++; OpcError = string.Format("{0}所在的PLC已经离线!订阅标签状态:" + value.StatusCode.ToString(), clientHandle); return; } ConnectCount[devinfo.S7Connection] = 0; if (clientHandle.ToString().IndexOf("split") >= 0) { Array arr = (Array)value.Value; byte[] GDKG = null; GDKG = new byte[arr.GetLength(0)]; Array.Copy(arr, GDKG, arr.GetLength(0)); CUpdateDBChangeEventArgs udbe = new CUpdateDBChangeEventArgs(deviceindex, GDKG, null); OnUpdateDB("OPCClient.CCommonOPCClient.OnDataChange", udbe); } else { Array arr = (Array)value.Value; byte[] itemnamevalue = new byte[arr.GetLength(0)]; Array.Copy(arr, itemnamevalue, arr.GetLength(0)); int head = 0; head = itemnamevalue[0];//读写标志 CUpdateDBChangeEventArgs udbe = new CUpdateDBChangeEventArgs(deviceindex, null, itemnamevalue); OnUpdateDB("OPCClient.CCommonOPCClient.OnDataChange", udbe); } } catch (Exception ex) {//20120420 OpcError = string.Format("OPC服务器订阅事件异常:{0},{1}", ex.Message, ex.StackTrace); } } } static private string _PLCconnectionID = "S7 connection_1";//OPC.UA的新写法"ns=2,S7 connection_1.db2.0,b,5"表示:"S7:[S7 connection_1]的db2.DBB0开始5个字节" /// /// 建立PLC连接的ID,例如:"S7:[S7 connection_1]" /// 或者"S7:[@LOCALSERVER]" /// public static string PlcConnectionID { get { return CCommonOPCClient._PLCconnectionID; } set { CCommonOPCClient._PLCconnectionID = value; } } static private string _opcError; static private bool _IfConnectOPCServer=false ; static private string _hostname; /// /// OPC Server的机器名称或者IP地址 /// public static string Hostname { get { return CCommonOPCClient._hostname; } set { CCommonOPCClient._hostname = value; } } static private string _progID; /// /// OPC Server的程序标识,例如:OPC.SimaticNET /// public static string ProgID { get { return CCommonOPCClient._progID; } set { CCommonOPCClient._progID = value; } } public static string OpcError { get { return CCommonOPCClient._opcError; } set { CCommonOPCClient._opcError = value; CUpdateDBChangeEventArgs udbe = new CUpdateDBChangeEventArgs(0, null, null); OnUpdateDB("OPCClient.CCommonOPCClient.OnDataChange", udbe); } } /// /// 《设备索引,对应设备订阅》 /// public static SubscriptionCollection SubscriptionGroup = new SubscriptionCollection();//定义组对象集合(订阅者集合)//20110309 /// /// 20110309每个设备创建一个订阅和DataChange事件 /// public static void CreateSubscriptionGroup() { if (_IfConnectOPCServer == false) { _hostname = CommonClassLib.AppSettings.GetValue("HostName"); _progID = CommonClassLib.AppSettings.GetValue("OPCProgID"); if (ConnectOPCServer(_hostname, _progID) == false) return ; } DataView dv = dbo.ExceSQL("SELECT F_ReadSubscription, F_DeviceIndex, F_DBW2Address, F_DBWGetLength, F_S7Connection, F_SplitByte FROM T_Base_Device WHERE (F_ReadSubscription IS NOT NULL) AND (F_ReadSubscription =F_DeviceIndex) ORDER BY F_ReadSubscription").Tables[0].DefaultView; if (dv.Count > 0) { if (dv.Count < SubscriptionGroup.Count) return; } DataView dvx; for (int i = 0; i < dv.Count; i++) { string dt = DateTime.Now.Minute.ToString() + "-" + DateTime.Now.Second.ToString() + "-" + DateTime.Now.Millisecond.ToString(); dvx = dbo.ExceSQL(string.Format("SELECT (MAX(F_DBW2Address) + F_DBWGetLength) as maxdbw2 FROM T_Base_Device WHERE (F_ReadSubscription = {0}) AND (F_DeviceKindIndex <> 33) GROUP BY F_DBWGetLength ORDER BY maxdbw2 DESC", dv[i]["F_ReadSubscription"])).Tables[0].DefaultView; if (dvx.Count > 0) { NodeId nodeId = new NodeId(dv[i]["F_S7Connection"].ToString() +"."+ Model.CGeneralFunction.DBGet +"."+ dv[i]["F_DBW2Address"].ToString() + ",b," + (System.Convert.ToInt32(System.Convert.ToInt32(dvx[0]["maxdbw2"]) - System.Convert.ToInt32(dv[i]["F_DBW2Address"]))).ToString(), 3);//richard.liu20140824 xp:参数NameSpaceIndex=2,win7:参数NameSpaceIndex=3 // Add the attribute name/value to the list view. object serverHandle = null; try { // Add the item and apply any changes to it. m_Subscription.AddDataMonitoredItem(nodeId, dv[i]["F_ReadSubscription"].ToString(), OnDataChange, 300, out serverHandle); } catch (ServiceResultException monitoredItemResult) { OpcError = "建立OPC订阅组时:" + monitoredItemResult.Message; } } } dv = dbo.ExceSQL("SELECT F_ReadSubscription, F_DeviceIndex, F_DBW2Address, F_DBWGetLength, F_S7Connection, F_SplitByte FROM T_Base_Device WHERE (F_ReadSubscription IS NOT NULL) AND (F_DeviceKindIndex=33) ORDER BY F_ReadSubscription").Tables[0].DefaultView; for (int i = 0; i < dv.Count; i++) { NodeId nodeId = new NodeId(dv[i]["F_S7Connection"].ToString() + "." + Model.CGeneralFunction.DBGet + "." + dv[i]["F_DBW2Address"].ToString() + ",b," + System.Convert.ToInt32(dv[i]["F_DBWGetLength"]).ToString(), 3);//richard.liu20140824 xp:参数NameSpaceIndex=2,win7:参数NameSpaceIndex=3 // Add the attribute name/value to the list view. object serverHandle = null; try { // Add the item and apply any changes to it. m_Subscription.AddDataMonitoredItem(nodeId, "split" + dv[i]["F_ReadSubscription"].ToString(), OnDataChange, 100, out serverHandle); } catch (ServiceResultException monitoredItemResult) { OpcError = "建立OPC订阅组时:" + monitoredItemResult.Message; } } } public static void kkk() { DisConnectOPCServer(); _IfConnectOPCServer = false; _hostname = CommonClassLib.AppSettings.GetValue("HostName"); _progID = CommonClassLib.AppSettings.GetValue("OPCProgID"); if (ConnectOPCServer(_hostname, _progID) == false) return; CreateSubscriptionGroup(); //for (int i = 0; i < 20; i++) //{ // //object ooo = severhandel[i]; // m_Subscription.RemoveMonitoredItem(severhandel[i]); //} } /// /// 连接指定主机的OPC服务器 /// /// 主机名称 /// OPC服务器程序标识例如:"OPC.SimaticNET" /// public static bool ConnectOPCServer(string hostname, string ProgID) { try { Uri discoveryUrl = new Uri("opc.tcp://" + hostname + ":55101"); Discovery discovery = new Discovery(); EndpointDescriptionCollection endpoints = null; string rr = discovery.GetEndpoints(discoveryUrl, ref endpoints); for (int i = 0; i < endpoints.Count; i++) { endpoints[i].EndpointUrl=discoveryUrl.ToString(); //richard.liu20140824 //xp: if ((endpoints[i].Server.ApplicationName.Text == ProgID + "@" + hostname.ToUpper()) //win7: if ((endpoints[i].Server.ApplicationName.Text == ProgID ) if ((endpoints[i].Server.ApplicationName.Text == ProgID ) && (endpoints[i].SecurityMode == MessageSecurityMode.None))//OPC.SimaticNET.S7@LIU-SHAOPENG { m_Server = new Server(); m_Server.CertificateEvent += new certificateValidation(m_Server_CertificateEvent); m_Server.Connect(endpoints[i]); _IfConnectOPCServer = true; if (m_Subscription == null) { m_Subscription = m_Server.AddSubscription(100); } return true; } } OpcError = "连接OPC数据存取服务器时:配置项OPCProgID和HostName不准确!"; _IfConnectOPCServer = false; return false; } catch (Exception ex) { OpcError = "连接OPC数据存取服务器时:" + ex.Message; _IfConnectOPCServer = false; return false; } } public static DataValueCollection SyncReadAllItemValue(StringBuilder[] itemnames) { DataValueCollection m_currentValues; if (_IfConnectOPCServer == false) { if (ConnectOPCServer(_hostname, _progID) == false) return null; } if (ConnectCount.ContainsKey(_PLCconnectionID) == false)//20110726 { ConnectCount.Add(_PLCconnectionID, 0); } if (ConnectCount[_PLCconnectionID] > 1)//20110726 { ConnectCount[_PLCconnectionID]++;//20110726 return null; } NodeIdCollection nodesToRead = new NodeIdCollection(itemnames.GetLength(0)); foreach (StringBuilder item in itemnames) { // NodeIds. String sNodeId = _PLCconnectionID + "." + item.ToString(); NodeId nodeId = new NodeId(sNodeId,3);//richard.liu20140824 xp:参数NameSpaceIndex=2,win7:参数NameSpaceIndex=3 nodesToRead.Add(nodeId); } // Call to ClientAPI. m_Server.ReadValues( nodesToRead, out m_currentValues); return m_currentValues; } public static bool SyncWriteAllItemValue(StringBuilder[] itemnames, StringBuilder[] itemvalues) { try { if (_IfConnectOPCServer == false) { if (ConnectOPCServer(_hostname, _progID) == false) return false; } if (ConnectCount.ContainsKey(_PLCconnectionID) == false)//20110726 { ConnectCount.Add(_PLCconnectionID, 0); } if (ConnectCount[_PLCconnectionID] > 1)//20110726 { ConnectCount[_PLCconnectionID]++;//20110726 //_opcError = "同步数据时:OPC Server没连接到PLC:"+_PLCconnectionID.ToString()+",人工确认后进行【设备初始化】"; return false; } NodeIdCollection nodesToWrite = new NodeIdCollection(itemnames.GetLength(0)); DataValueCollection values = new DataValueCollection(itemnames.GetLength(0)); StatusCodeCollection results = null; int i = 0; foreach (StringBuilder item in itemnames) { // Values to write. String sValue = itemvalues[i].ToString(); // Leave current value if write value is empty. if (sValue.Length == 0) { i++; continue; } #region win7等64位操作系统需要指定确切的数据类型//richard.liu20141118修正误把sValue当做item Type gt = typeof(byte); if (item.ToString().IndexOf(",b") >= 0)//无符号字节型0--255 { gt = typeof(byte); } else if (item.ToString().IndexOf(",i") >= 0)//short:有符号两个字节的单字-32768至32767 { gt = typeof(short); } else if (item.ToString().IndexOf(",w") >= 0)//ushort:无符号两个字节的单字0至65535 { gt = typeof(ushort); } else if (item.ToString().IndexOf(",di") >= 0)//int:有符号四个字节的双字-2147483648至2147483647 { gt = typeof(int); } else if (item.ToString().IndexOf(",dw") >= 0)//uint:无符号四个字节的双字0至4294967295 { gt = typeof(uint); } Variant variant = new Variant(Convert.ChangeType(sValue, gt)); #endregion DataValue value = new DataValue(variant); values.Add(value); // NodeIds. String sNodeId =_PLCconnectionID+"."+ item.ToString(); NodeId nodeId = new NodeId(sNodeId, 3); //richard.liu20140824 xp:参数NameSpaceIndex=2,win7:参数NameSpaceIndex=3 nodesToWrite.Add(nodeId); i++; } // Call to ClientAPI. m_Server.WriteValues( nodesToWrite, values, out results); int ia = 0;//richard.liu20141110 foreach (StatusCode sc in results) {//richard.liu20141110 if (sc.Code != StatusCodes.Good) { OpcError = "向OPC数据存取服务器写数据时:WriteValues:" + nodesToWrite[ia].ToString() + "的返回值有错误!"+sc.ToString(); return false; } ia++; } return true; } catch (Exception ex) { OpcError = "向OPC数据存取服务器写数据时:" + ex.Message; _IfConnectOPCServer = false; return false; } } /// /// 获得设备类型索引 /// /// 设备索引 /// 设备类型索引 static int GetDeviceKindIdx(int devIdx) { System.Object lockThis = new System.Object(); lock (lockThis) { try { DataView dv= dbo.ExceSQL(string.Format("SELECT F_DeviceKindIndex FROM T_Base_Device WHERE F_DeviceIndex={0}", devIdx)).Tables[0].DefaultView; if (dv.Count > 0) { return System.Convert.ToInt32(dv[0]["F_DeviceKindIndex"]); } else return 0; } catch (Exception ex) { throw ex; } } } public static void DisConnectOPCServer() { try { int result; if (m_Server == null) return; result = m_Server.Disconnect(); // Disconnect succeeded. if (result == 0) { m_Server.RemoveSubscription(m_Subscription); m_Subscription = null; } _IfConnectOPCServer = false;//20140514张博在山东电力发现此问题 } catch (Exception ex) { OpcError = "关闭OPC数据存取服务器时:" + ex.Message; } } static void m_Server_CertificateEvent(CertificateValidator validator, CertificateValidationEventArgs e) { e.Accept = true; } } }