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 ushort S7NameSpace = 2;//20151120,默认值是32位系统的值2,64位系统的值是3 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.IsNotGood(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); } OpcError = string.Format("{0}所在的PLC已经离线!订阅标签状态:" + value.StatusCode.ToString(), clientHandle); if (ConnectCount[devinfo.S7Connection] > 3)//20150204 { _opcError = "订阅返回数据时:OPC Server没连接到PLC:" + devinfo.S7Connection.ToString() + ",人工确认后进行【PLC初始化】";//20150204 return; } ConnectCount[devinfo.S7Connection]++;//20150204 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; #region//20220526注释 //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(), S7NameSpace);//20151120 // // 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, 100, out serverHandle); // } // catch (ServiceResultException monitoredItemResult) // { // OpcError = "建立OPC订阅组时:" + monitoredItemResult.Message; // } // } //} #endregion #region//20220526在创建OPC订阅时候先创建条码扫描器的订阅,之后再创建其余设备类型的订阅 for (int i = 0; i < dv.Count; i++) { string dt = string.Format("SELECT (MAX(F_DBW2Address) + F_DBWGetLength) as maxdbw2 FROM T_Base_Device WHERE (F_ReadSubscription = {0}) AND (F_DeviceKindIndex = 7 ) GROUP BY F_DBWGetLength ORDER BY maxdbw2 DESC", dv[i]["F_ReadSubscription"]);//DateTime.Now.Minute.ToString() + "-" + DateTime.Now.Second.ToString() + "-" + DateTime.Now.Millisecond.ToString(); dvx = dbo.ExceSQL(dt).Tables[0].DefaultView;//20220526先创建条码扫描器 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(), S7NameSpace);//20151120 // 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, 100, out serverHandle); } catch (ServiceResultException monitoredItemResult) { OpcError = "建立OPC订阅组时:" + monitoredItemResult.Message; } } dvx = dbo.ExceSQL(string.Format("SELECT (MAX(F_DBW2Address) + F_DBWGetLength) as maxdbw2 FROM T_Base_Device WHERE (F_ReadSubscription = {0}) AND (F_DeviceKindIndex <>7 and F_DeviceKindIndex <>33 ) GROUP BY F_DBWGetLength ORDER BY maxdbw2 DESC", dv[i]["F_ReadSubscription"])).Tables[0].DefaultView;//20220526后创建非条码扫描器 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(), S7NameSpace);//20151120 // 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, 100, out serverHandle); } catch (ServiceResultException monitoredItemResult) { OpcError = "建立OPC订阅组时:" + monitoredItemResult.Message; } } } #endregion 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(), S7NameSpace);//20151120 // 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 bool RefreshMonitoringMode() {//20150103增加断网恢复 if (m_Subscription != null) { m_Subscription.RefreshMonitoringMode(); } return true; } /// /// 连接指定主机的OPC服务器 /// /// 主机名称 /// OPC服务器程序标识例如:"OPC.SimaticNET" /// public static bool ConnectOPCServer(string hostname, string ProgID) { try { //Uri discoveryUrl = new Uri("opc.tcp://" + hostname + ":4845"); Uri discoveryUrl = new Uri("opc.tcp://" + hostname); Discovery discovery = new Discovery(); EndpointDescriptionCollection endpoints = null; string rr = discovery.GetEndpoints(discoveryUrl, ref endpoints); for (int i = 0; i < endpoints.Count; i++) { if ((endpoints[i].Server.ApplicationName.Text.IndexOf( ProgID)>-1) && (endpoints[i].SecurityMode == MessageSecurityMode.None))//20151120 { 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); } #region 自动读取“S7:”的命名空间索引//20151120 DataValueCollection m_currentValues; NodeId nodeId=new NodeId(2255); NodeIdCollection nodesToRead = new NodeIdCollection(1); nodesToRead.Add(nodeId); m_Server.ReadValues(nodesToRead, out m_currentValues); if (m_currentValues.Count > 0) { string[] arr; arr =(string[]) m_currentValues[0].Value; S7NameSpace =(ushort) Array.IndexOf(arr, "S7:"); } #endregion 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] > 3)//20150204 { _opcError = "读数据时:OPC Server没连接到PLC:" + _PLCconnectionID.ToString() + ",人工确认后进行【PLC初始化】";//20150204 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,S7NameSpace);//20151120 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] > 3)//20150204 { _opcError = "写数据时:OPC Server没连接到PLC:" + _PLCconnectionID.ToString() + ",人工确认后进行【PLC初始化】";//20150204 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位操作系统需要指定确切的数据类型//20151120 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); } else if (item.ToString().IndexOf(",dt") >= 0)//DateTime:日期和时间类型:05/31/2016 08:21:10.123 PM { gt = typeof(DateTime); } 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, S7NameSpace); //20151120 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) { ConnectCount[_PLCconnectionID]++;//20150204 OpcError = "向OPC数据存取服务器写数据时:WriteValues:" + nodesToWrite[ia].ToString() + "的返回值有错误!"+sc.ToString(); return false; } ia++; } ConnectCount[_PLCconnectionID] = 0;//20150204 return true; } catch (Exception ex) { ConnectCount[_PLCconnectionID]++;//20150204 OpcError = "向OPC数据存取服务器写数据时:" + ex.Message; _IfConnectOPCServer = false; return false; } } #region 20220518 zb 异步写入数据 public static bool ASyncWriteAllItemValue(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] > 3)//20150204 { _opcError = "写数据时:OPC Server没连接到PLC:" + _PLCconnectionID.ToString() + ",人工确认后进行【PLC初始化】";//20150204 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位操作系统需要指定确切的数据类型//20151120 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); } else if (item.ToString().IndexOf(",dt") >= 0)//DateTime:日期和时间类型:05/31/2016 08:21:10.123 PM { gt = typeof(DateTime); } 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, S7NameSpace); //20151120 nodesToWrite.Add(nodeId); i++; } // 20220518 Call to ClientAPI. m_Server.AsyWriteValues( nodesToWrite, values, out results); // 异步调用不需要判断返回值 //int ia = 0;//richard.liu20141110 //foreach (StatusCode sc in results) //{//richard.liu20141110 // if (sc.Code != StatusCodes.Good) // { // ConnectCount[_PLCconnectionID]++;//20150204 // OpcError = "向OPC数据存取服务器写数据时:WriteValues:" + nodesToWrite[ia].ToString() + "的返回值有错误!" + sc.ToString(); // return false; // } // ia++; //} ConnectCount[_PLCconnectionID] = 0;//20150204 return true; } catch (Exception ex) { ConnectCount[_PLCconnectionID]++;//20150204 OpcError = "向OPC数据存取服务器写数据时:" + ex.Message; _IfConnectOPCServer = false; return false; } } #endregion /// /// 获得设备类型索引 /// /// 设备索引 /// 设备类型索引 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_Subscription.RemoveAllMonitoredItems();//20150103增加断网恢复 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; } } }