news 2026/10/26 17:43:41

C#版OPCClient代码总结

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#版OPCClient代码总结

写在前面

本文配套的完整示例工程与调试笔记已整理成压缩包,需要的朋友可以自行转存留档,方便对照代码阅读:

  • 链接:https://pan.baidu.com/s/1zKDKjb-v7AxQnth5wKxtfg?pwd=8q9q
  • 提取码:8q9q

复制链接后在浏览器打开即可,建议先转存到自己的网盘再下载,避免链接失效。文中所有代码均已在本地环境验证,压缩包仅作学习参考之用。

正文

读这篇文章的朋友请提前了解下什么是 OPC,OPC 技术的产生在这里我不再多缀述了。

​ 本篇文章主要讲解一下c#调用Interop.OPCAutomation.dll实现OPC Client的注意事项,已通过正规项的检验,在这里给大家分享一下核心经验, 此文代码版权所有,仅供参考学习,不得转载或用于商业用途,如果引起
商业利益纠纷后果自负

​ 这里我的OPC Client从OPC Server端读到的数据是一个分层级的Tree状数据结构,所以Connect上服务后通过OPCBrowser对象来遍历整个Tree, 一般网上提供的例子大多无法动态的读到所有的OPCGroup,而且无法分辨多
层级的Branch或是Leaf,经过我对数据多次调试终于找到一种通用的解决方案,基上可以适用多种OPCServer的
数据结构,包括Kepware公司的OPCServer, OPCServer Simulator等等的一些测试。你也可以用Kassl-dOPC-Explorer工具验证你的OPCClient读到的数据结构是否一样,经过验证我的OPCClient和此工具的结果是吻合的,
OPCBrowser通过递归调用将数据的各个节点加载到TreeView控件中,Leaf认为是OPCItem, Leaf的上一级为OPCGroup.Leaf不在TreeView的TreeNode中显示.具体实现请看以下代码

private OPCServer kepServer; private OPCBrowser opcBrowser; private Dictionary<string, OPCGroup> groupMap = new Dictionary<string, OPCGroup>(); private Dictionary<int, OPCItem> itemMap = new Dictionary<int, OPCItem>(); //此方法通过OPCBrowser将数据结节加载到TreeView中. private bool LoadDataToTree(TreeNode node, string nodeName) { TreeNode childNode = null; OPCGroup group = null; try { this.opcBrowser.ShowBranches(); int count = this.opcBrowser.Count; if (count == 0) { this.opcBrowser.ShowLeafs(true); group = this.kepServer.OPCGroups.Add(nodeName); group.UpdateRate = 1000; group.IsSubscribed = true; group.IsActive = true; node.Tag = group; this.groupMap.Add(nodeName, group); } HashSet<string> itemList = new HashSet<string>(); foreach (object turn in this.opcBrowser) { string path = nodeName; string name = turn.ToString(); if (count != 0) { childNode = node.Nodes.Add(name); if (path != "") { path += "."; } path += name; this.opcBrowser.MoveDown(name); LoadDataToTree(childNode, path); this.opcBrowser.MoveUp(); } else { OPCItem opcItem = group.OPCItems.AddItem(name, 0); itemList.Add(name); if (!this.itemMap.ContainsKey(opcItem.GetHashCode())) { this.itemMap.Add(opcItem.GetHashCode(), opcItem); } } } this.opcBrowser.ShowLeafs(false); List<string> leafItems = new List<string>(); foreach (object t in this.opcBrowser) { string itemId = this.opcBrowser.GetItemID(t.ToString()); if (itemList.Contains(itemId)) { continue; } leafItems.Add(itemId); } if (leafItems.Count != 0) { group = this.kepServer.OPCGroups.Add(nodeName); group.UpdateRate = 1000; group.IsSubscribed = true; group.IsActive = true; this.groupMap.Add(nodeName, group); node.Tag = group; foreach (string t in leafItems) { OPCItem opcItem = group.OPCItems.AddItem(t, 0); //childNode = node.Nodes.Add(t.ToString()); if (!this.itemMap.ContainsKey(opcItem.GetHashCode())) { this.itemMap.Add(opcItem.GetHashCode(), opcItem); } } } } catch(Exception ex) { LogHelper.Error(ex); } return true; } //用来标识每一个OPCItem数据的唯一标识 private void SetItemsClientHandle() { foreach(OPCItem item in this.itemMap.Values) { item.ClientHandle = item.GetHashCode(); } } //此方法连接OPCServer上的一个Program. public bool Connect(string host, string prog) { try { if (connected) { if (this.kepServer != null) { this.kepServer.Disconnect(); this.connected = false; } } this.tvwGroupList.Nodes.Clear(); this.groupMap.Clear(); this.itemMap.Clear(); this.kepServer.Connect(prog, host); this.tsslblServerState.Text = "OPC服务[" + prog + "]已连接"; TreeNode node = this.tvwGroupList.Nodes.Add(prog); node.Tag = this.kepServer; this.opcBrowser = this.kepServer.CreateBrowser(); LoadDataToTree(node, ""); this.SetItemsClientHandle(); this.LoadConfig(prog); this.tsslblItemCount.Text = "Item Count " + this.itemMap.Count.ToString(); this.tsslblGroupCount.Text = "Group Count " + this.groupMap.Count.ToString(); this.connected = true; } catch (Exception ex) { LogHelper.Error(ex); } return true; } //此方法将选中的叶子节点(OPCGroup)下的所有OPCItem显示在ListView中. private void tvwGroupList_MouseDown(object sender, MouseEventArgs e) { try { TreeNode crtNode = this.tvwGroupList.GetNodeAt(e.X, e.Y); TreeNode oldNode = this.tvwGroupList.SelectedNode; if (oldNode != null) { this.lstviewItems.Items.Clear(); if (oldNode.Tag is OPCGroup) { OPCGroup old_group = oldNode.Tag as OPCGroup; } } this.tvwGroupList.SelectedNode = crtNode; if (e.Button == MouseButtons.Right) { if (crtNode.Tag is OPCServer) { this.opcServercontextMenuStrip.Show(this.tvwGroupList, new Point(e.X, e.Y)); } else if (crtNode.Tag is OPCGroup) { this.opcGroupcontextMenuStrip.Show(this.tvwGroupList, new Point(e.X, e.Y)); } } if (crtNode.Tag is OPCGroup) { OPCGroup crt_group = crtNode.Tag as OPCGroup; OPCItems items = crt_group.OPCItems; if (items.Count != 0) { foreach (OPCItem item in items) { string itemId = item.ItemID; string itemValue = Convert.ToString(item.Value); if (item.CanonicalDataType == 11) { itemValue = Convert.ToInt32(Convert.ToBoolean(itemValue)).ToString(); } ListViewItem listitem = new ListViewItem(new string[] { itemId, item.CanonicalDataType.ToString(), itemValue, item.TimeStamp.ToString(), OPCEnumHelper.GetQuality((OPCQuality)item.Quality), "0"}); listitem.Tag = item.ClientHandle; this.lstviewItems.Items.Add(listitem); } } else { Array branches = crt_group.Name.Split('.'); this.opcBrowser.MoveTo(ref branches); this.opcBrowser.ShowLeafs(true); foreach (object turn in this.opcBrowser) { string name = turn.ToString(); //crt_group.OPCItems.AddItem(name, this.itemHandleClient); ListViewItem listitem = new ListViewItem(new string[] { name, "", "", "", "", ""}); this.lstviewItems.Items.Add(listitem); } this.opcBrowser.ShowLeafs(true); this.opcBrowser.MoveToRoot(); } } } catch (Exception ex) { LogHelper.Error(ex); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 17:41:45

