C#创建OPC Client来访问OPC server

匿名 (未验证) 提交于 2019-12-03 00:41:02

  最近一个项目,需要跟PLC通讯,所以测试使用了OPC server。现主要记录使用C#编写的Client例程,其它方面不作详细描述。

  第一步,OPC Server使用的是KEPServer 5版本,网上很多资料。安装完成后,它的配置页面如下图。配置中,我已配置了和Omron PLC连接的project,创建了访问PLC的area地址的几十个变量。具体配置根据不同PLC的信息对应配置就行了。

1、创建线程

          #region OPC通讯线程             try             {                 OPCClient opcClient = new OPCClient();                 Thread thrOpc = new Thread(opcClient.OPCClientOperate);                 thrOpc.IsBackground = true;                 thrOpc.Start();             }             catch { }             #endregion    

2、创建类

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using OPCAutomation; using Model; using System.Threading;  namespace BLL {     public class OPCClient     {         #region 全局变量         /// <summary>         /// OPC对应PLC的位置         /// </summary>         public static Dictionary<string, OPCItemParameter> dtOpcToPlc = new Dictionary<string, OPCItemParameter>();         /// <summary>         /// opc服务器信息         /// </summary>         public static OPCInformation opcInformation = new OPCInformation();         #endregion         /// <summary>         /// 初始化         /// </summary>         public OPCClient()         { }     } }    

上述中的dtOpcToPlc为后台配置,用于配置对应在OPC Server中想获取的变量的名称及对应的属性,因为项目实施时可能有变动,对于信息的获取只能用配置的形式了。

OPCInformation则是创建的与OPC通讯的信息实例了

两个实例如下

/// <summary>     /// opc参数信息     /// </summary>     public class OPCItemParameter     {         public OPCItemParameter()         {             this.ChangeTime = DateTime.Now;         }         /// <summary>         /// 初始化         /// </summary>         /// <param name="pName">opc项名称</param>         /// <param name="plcName">PLC命名</param>         /// <param name="handle">客户端句柄</param>         /// <param name="value">值</param>         public OPCItemParameter(string pName, string plcName, int handle, int value)         {             this.ParameterName = pName;             this.PLCName = plcName;             this.ItemHandle = handle;             this.Value = value;             this.ChangeTime = DateTime.Now;             this.IsWriteOk = false;         }         /// <summary>         /// 客户端参数句柄         /// </summary>         public int ItemHandle { get; set; }         /// <summary>         /// 对应PLC值的参数名称(OPC server命名)         /// </summary>         public string ParameterName { get; set; }         /// <summary>         /// PLC位置名称         /// </summary>         public string PLCName { get; set; }         /// <summary>         /// 参数值          /// </summary>         public int Value { get; set; }         /// <summary>         /// 品质         /// </summary>         public string Qualities { get; set; }         /// <summary>         /// 时间戳         /// </summary>         public string TimeStamps { get; set; }         /// <summary>         /// 值发生变化的时间,用于后期任务优先级         /// </summary>         public DateTime ChangeTime { get; set; }         /// <summary>         /// 是否写入成功         /// </summary>         public bool IsWriteOk { get; set; }     }
 #region OPC服务器类信息     /// <summary>     /// OPC服务器的参数信息     /// </summary>     public class OPCInformation     {         public OPCInformation()         {             this.Ip = string.Empty;             this.HostName = string.Empty;             this.ConnectState = false;             this.GroupsState = false;             this.ConnectContents = "Opc Failed";         }         /// <summary>         /// ip地址         /// </summary>         public string Ip { get; set; }         /// <summary>         /// 名称         /// </summary>         public string HostName { get; set; }         /// <summary>         /// opc服务器名称         /// </summary>         public string ServerName { get; set; }         /// <summary>         /// 服务器句柄         /// </summary>         public int itmHandleServer { get; set; }         /// <summary>         /// opc服务器对象         /// </summary>         public OPCServer KepServer { get; set; }         /// <summary>         /// opc组别集合对象         /// </summary>         public OPCGroups KepGroups { get; set; }         /// <summary>         /// opc组别对象         /// </summary>         public OPCGroup KepGroup { get; set; }         /// <summary>         /// opc项集合对象         /// </summary>         public OPCItems KepItems { get; set; }         /// <summary>         /// opc项对象         /// </summary>         public OPCItem KepItem { get; set; }          /// <summary>         /// 连接状态         /// </summary>         public bool ConnectState { get; set; }         /// <summary>         /// 连接内容         /// </summary>         public string ConnectContents { get; set; }         /// <summary>         /// 创建群组是否成功         /// </summary>         public bool GroupsState { get; set; }     }     #endregion

3、下面为创建连接通讯及循环判断是否掉线,这个主要是为了新创建连接及掉线是能迅速响应重连

 /// <summary>         /// 对opc获取的数据进行业务处理         /// </summary>         public void OPCClientOperate()         {             int lineoffCount = 0;//掉线判断计数             while (true)             {                 try                 {                     if (!opcInformation.ConnectState)                     {//连接不成功,尝试重新连接                         if (GetLocalServer())                         {//获取OPC服务器信息成功                             if (ConnectRemoteServer())                             {//连接OPC成功                                  opcInformation.ConnectState = true;                                 RecurBrowse(opcInformation.KepServer.CreateBrowser());                             }                         }                         else                         {                             Thread.Sleep(3000);                         }                     }                     else                     {                         if (!opcInformation.GroupsState)                         {//创建组集合失败,尝试重新创建                             opcInformation.GroupsState = CreateGroup();                         }                         else                         {                             //判断状态及时重连                           }                     }                 }                 catch (Exception ex)                 {                     opcInformation.ConnectState = false;                     opcInformation.ConnectContents = "OPC disconnected";                     opcInformation.GroupsState = false;                     try                     {                         opcInformation.KepServer.Disconnect();                     }                     catch { }                 }                 //Thread.Sleep(100);             }         }

4、当连接上时,OPC的dll控件有数据变化响应事件创建调用就行了

 /// <summary>         /// 每当项数据有变化时执行的事件         /// </summary>         /// <param name="TransactionID">处理ID</param>         /// <param name="NumItems">项个数</param>         /// <param name="ClientHandles">项客户端句柄</param>         /// <param name="ItemValues">TAG值</param>         /// <param name="Qualities">品质</param>         /// <param name="TimeStamps">时间戳</param>         private void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)         {             for (int i = 1; i <= NumItems; i++)             {                 try                 {                     int index = int.Parse(ClientHandles.GetValue(i).ToString());                     string key = dtOpcToPlc.FirstOrDefault(o => o.Value.ItemHandle == index).Key;                     if (!string.IsNullOrEmpty(key))                     {//需要判断类型,是int还是boolean                          int value = int.Parse(ItemValues.GetValue(i).ToString());                         if (value != dtOpcToPlc[key].Value)                         {                             dtOpcToPlc[key].Value = value;                             dtOpcToPlc[key].ChangeTime = DateTime.Now;                         }                         dtOpcToPlc[key].Qualities = Qualities.GetValue(i).ToString();                         dtOpcToPlc[key].TimeStamps = TimeStamps.GetValue(i).ToString();                      }                 }                 catch { }             }         }

5、向OPC Server的变量写入数据

        /// <summary>         /// 向OPC对应项写入值         /// </summary>         /// <param name="value">需要写入的值</param>         /// <param name="OPCItemParameter">item地址</param>         /// <returns></returns>         public static bool WriteOpc(int value, OPCItemParameter opcItem)         {             try             {                 string key = dtOpcToPlc.First(o => o.Value.ItemHandle == opcItem.ItemHandle).Key;                 dtOpcToPlc[key].IsWriteOk = false;                 OPCItem bItem = opcInformation.KepItems.Item(opcItem.ParameterName);                 opcInformation.itmHandleServer = bItem.ServerHandle;                 int[] temp = new int[2] { 0, bItem.ServerHandle };                 Array serverHandles = (Array)temp;                 object[] valueTemp = new object[2] { "", value.ToString() };                 Array values = (Array)valueTemp;                 Array Errors;                 int cancelID;                 opcInformation.KepGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID);                 //KepItem.Write(txtWriteTagValue.Text);//这句也可以写入,但并不触发写入事件                 GC.Collect();                 return true;             }             catch             {                 return false;             }         }

很简单,只需要调用对应的函数就可以了。

6、写入成功响应

  当写入成功后,对应的响应函数会响应

 private void KepGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)         {             try             {                 for (int i = 1; i <= NumItems; i++)                 {                     int error = int.Parse(Errors.GetValue(i).ToString());                     int handle = int.Parse(ClientHandles.GetValue(i).ToString());                     string key = dtOpcToPlc.First(o => o.Value.ItemHandle == handle).Key;                     if (error == 0)                     {                         dtOpcToPlc[key].IsWriteOk = true;                     }                 }             }             catch { }         }

其它的一些基本连接方法(ConnectRemoteServer、GetLocalServer、RecurBrowse、SetGroupProperty等),是引用了百度上其它网友的案例,就不一一描述了。

7、总结

  OPC的dll提供了很多接口,相对调用简单,只需要根据项目来作简单修改。对于掉线异常重连,则需要根据实际调试案例来处理就行了,这个需要花一些时间来测试。

  这案例只测试了Omron PLC的通讯连接,其它PLC尚未进行实际测试。

  

原文:https://www.cnblogs.com/GanSlide/p/9337608.html

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!