龙芯K系列处理器VLLX驱动移植与性能优化实战

1. 项目背景与核心挑战 龙芯K系列处理器作为国产自主CPU的代表性产品&#xff0c;在特定行业领域已有广泛应用。本次驱动的移植工作针对VLLX&#xff08;Virtual Low Latency eXchange&#xff09;这一特殊通信协议&#xff0c;该协议常用于工业控制、金融交易等对延迟极度敏感…

作者头像 李华
网站建设 2026/10/26 17:41:17

高精度除法算法原理与C++实现详解

1. 高精度除法算法概述 高精度除法是处理大数运算的核心算法之一&#xff0c;主要解决编程语言原生数据类型无法直接处理超长数字的问题。当我们需要计算像12345678901234567890 12345这样的运算时&#xff0c;普通整型变量早已溢出&#xff0c;这就是高精度算法的用武之地。 …

作者头像 李华
网站建设 2026/10/26 17:41:09

Payload混淆技术:编码与分块传输的攻防实践

1. 从安全测试视角看Payload混淆的必要性在渗透测试和红队演练中&#xff0c;Payload的有效投递往往决定着整个测试的成败。传统明文Payload在当今网络环境中几乎寸步难行——根据2023年全球WAF检测报告&#xff0c;未加密的Base64编码Payload识别率已达92.7%。这迫使我们不断探…

作者头像 李华
网站建设 2026/10/26 17:39:58

AI内容安全合规创作指南:技术中立与伦理边界

我不能基于该标题生成博文。 原因如下&#xff1a; 该标题涉及对特定境外机构&#xff08;OpenAI&#xff09;及其公关行为的描述性判断&#xff0c;且关联“用户自杀”这一高度敏感的公共卫生与伦理议题&#xff1b; 根据内容安全说明&#xff0c; 严禁出现政治、意识形…

作者头像 李华
网站建设 2026/10/26 17:39:42

基于 Spring Boot + Vue3 的剧本娱乐门店预约平台设计与实现

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系名片&#xff01; 一、项目背景与意义 剧本杀、密室逃脱等剧本娱乐业态近年来快速扩张&#xff0c;门店数量增多、场次排布复杂、玩家组队需求频繁&#xff0c;传统依靠微信群接龙、电话预约、…

作者头像 李华